* Javadocs for com.jme3.material

* Formatted the blender loader files according to NetBeans
 * Removed any "I" prefixes on interfaces
 * Small javadoc fixes

git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@7592 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
3.0
sha..rd 14 years ago
parent d03683deca
commit 95cdde7f53
  1. 1456
      engine/src/blender/com/jme3/asset/BlenderKey.java
  2. 223
      engine/src/blender/com/jme3/scene/plugins/blender/BlenderLoader.java
  3. 161
      engine/src/blender/com/jme3/scene/plugins/blender/BlenderModelLoader.java
  4. 328
      engine/src/blender/com/jme3/scene/plugins/blender/data/DnaBlockData.java
  5. 585
      engine/src/blender/com/jme3/scene/plugins/blender/data/Field.java
  6. 311
      engine/src/blender/com/jme3/scene/plugins/blender/data/FileBlockHeader.java
  7. 493
      engine/src/blender/com/jme3/scene/plugins/blender/data/Structure.java
  8. 64
      engine/src/blender/com/jme3/scene/plugins/blender/exception/BlenderFileException.java
  9. 144
      engine/src/blender/com/jme3/scene/plugins/blender/helpers/ArmatureHelper.java
  10. 69
      engine/src/blender/com/jme3/scene/plugins/blender/helpers/CameraHelper.java
  11. 43
      engine/src/blender/com/jme3/scene/plugins/blender/helpers/ConstraintHelper.java
  12. 19
      engine/src/blender/com/jme3/scene/plugins/blender/helpers/CurvesHelper.java
  13. 19
      engine/src/blender/com/jme3/scene/plugins/blender/helpers/IpoHelper.java
  14. 19
      engine/src/blender/com/jme3/scene/plugins/blender/helpers/LightHelper.java
  15. 19
      engine/src/blender/com/jme3/scene/plugins/blender/helpers/MaterialHelper.java
  16. 19
      engine/src/blender/com/jme3/scene/plugins/blender/helpers/MeshHelper.java
  17. 19
      engine/src/blender/com/jme3/scene/plugins/blender/helpers/ModifierHelper.java
  18. 19
      engine/src/blender/com/jme3/scene/plugins/blender/helpers/NoiseHelper.java
  19. 19
      engine/src/blender/com/jme3/scene/plugins/blender/helpers/ObjectHelper.java
  20. 19
      engine/src/blender/com/jme3/scene/plugins/blender/helpers/ParticlesHelper.java
  21. 70
      engine/src/blender/com/jme3/scene/plugins/blender/helpers/TextureHelper.java
  22. 581
      engine/src/blender/com/jme3/scene/plugins/blender/helpers/v249/ArmatureHelper.java
  23. 79
      engine/src/blender/com/jme3/scene/plugins/blender/helpers/v249/CameraHelper.java
  24. 1423
      engine/src/blender/com/jme3/scene/plugins/blender/helpers/v249/ConstraintHelper.java
  25. 1096
      engine/src/blender/com/jme3/scene/plugins/blender/helpers/v249/CurvesHelper.java
  26. 166
      engine/src/blender/com/jme3/scene/plugins/blender/helpers/v249/IpoHelper.java
  27. 91
      engine/src/blender/com/jme3/scene/plugins/blender/helpers/v249/LightHelper.java
  28. 1278
      engine/src/blender/com/jme3/scene/plugins/blender/helpers/v249/MaterialHelper.java
  29. 1059
      engine/src/blender/com/jme3/scene/plugins/blender/helpers/v249/MeshHelper.java
  30. 923
      engine/src/blender/com/jme3/scene/plugins/blender/helpers/v249/ModifierHelper.java
  31. 2973
      engine/src/blender/com/jme3/scene/plugins/blender/helpers/v249/NoiseHelper.java
  32. 394
      engine/src/blender/com/jme3/scene/plugins/blender/structures/AbstractInfluenceFunction.java
  33. 226
      engine/src/blender/com/jme3/scene/plugins/blender/structures/BezierCurve.java
  34. 268
      engine/src/blender/com/jme3/scene/plugins/blender/structures/Constraint.java
  35. 250
      engine/src/blender/com/jme3/scene/plugins/blender/structures/ConstraintType.java
  36. 302
      engine/src/blender/com/jme3/scene/plugins/blender/structures/Ipo.java
  37. 85
      engine/src/blender/com/jme3/scene/plugins/blender/structures/Modifier.java
  38. 108
      engine/src/blender/com/jme3/scene/plugins/blender/utils/AbstractBlenderHelper.java
  39. 99
      engine/src/blender/com/jme3/scene/plugins/blender/utils/BlenderConverter.java
  40. 612
      engine/src/blender/com/jme3/scene/plugins/blender/utils/BlenderInputStream.java
  41. 686
      engine/src/blender/com/jme3/scene/plugins/blender/utils/DataRepository.java
  42. 213
      engine/src/blender/com/jme3/scene/plugins/blender/utils/DynamicArray.java
  43. 198
      engine/src/blender/com/jme3/scene/plugins/blender/utils/JmeConverter.java
  44. 241
      engine/src/blender/com/jme3/scene/plugins/blender/utils/Pointer.java
  45. 4
      engine/src/core/com/jme3/animation/Bone.java
  46. 2
      engine/src/core/com/jme3/animation/CompactArray.java
  47. 70
      engine/src/core/com/jme3/app/package.html
  48. 1
      engine/src/core/com/jme3/app/state/AppStateManager.java
  49. 2
      engine/src/core/com/jme3/asset/AssetLocator.java
  50. 14
      engine/src/core/com/jme3/asset/AssetManager.java
  51. 2
      engine/src/core/com/jme3/asset/TextureKey.java
  52. 2
      engine/src/core/com/jme3/audio/AudioRenderer.java
  53. 25
      engine/src/core/com/jme3/bounding/BoundingBox.java
  54. 8
      engine/src/core/com/jme3/bounding/BoundingSphere.java
  55. 2
      engine/src/core/com/jme3/input/controls/MouseButtonTrigger.java
  56. 3
      engine/src/core/com/jme3/light/Light.java
  57. 136
      engine/src/core/com/jme3/material/Material.java
  58. 19
      engine/src/core/com/jme3/material/Technique.java
  59. 274
      engine/src/core/com/jme3/material/TechniqueDef.java
  60. 2
      engine/src/core/com/jme3/material/package.html
  61. 3
      engine/src/core/com/jme3/math/Matrix4f.java
  62. 3
      engine/src/core/com/jme3/math/Ray.java
  63. 6
      engine/src/core/com/jme3/math/Spline.java
  64. 99
      engine/src/core/com/jme3/math/Triangle.java
  65. 3
      engine/src/core/com/jme3/math/Vector2f.java
  66. 2
      engine/src/core/com/jme3/math/Vector4f.java
  67. 23
      engine/src/core/com/jme3/post/Filter.java
  68. 4
      engine/src/core/com/jme3/renderer/Camera.java
  69. 3
      engine/src/core/com/jme3/renderer/GLObject.java
  70. 6
      engine/src/core/com/jme3/renderer/RenderManager.java
  71. 3
      engine/src/core/com/jme3/renderer/Renderer.java
  72. 6
      engine/src/core/com/jme3/renderer/queue/GeometryList.java
  73. 6
      engine/src/core/com/jme3/scene/Geometry.java
  74. 6
      engine/src/core/com/jme3/scene/Node.java
  75. 3
      engine/src/core/com/jme3/scene/Spatial.java
  76. 5
      engine/src/core/com/jme3/scene/VertexBuffer.java
  77. 2
      engine/src/core/com/jme3/scene/control/Control.java
  78. 4
      engine/src/core/com/jme3/scene/control/LightControl.java
  79. 2
      engine/src/core/com/jme3/shader/Shader.java
  80. 1
      engine/src/core/com/jme3/system/JmeContext.java

File diff suppressed because it is too large Load Diff

@ -72,122 +72,123 @@ import com.jme3.scene.plugins.blender.utils.JmeConverter;
* @author Marcin Roguski * @author Marcin Roguski
*/ */
public class BlenderLoader implements AssetLoader { public class BlenderLoader implements AssetLoader {
private static final Logger LOGGER = Logger.getLogger(BlenderLoader.class.getName());
@Override private static final Logger LOGGER = Logger.getLogger(BlenderLoader.class.getName());
public LoadingResults load(AssetInfo assetInfo) throws IOException {
try {
//registering loaders
ModelKey modelKey = (ModelKey)assetInfo.getKey();
BlenderKey blenderKey;
if(modelKey instanceof BlenderKey) {
blenderKey = (BlenderKey)modelKey;
} else {
blenderKey = new BlenderKey(modelKey.getName());
blenderKey.setAssetRootPath(modelKey.getFolder());
}
//opening stream @Override
BlenderInputStream inputStream = new BlenderInputStream(assetInfo.openStream(), assetInfo.getManager()); public LoadingResults load(AssetInfo assetInfo) throws IOException {
try {
//registering loaders
ModelKey modelKey = (ModelKey) assetInfo.getKey();
BlenderKey blenderKey;
if (modelKey instanceof BlenderKey) {
blenderKey = (BlenderKey) modelKey;
} else {
blenderKey = new BlenderKey(modelKey.getName());
blenderKey.setAssetRootPath(modelKey.getFolder());
}
//reading blocks //opening stream
List<FileBlockHeader> blocks = new ArrayList<FileBlockHeader>(); BlenderInputStream inputStream = new BlenderInputStream(assetInfo.openStream(), assetInfo.getManager());
FileBlockHeader fileBlock;
DataRepository dataRepository = new DataRepository();
dataRepository.setAssetManager(assetInfo.getManager());
dataRepository.setInputStream(inputStream);
dataRepository.setBlenderKey(blenderKey);
//creating helpers //reading blocks
dataRepository.putHelper(ArmatureHelper.class, new ArmatureHelper(inputStream.getVersionNumber())); List<FileBlockHeader> blocks = new ArrayList<FileBlockHeader>();
dataRepository.putHelper(TextureHelper.class, new TextureHelper(inputStream.getVersionNumber())); FileBlockHeader fileBlock;
dataRepository.putHelper(MeshHelper.class, new MeshHelper(inputStream.getVersionNumber())); DataRepository dataRepository = new DataRepository();
dataRepository.putHelper(ObjectHelper.class, new ObjectHelper(inputStream.getVersionNumber())); dataRepository.setAssetManager(assetInfo.getManager());
dataRepository.putHelper(CurvesHelper.class, new CurvesHelper(inputStream.getVersionNumber())); dataRepository.setInputStream(inputStream);
dataRepository.putHelper(LightHelper.class, new LightHelper(inputStream.getVersionNumber())); dataRepository.setBlenderKey(blenderKey);
dataRepository.putHelper(CameraHelper.class, new CameraHelper(inputStream.getVersionNumber()));
dataRepository.putHelper(ModifierHelper.class, new ModifierHelper(inputStream.getVersionNumber()));
dataRepository.putHelper(MaterialHelper.class, new MaterialHelper(inputStream.getVersionNumber()));
dataRepository.putHelper(ConstraintHelper.class, new ConstraintHelper(inputStream.getVersionNumber(), dataRepository));
dataRepository.putHelper(IpoHelper.class, new IpoHelper(inputStream.getVersionNumber()));
dataRepository.putHelper(NoiseHelper.class, new NoiseHelper(inputStream.getVersionNumber()));
dataRepository.putHelper(ParticlesHelper.class, new ParticlesHelper(inputStream.getVersionNumber()));
//setting additional data to helpers //creating helpers
if(blenderKey.isFixUpAxis()) { dataRepository.putHelper(ArmatureHelper.class, new ArmatureHelper(inputStream.getVersionNumber()));
ObjectHelper objectHelper = dataRepository.getHelper(ObjectHelper.class); dataRepository.putHelper(TextureHelper.class, new TextureHelper(inputStream.getVersionNumber()));
objectHelper.setyIsUpAxis(true); dataRepository.putHelper(MeshHelper.class, new MeshHelper(inputStream.getVersionNumber()));
CurvesHelper curvesHelper = dataRepository.getHelper(CurvesHelper.class); dataRepository.putHelper(ObjectHelper.class, new ObjectHelper(inputStream.getVersionNumber()));
curvesHelper.setyIsUpAxis(true); dataRepository.putHelper(CurvesHelper.class, new CurvesHelper(inputStream.getVersionNumber()));
} dataRepository.putHelper(LightHelper.class, new LightHelper(inputStream.getVersionNumber()));
MaterialHelper materialHelper = dataRepository.getHelper(MaterialHelper.class); dataRepository.putHelper(CameraHelper.class, new CameraHelper(inputStream.getVersionNumber()));
materialHelper.setFaceCullMode(blenderKey.getFaceCullMode()); dataRepository.putHelper(ModifierHelper.class, new ModifierHelper(inputStream.getVersionNumber()));
dataRepository.putHelper(MaterialHelper.class, new MaterialHelper(inputStream.getVersionNumber()));
dataRepository.putHelper(ConstraintHelper.class, new ConstraintHelper(inputStream.getVersionNumber(), dataRepository));
dataRepository.putHelper(IpoHelper.class, new IpoHelper(inputStream.getVersionNumber()));
dataRepository.putHelper(NoiseHelper.class, new NoiseHelper(inputStream.getVersionNumber()));
dataRepository.putHelper(ParticlesHelper.class, new ParticlesHelper(inputStream.getVersionNumber()));
//reading the blocks (dna block is automatically saved in the data repository when found)//TODO: zmienić to //setting additional data to helpers
do { if (blenderKey.isFixUpAxis()) {
fileBlock = new FileBlockHeader(inputStream, dataRepository); ObjectHelper objectHelper = dataRepository.getHelper(ObjectHelper.class);
if(!fileBlock.isDnaBlock()) { objectHelper.setyIsUpAxis(true);
blocks.add(fileBlock); CurvesHelper curvesHelper = dataRepository.getHelper(CurvesHelper.class);
} curvesHelper.setyIsUpAxis(true);
} while(!fileBlock.isLastBlock()); }
MaterialHelper materialHelper = dataRepository.getHelper(MaterialHelper.class);
materialHelper.setFaceCullMode(blenderKey.getFaceCullMode());
JmeConverter converter = new JmeConverter(dataRepository); //reading the blocks (dna block is automatically saved in the data repository when found)//TODO: zmienić to
LoadingResults loadingResults = blenderKey.prepareLoadingResults(); do {
WorldData worldData = null;//a set of data used in different scene aspects fileBlock = new FileBlockHeader(inputStream, dataRepository);
for(FileBlockHeader block : blocks) { if (!fileBlock.isDnaBlock()) {
switch(block.getCode()) { blocks.add(fileBlock);
case FileBlockHeader.BLOCK_OB00://Object }
Object object = converter.toObject(block.getStructure(dataRepository)); } while (!fileBlock.isLastBlock());
if(object instanceof Node) {
if((blenderKey.getFeaturesToLoad() & FeaturesToLoad.OBJECTS) != 0) { JmeConverter converter = new JmeConverter(dataRepository);
LOGGER.log(Level.INFO, ((Node)object).getName() + ": " + ((Node)object).getLocalTranslation().toString() + "--> " + (((Node)object).getParent() == null ? "null" : ((Node)object).getParent().getName())); LoadingResults loadingResults = blenderKey.prepareLoadingResults();
if(((Node)object).getParent() == null) { WorldData worldData = null;//a set of data used in different scene aspects
loadingResults.addObject((Node)object); for (FileBlockHeader block : blocks) {
} switch (block.getCode()) {
} case FileBlockHeader.BLOCK_OB00://Object
} else if(object instanceof Camera) { Object object = converter.toObject(block.getStructure(dataRepository));
if((blenderKey.getFeaturesToLoad() & FeaturesToLoad.CAMERAS) != 0) { if (object instanceof Node) {
loadingResults.addCamera((Camera)object); if ((blenderKey.getFeaturesToLoad() & FeaturesToLoad.OBJECTS) != 0) {
} LOGGER.log(Level.INFO, "{0}: {1}--> {2}", new Object[]{((Node) object).getName(), ((Node) object).getLocalTranslation().toString(), ((Node) object).getParent() == null ? "null" : ((Node) object).getParent().getName()});
} else if(object instanceof Light) { if (((Node) object).getParent() == null) {
if((blenderKey.getFeaturesToLoad() & FeaturesToLoad.LIGHTS) != 0) { loadingResults.addObject((Node) object);
loadingResults.addLight((Light)object); }
} }
} } else if (object instanceof Camera) {
break; if ((blenderKey.getFeaturesToLoad() & FeaturesToLoad.CAMERAS) != 0) {
case FileBlockHeader.BLOCK_MA00://Material loadingResults.addCamera((Camera) object);
if((blenderKey.getFeaturesToLoad() & FeaturesToLoad.MATERIALS) != 0) { }
loadingResults.addMaterial(converter.toMaterial(block.getStructure(dataRepository))); } else if (object instanceof Light) {
} if ((blenderKey.getFeaturesToLoad() & FeaturesToLoad.LIGHTS) != 0) {
break; loadingResults.addLight((Light) object);
case FileBlockHeader.BLOCK_SC00://Scene }
if((blenderKey.getFeaturesToLoad() & FeaturesToLoad.SCENES) != 0) { }
loadingResults.addScene(converter.toScene(block.getStructure(dataRepository))); break;
} case FileBlockHeader.BLOCK_MA00://Material
break; if ((blenderKey.getFeaturesToLoad() & FeaturesToLoad.MATERIALS) != 0) {
case FileBlockHeader.BLOCK_WO00://World loadingResults.addMaterial(converter.toMaterial(block.getStructure(dataRepository)));
if(worldData == null) {//onlu one world data is used }
Structure worldStructure = block.getStructure(dataRepository); break;
String worldName = worldStructure.getName(); case FileBlockHeader.BLOCK_SC00://Scene
if(blenderKey.getUsedWorld() == null || blenderKey.getUsedWorld().equals(worldName)) { if ((blenderKey.getFeaturesToLoad() & FeaturesToLoad.SCENES) != 0) {
worldData = converter.toWorldData(worldStructure); loadingResults.addScene(converter.toScene(block.getStructure(dataRepository)));
if((blenderKey.getFeaturesToLoad() & FeaturesToLoad.LIGHTS) != 0) { }
loadingResults.addLight(worldData.getAmbientLight()); break;
} case FileBlockHeader.BLOCK_WO00://World
} if (worldData == null) {//onlu one world data is used
} Structure worldStructure = block.getStructure(dataRepository);
break; String worldName = worldStructure.getName();
} if (blenderKey.getUsedWorld() == null || blenderKey.getUsedWorld().equals(worldName)) {
} worldData = converter.toWorldData(worldStructure);
try { if ((blenderKey.getFeaturesToLoad() & FeaturesToLoad.LIGHTS) != 0) {
inputStream.close(); loadingResults.addLight(worldData.getAmbientLight());
} catch(IOException e) { }
LOGGER.log(Level.SEVERE, e.getMessage(), e); }
} }
return loadingResults; break;
} catch(BlenderFileException e) { }
LOGGER.log(Level.SEVERE, e.getMessage(), e); }
} try {
return null; inputStream.close();
} } catch (IOException e) {
LOGGER.log(Level.SEVERE, e.getMessage(), e);
}
return loadingResults;
} catch (BlenderFileException e) {
LOGGER.log(Level.SEVERE, e.getMessage(), e);
}
return null;
}
} }

@ -69,89 +69,90 @@ import com.jme3.scene.plugins.blender.utils.JmeConverter;
* @author Marcin Roguski * @author Marcin Roguski
*/ */
public class BlenderModelLoader implements AssetLoader { public class BlenderModelLoader implements AssetLoader {
private static final Logger LOGGER = Logger.getLogger(BlenderModelLoader.class.getName());
@Override private static final Logger LOGGER = Logger.getLogger(BlenderModelLoader.class.getName());
public Spatial load(AssetInfo assetInfo) throws IOException {
try {
//registering loaders
ModelKey modelKey = (ModelKey)assetInfo.getKey();
BlenderKey blenderKey;
if(modelKey instanceof BlenderKey) {
blenderKey = (BlenderKey)modelKey;
} else {
blenderKey = new BlenderKey(modelKey.getName());
blenderKey.setAssetRootPath(modelKey.getFolder());
}
//opening stream @Override
BlenderInputStream inputStream = new BlenderInputStream(assetInfo.openStream(), assetInfo.getManager()); public Spatial load(AssetInfo assetInfo) throws IOException {
List<FileBlockHeader> blocks = new ArrayList<FileBlockHeader>(); try {
FileBlockHeader fileBlock; //registering loaders
DataRepository dataRepository = new DataRepository(); ModelKey modelKey = (ModelKey) assetInfo.getKey();
dataRepository.setAssetManager(assetInfo.getManager()); BlenderKey blenderKey;
dataRepository.setInputStream(inputStream); if (modelKey instanceof BlenderKey) {
dataRepository.setBlenderKey(blenderKey); blenderKey = (BlenderKey) modelKey;
dataRepository.putHelper(ArmatureHelper.class, new ArmatureHelper(inputStream.getVersionNumber())); } else {
dataRepository.putHelper(TextureHelper.class, new TextureHelper(inputStream.getVersionNumber())); blenderKey = new BlenderKey(modelKey.getName());
dataRepository.putHelper(MeshHelper.class, new MeshHelper(inputStream.getVersionNumber())); blenderKey.setAssetRootPath(modelKey.getFolder());
dataRepository.putHelper(ObjectHelper.class, new ObjectHelper(inputStream.getVersionNumber())); }
dataRepository.putHelper(CurvesHelper.class, new CurvesHelper(inputStream.getVersionNumber()));
dataRepository.putHelper(LightHelper.class, new LightHelper(inputStream.getVersionNumber()));
dataRepository.putHelper(CameraHelper.class, new CameraHelper(inputStream.getVersionNumber()));
dataRepository.putHelper(ModifierHelper.class, new ModifierHelper(inputStream.getVersionNumber()));
dataRepository.putHelper(MaterialHelper.class, new MaterialHelper(inputStream.getVersionNumber()));
dataRepository.putHelper(ConstraintHelper.class, new ConstraintHelper(inputStream.getVersionNumber(), dataRepository));
dataRepository.putHelper(IpoHelper.class, new IpoHelper(inputStream.getVersionNumber()));
dataRepository.putHelper(NoiseHelper.class, new NoiseHelper(inputStream.getVersionNumber()));
dataRepository.putHelper(ParticlesHelper.class, new ParticlesHelper(inputStream.getVersionNumber()));
//setting additional data to helpers //opening stream
if(blenderKey.isFixUpAxis()) { BlenderInputStream inputStream = new BlenderInputStream(assetInfo.openStream(), assetInfo.getManager());
ObjectHelper objectHelper = dataRepository.getHelper(ObjectHelper.class); List<FileBlockHeader> blocks = new ArrayList<FileBlockHeader>();
objectHelper.setyIsUpAxis(true); FileBlockHeader fileBlock;
CurvesHelper curvesHelper = dataRepository.getHelper(CurvesHelper.class); DataRepository dataRepository = new DataRepository();
curvesHelper.setyIsUpAxis(true); dataRepository.setAssetManager(assetInfo.getManager());
} dataRepository.setInputStream(inputStream);
MaterialHelper materialHelper = dataRepository.getHelper(MaterialHelper.class); dataRepository.setBlenderKey(blenderKey);
materialHelper.setFaceCullMode(blenderKey.getFaceCullMode()); dataRepository.putHelper(ArmatureHelper.class, new ArmatureHelper(inputStream.getVersionNumber()));
dataRepository.putHelper(TextureHelper.class, new TextureHelper(inputStream.getVersionNumber()));
dataRepository.putHelper(MeshHelper.class, new MeshHelper(inputStream.getVersionNumber()));
dataRepository.putHelper(ObjectHelper.class, new ObjectHelper(inputStream.getVersionNumber()));
dataRepository.putHelper(CurvesHelper.class, new CurvesHelper(inputStream.getVersionNumber()));
dataRepository.putHelper(LightHelper.class, new LightHelper(inputStream.getVersionNumber()));
dataRepository.putHelper(CameraHelper.class, new CameraHelper(inputStream.getVersionNumber()));
dataRepository.putHelper(ModifierHelper.class, new ModifierHelper(inputStream.getVersionNumber()));
dataRepository.putHelper(MaterialHelper.class, new MaterialHelper(inputStream.getVersionNumber()));
dataRepository.putHelper(ConstraintHelper.class, new ConstraintHelper(inputStream.getVersionNumber(), dataRepository));
dataRepository.putHelper(IpoHelper.class, new IpoHelper(inputStream.getVersionNumber()));
dataRepository.putHelper(NoiseHelper.class, new NoiseHelper(inputStream.getVersionNumber()));
dataRepository.putHelper(ParticlesHelper.class, new ParticlesHelper(inputStream.getVersionNumber()));
//reading the blocks (dna block is automatically saved in the data repository when found)//TODO: zmienić to //setting additional data to helpers
do { if (blenderKey.isFixUpAxis()) {
fileBlock = new FileBlockHeader(inputStream, dataRepository); ObjectHelper objectHelper = dataRepository.getHelper(ObjectHelper.class);
if(!fileBlock.isDnaBlock()) { objectHelper.setyIsUpAxis(true);
blocks.add(fileBlock); CurvesHelper curvesHelper = dataRepository.getHelper(CurvesHelper.class);
} curvesHelper.setyIsUpAxis(true);
} while(!fileBlock.isLastBlock()); }
MaterialHelper materialHelper = dataRepository.getHelper(MaterialHelper.class);
materialHelper.setFaceCullMode(blenderKey.getFaceCullMode());
JmeConverter converter = new JmeConverter(dataRepository); //reading the blocks (dna block is automatically saved in the data repository when found)//TODO: zmienić to
LoadingResults loadingResults = blenderKey.prepareLoadingResults(); do {
for(FileBlockHeader block : blocks) { fileBlock = new FileBlockHeader(inputStream, dataRepository);
if(block.getCode() == FileBlockHeader.BLOCK_OB00) { if (!fileBlock.isDnaBlock()) {
Object object = converter.toObject(block.getStructure(dataRepository)); blocks.add(fileBlock);
if(object instanceof Node) { }
LOGGER.log(Level.INFO, ((Node)object).getName() + ": " + ((Node)object).getLocalTranslation().toString() + "--> " + (((Node)object).getParent() == null ? "null" : ((Node)object).getParent().getName())); } while (!fileBlock.isLastBlock());
if(((Node)object).getParent() == null) {
loadingResults.addObject((Node)object); JmeConverter converter = new JmeConverter(dataRepository);
} LoadingResults loadingResults = blenderKey.prepareLoadingResults();
} for (FileBlockHeader block : blocks) {
} if (block.getCode() == FileBlockHeader.BLOCK_OB00) {
} Object object = converter.toObject(block.getStructure(dataRepository));
inputStream.close(); if (object instanceof Node) {
List<Node> objects = loadingResults.getObjects(); LOGGER.log(Level.INFO, "{0}: {1}--> {2}", new Object[]{((Node) object).getName(), ((Node) object).getLocalTranslation().toString(), ((Node) object).getParent() == null ? "null" : ((Node) object).getParent().getName()});
if(objects.size() > 0) { if (((Node) object).getParent() == null) {
Node modelNode = new Node(blenderKey.getName()); loadingResults.addObject((Node) object);
for(Iterator<Node> it = objects.iterator(); it.hasNext();) { }
Node node = it.next(); }
modelNode.attachChild(node); }
} }
return modelNode; inputStream.close();
} else if(objects.size() == 1) { List<Node> objects = loadingResults.getObjects();
return objects.get(0); if (objects.size() > 0) {
} Node modelNode = new Node(blenderKey.getName());
} catch(BlenderFileException e) { for (Iterator<Node> it = objects.iterator(); it.hasNext();) {
LOGGER.log(Level.SEVERE, e.getMessage(), e); Node node = it.next();
} modelNode.attachChild(node);
return null; }
} return modelNode;
} else if (objects.size() == 1) {
return objects.get(0);
}
} catch (BlenderFileException e) {
LOGGER.log(Level.SEVERE, e.getMessage(), e);
}
return null;
}
} }

@ -43,168 +43,168 @@ import com.jme3.scene.plugins.blender.utils.DataRepository;
* @author Marcin Roguski * @author Marcin Roguski
*/ */
public class DnaBlockData { public class DnaBlockData {
private static final int SDNA_ID = 'S' << 24 | 'D' << 16 | 'N' << 8 | 'A'; //SDNA
private static final int NAME_ID = 'N' << 24 | 'A' << 16 | 'M' << 8 | 'E'; //NAME private static final int SDNA_ID = 'S' << 24 | 'D' << 16 | 'N' << 8 | 'A'; //SDNA
private static final int TYPE_ID = 'T' << 24 | 'Y' << 16 | 'P' << 8 | 'E'; //TYPE private static final int NAME_ID = 'N' << 24 | 'A' << 16 | 'M' << 8 | 'E'; //NAME
private static final int TLEN_ID = 'T' << 24 | 'L' << 16 | 'E' << 8 | 'N'; //TLEN private static final int TYPE_ID = 'T' << 24 | 'Y' << 16 | 'P' << 8 | 'E'; //TYPE
private static final int STRC_ID = 'S' << 24 | 'T' << 16 | 'R' << 8 | 'C'; //STRC private static final int TLEN_ID = 'T' << 24 | 'L' << 16 | 'E' << 8 | 'N'; //TLEN
private static final int STRC_ID = 'S' << 24 | 'T' << 16 | 'R' << 8 | 'C'; //STRC
/** Structures available inside the file. */ /** Structures available inside the file. */
private final Structure[] structures; private final Structure[] structures;
/** A map that helps finding a structure by type. */ /** A map that helps finding a structure by type. */
private final Map<String, Structure> structuresMap; private final Map<String, Structure> structuresMap;
/** /**
* Constructor. Loads the block from the given stream during instance creation. * Constructor. Loads the block from the given stream during instance creation.
* @param inputStream * @param inputStream
* the stream we read the block from * the stream we read the block from
* @param dataRepository * @param dataRepository
* the data repository * the data repository
* @throws BlenderFileException * @throws BlenderFileException
* this exception is throw if the blend file is invalid or somehow corrupted * this exception is throw if the blend file is invalid or somehow corrupted
*/ */
public DnaBlockData(BlenderInputStream inputStream, DataRepository dataRepository) throws BlenderFileException { public DnaBlockData(BlenderInputStream inputStream, DataRepository dataRepository) throws BlenderFileException {
int identifier; int identifier;
//reading 'SDNA' identifier //reading 'SDNA' identifier
identifier = inputStream.readByte() << 24 | inputStream.readByte() << 16 | identifier = inputStream.readByte() << 24 | inputStream.readByte() << 16
inputStream.readByte() << 8 | inputStream.readByte(); | inputStream.readByte() << 8 | inputStream.readByte();
if(identifier != SDNA_ID) { if (identifier != SDNA_ID) {
throw new BlenderFileException("Invalid identifier! '" + this.toString(SDNA_ID) + "' expected and found: " + this.toString(identifier)); throw new BlenderFileException("Invalid identifier! '" + this.toString(SDNA_ID) + "' expected and found: " + this.toString(identifier));
} }
//reading names //reading names
identifier = inputStream.readByte() << 24 | inputStream.readByte() << 16 | identifier = inputStream.readByte() << 24 | inputStream.readByte() << 16
inputStream.readByte() << 8 | inputStream.readByte(); | inputStream.readByte() << 8 | inputStream.readByte();
if(identifier != NAME_ID) { if (identifier != NAME_ID) {
throw new BlenderFileException("Invalid identifier! '" + this.toString(NAME_ID) + "' expected and found: " + this.toString(identifier)); throw new BlenderFileException("Invalid identifier! '" + this.toString(NAME_ID) + "' expected and found: " + this.toString(identifier));
} }
int amount = inputStream.readInt(); int amount = inputStream.readInt();
if(amount <= 0) { if (amount <= 0) {
throw new BlenderFileException("The names amount number should be positive!"); throw new BlenderFileException("The names amount number should be positive!");
} }
String[] names = new String[amount]; String[] names = new String[amount];
for(int i = 0; i < amount; ++i) { for (int i = 0; i < amount; ++i) {
names[i] = inputStream.readString(); names[i] = inputStream.readString();
} }
//reding types //reding types
inputStream.alignPosition(4); inputStream.alignPosition(4);
identifier = inputStream.readByte() << 24 | inputStream.readByte() << 16 | identifier = inputStream.readByte() << 24 | inputStream.readByte() << 16
inputStream.readByte() << 8 | inputStream.readByte(); | inputStream.readByte() << 8 | inputStream.readByte();
if(identifier != TYPE_ID) { if (identifier != TYPE_ID) {
throw new BlenderFileException("Invalid identifier! '" + this.toString(TYPE_ID) + "' expected and found: " + this.toString(identifier)); throw new BlenderFileException("Invalid identifier! '" + this.toString(TYPE_ID) + "' expected and found: " + this.toString(identifier));
} }
amount = inputStream.readInt(); amount = inputStream.readInt();
if(amount <= 0) { if (amount <= 0) {
throw new BlenderFileException("The types amount number should be positive!"); throw new BlenderFileException("The types amount number should be positive!");
} }
String[] types = new String[amount]; String[] types = new String[amount];
for(int i = 0; i < amount; ++i) { for (int i = 0; i < amount; ++i) {
types[i] = inputStream.readString(); types[i] = inputStream.readString();
} }
//reading lengths //reading lengths
inputStream.alignPosition(4); inputStream.alignPosition(4);
identifier = inputStream.readByte() << 24 | inputStream.readByte() << 16 | identifier = inputStream.readByte() << 24 | inputStream.readByte() << 16
inputStream.readByte() << 8 | inputStream.readByte(); | inputStream.readByte() << 8 | inputStream.readByte();
if(identifier != TLEN_ID) { if (identifier != TLEN_ID) {
throw new BlenderFileException("Invalid identifier! '" + this.toString(TLEN_ID) + "' expected and found: " + this.toString(identifier)); throw new BlenderFileException("Invalid identifier! '" + this.toString(TLEN_ID) + "' expected and found: " + this.toString(identifier));
} }
int[] lengths = new int[amount];//theamount is the same as int types int[] lengths = new int[amount];//theamount is the same as int types
for(int i = 0; i < amount; ++i) { for (int i = 0; i < amount; ++i) {
lengths[i] = inputStream.readShort(); lengths[i] = inputStream.readShort();
} }
//reading structures //reading structures
inputStream.alignPosition(4); inputStream.alignPosition(4);
identifier = inputStream.readByte() << 24 | inputStream.readByte() << 16 | identifier = inputStream.readByte() << 24 | inputStream.readByte() << 16
inputStream.readByte() << 8 | inputStream.readByte(); | inputStream.readByte() << 8 | inputStream.readByte();
if(identifier != STRC_ID) { if (identifier != STRC_ID) {
throw new BlenderFileException("Invalid identifier! '" + this.toString(STRC_ID) + "' expected and found: " + this.toString(identifier)); throw new BlenderFileException("Invalid identifier! '" + this.toString(STRC_ID) + "' expected and found: " + this.toString(identifier));
} }
amount = inputStream.readInt(); amount = inputStream.readInt();
if(amount <= 0) { if (amount <= 0) {
throw new BlenderFileException("The structures amount number should be positive!"); throw new BlenderFileException("The structures amount number should be positive!");
} }
structures = new Structure[amount]; structures = new Structure[amount];
structuresMap = new HashMap<String, Structure>(amount); structuresMap = new HashMap<String, Structure>(amount);
for(int i = 0; i < amount; ++i) { for (int i = 0; i < amount; ++i) {
structures[i] = new Structure(inputStream, names, types, dataRepository); structures[i] = new Structure(inputStream, names, types, dataRepository);
if(structuresMap.containsKey(structures[i].getType())) { if (structuresMap.containsKey(structures[i].getType())) {
throw new BlenderFileException("Blend file seems to be corrupted! The type " + structures[i].getType() + " is defined twice!"); throw new BlenderFileException("Blend file seems to be corrupted! The type " + structures[i].getType() + " is defined twice!");
} }
structuresMap.put(structures[i].getType(), structures[i]); structuresMap.put(structures[i].getType(), structures[i]);
} }
} }
/** /**
* This method returns the amount of the structures. * This method returns the amount of the structures.
* @return the amount of the structures * @return the amount of the structures
*/ */
public int getStructuresCount() { public int getStructuresCount() {
return structures.length; return structures.length;
} }
/** /**
* This method returns the structure of the given index. * This method returns the structure of the given index.
* @param index * @param index
* the index of the structure * the index of the structure
* @return the structure of the given index * @return the structure of the given index
*/ */
public Structure getStructure(int index) { public Structure getStructure(int index) {
try { try {
return (Structure)structures[index].clone(); return (Structure) structures[index].clone();
} catch(CloneNotSupportedException e) { } catch (CloneNotSupportedException e) {
throw new IllegalStateException("Structure should be clonable!!!", e); throw new IllegalStateException("Structure should be clonable!!!", e);
} }
} }
/** /**
* This method returns a structure of the given name. If the name does not exists then null is returned. * This method returns a structure of the given name. If the name does not exists then null is returned.
* @param name * @param name
* the name of the structure * the name of the structure
* @return the required structure or null if the given name is inapropriate * @return the required structure or null if the given name is inapropriate
*/ */
public Structure getStructure(String name) { public Structure getStructure(String name) {
try { try {
return (Structure)structuresMap.get(name).clone(); return (Structure) structuresMap.get(name).clone();
} catch(CloneNotSupportedException e) { } catch (CloneNotSupportedException e) {
throw new IllegalStateException(e.getMessage(), e); throw new IllegalStateException(e.getMessage(), e);
} }
} }
/** /**
* This method indicates if the structure of the given name exists. * This method indicates if the structure of the given name exists.
* @param name * @param name
* the name of the structure * the name of the structure
* @return true if the structure exists and false otherwise * @return true if the structure exists and false otherwise
*/ */
public boolean hasStructure(String name) { public boolean hasStructure(String name) {
return structuresMap.containsKey(name); return structuresMap.containsKey(name);
} }
/** /**
* This method converts the given identifier code to string. * This method converts the given identifier code to string.
* @param code * @param code
* the code taht is to be converted * the code taht is to be converted
* @return the string value of the identifier * @return the string value of the identifier
*/ */
private String toString(int code) { private String toString(int code) {
char c1 = (char)((code & 0xFF000000) >> 24); char c1 = (char) ((code & 0xFF000000) >> 24);
char c2 = (char)((code & 0xFF0000) >> 16); char c2 = (char) ((code & 0xFF0000) >> 16);
char c3 = (char)((code & 0xFF00) >> 8); char c3 = (char) ((code & 0xFF00) >> 8);
char c4 = (char)(code & 0xFF); char c4 = (char) (code & 0xFF);
return String.valueOf(c1) + c2 + c3 + c4; return String.valueOf(c1) + c2 + c3 + c4;
} }
@Override @Override
public String toString() { public String toString() {
StringBuilder stringBuilder = new StringBuilder("=============== ").append(SDNA_ID).append('\n'); StringBuilder stringBuilder = new StringBuilder("=============== ").append(SDNA_ID).append('\n');
for(Structure structure : structures) { for (Structure structure : structures) {
stringBuilder.append(structure.toString()).append('\n'); stringBuilder.append(structure.toString()).append('\n');
} }
return stringBuilder.append("===============").toString(); return stringBuilder.append("===============").toString();
} }
} }

@ -15,305 +15,306 @@ import com.jme3.scene.plugins.blender.utils.Pointer;
* another structure. * another structure.
* @author Marcin Roguski * @author Marcin Roguski
*/ */
/*package*/class Field implements Cloneable { /*package*/
private static final int NAME_LENGTH = 24; class Field implements Cloneable {
private static final int TYPE_LENGTH = 16;
/** The data repository. */ private static final int NAME_LENGTH = 24;
public DataRepository dataRepository; private static final int TYPE_LENGTH = 16;
/** The type of the field. */ /** The data repository. */
public String type; public DataRepository dataRepository;
/** The name of the field. */ /** The type of the field. */
public String name; public String type;
/** The value of the field. Filled during data reading. */ /** The name of the field. */
public Object value; public String name;
/** This variable indicates the level of the pointer. */ /** The value of the field. Filled during data reading. */
public int pointerLevel; public Object value;
/** /** This variable indicates the level of the pointer. */
* This variable determines the sizes of the array. If the value is null the n the field is not an array. public int pointerLevel;
*/ /**
public int[] tableSizes; * This variable determines the sizes of the array. If the value is null the n the field is not an array.
/** This variable indicates if the field is a function pointer. */ */
public boolean function; public int[] tableSizes;
/** This variable indicates if the field is a function pointer. */
public boolean function;
/** /**
* Constructor. Saves the field data and parses its name. * Constructor. Saves the field data and parses its name.
* @param name * @param name
* the name of the field * the name of the field
* @param type * @param type
* the type of the field * the type of the field
* @param dataRepository * @param dataRepository
* the data repository * the data repository
* @throws BlenderFileException * @throws BlenderFileException
* this exception is thrown if the names contain errors * this exception is thrown if the names contain errors
*/ */
public Field(String name, String type, DataRepository dataRepository) throws BlenderFileException { public Field(String name, String type, DataRepository dataRepository) throws BlenderFileException {
this.type = type; this.type = type;
this.dataRepository = dataRepository; this.dataRepository = dataRepository;
this.parseField(new StringBuilder(name)); this.parseField(new StringBuilder(name));
} }
/** /**
* Copy constructor. Used in clone method. Copying is not full. The value in the new object is not set so that we * Copy constructor. Used in clone method. Copying is not full. The value in the new object is not set so that we
* have a clead empty copy of the filed to fill with data. * have a clead empty copy of the filed to fill with data.
* @param field * @param field
* the object that we copy * the object that we copy
*/ */
private Field(Field field) { private Field(Field field) {
type = field.type; type = field.type;
name = field.name; name = field.name;
dataRepository = field.dataRepository; dataRepository = field.dataRepository;
pointerLevel = field.pointerLevel; pointerLevel = field.pointerLevel;
if(field.tableSizes != null) { if (field.tableSizes != null) {
tableSizes = field.tableSizes.clone(); tableSizes = field.tableSizes.clone();
} }
function = field.function; function = field.function;
} }
@Override @Override
public Object clone() throws CloneNotSupportedException { public Object clone() throws CloneNotSupportedException {
return new Field(this); return new Field(this);
} }
/** /**
* This method fills the field wth data read from the input stream. * This method fills the field wth data read from the input stream.
* @param blenderInputStream * @param blenderInputStream
* the stream we read data from * the stream we read data from
* @throws BlenderFileException * @throws BlenderFileException
* an exception is thrown when the blend file is somehow invalid or corrupted * an exception is thrown when the blend file is somehow invalid or corrupted
*/ */
public void fill(BlenderInputStream blenderInputStream) throws BlenderFileException { public void fill(BlenderInputStream blenderInputStream) throws BlenderFileException {
int dataToRead = 1; int dataToRead = 1;
if(tableSizes != null && tableSizes.length > 0) { if (tableSizes != null && tableSizes.length > 0) {
for(int size : tableSizes) { for (int size : tableSizes) {
if(size <= 0) { if (size <= 0) {
throw new BlenderFileException("The field " + name + " has invalid table size: " + size); throw new BlenderFileException("The field " + name + " has invalid table size: " + size);
} }
dataToRead *= size; dataToRead *= size;
} }
} }
DataType dataType = pointerLevel == 0 ? DataType.getDataType(type, dataRepository) : DataType.POINTER; DataType dataType = pointerLevel == 0 ? DataType.getDataType(type, dataRepository) : DataType.POINTER;
switch(dataType) { switch (dataType) {
case POINTER: case POINTER:
if(dataToRead == 1) { if (dataToRead == 1) {
Pointer pointer = new Pointer(pointerLevel, function, dataRepository); Pointer pointer = new Pointer(pointerLevel, function, dataRepository);
pointer.fill(blenderInputStream); pointer.fill(blenderInputStream);
value = pointer; value = pointer;
} else { } else {
Pointer[] data = new Pointer[dataToRead]; Pointer[] data = new Pointer[dataToRead];
for(int i = 0; i < dataToRead; ++i) { for (int i = 0; i < dataToRead; ++i) {
Pointer pointer = new Pointer(pointerLevel, function, dataRepository); Pointer pointer = new Pointer(pointerLevel, function, dataRepository);
pointer.fill(blenderInputStream); pointer.fill(blenderInputStream);
data[i] = pointer; data[i] = pointer;
} }
value = new DynamicArray<Pointer>(tableSizes, data); value = new DynamicArray<Pointer>(tableSizes, data);
} }
break; break;
case CHARACTER: case CHARACTER:
//character is also stored as a number, because sometimes the new blender version uses //character is also stored as a number, because sometimes the new blender version uses
//other number type instead of character as a field type //other number type instead of character as a field type
//and characters are very often used as byte number stores instead of real chars //and characters are very often used as byte number stores instead of real chars
if(dataToRead == 1) { if (dataToRead == 1) {
value = Byte.valueOf((byte)blenderInputStream.readByte()); value = Byte.valueOf((byte) blenderInputStream.readByte());
} else { } else {
Character[] data = new Character[dataToRead]; Character[] data = new Character[dataToRead];
for(int i = 0; i < dataToRead; ++i) { for (int i = 0; i < dataToRead; ++i) {
data[i] = Character.valueOf((char)blenderInputStream.readByte()); data[i] = Character.valueOf((char) blenderInputStream.readByte());
} }
value = new DynamicArray<Character>(tableSizes, data); value = new DynamicArray<Character>(tableSizes, data);
} }
break; break;
case SHORT: case SHORT:
if(dataToRead == 1) { if (dataToRead == 1) {
value = Integer.valueOf(blenderInputStream.readShort()); value = Integer.valueOf(blenderInputStream.readShort());
} else { } else {
Number[] data = new Number[dataToRead]; Number[] data = new Number[dataToRead];
for(int i = 0; i < dataToRead; ++i) { for (int i = 0; i < dataToRead; ++i) {
data[i] = Integer.valueOf(blenderInputStream.readShort()); data[i] = Integer.valueOf(blenderInputStream.readShort());
} }
value = new DynamicArray<Number>(tableSizes, data); value = new DynamicArray<Number>(tableSizes, data);
} }
break; break;
case INTEGER: case INTEGER:
if(dataToRead == 1) { if (dataToRead == 1) {
value = Integer.valueOf(blenderInputStream.readInt()); value = Integer.valueOf(blenderInputStream.readInt());
} else { } else {
Number[] data = new Number[dataToRead]; Number[] data = new Number[dataToRead];
for(int i = 0; i < dataToRead; ++i) { for (int i = 0; i < dataToRead; ++i) {
data[i] = Integer.valueOf(blenderInputStream.readInt()); data[i] = Integer.valueOf(blenderInputStream.readInt());
} }
value = new DynamicArray<Number>(tableSizes, data); value = new DynamicArray<Number>(tableSizes, data);
} }
break; break;
case LONG: case LONG:
if(dataToRead == 1) { if (dataToRead == 1) {
value = Long.valueOf(blenderInputStream.readLong()); value = Long.valueOf(blenderInputStream.readLong());
} else { } else {
Number[] data = new Number[dataToRead]; Number[] data = new Number[dataToRead];
for(int i = 0; i < dataToRead; ++i) { for (int i = 0; i < dataToRead; ++i) {
data[i] = Long.valueOf(blenderInputStream.readLong()); data[i] = Long.valueOf(blenderInputStream.readLong());
} }
value = new DynamicArray<Number>(tableSizes, data); value = new DynamicArray<Number>(tableSizes, data);
} }
break; break;
case FLOAT: case FLOAT:
if(dataToRead == 1) { if (dataToRead == 1) {
value = Float.valueOf(blenderInputStream.readFloat()); value = Float.valueOf(blenderInputStream.readFloat());
} else { } else {
Number[] data = new Number[dataToRead]; Number[] data = new Number[dataToRead];
for(int i = 0; i < dataToRead; ++i) { for (int i = 0; i < dataToRead; ++i) {
data[i] = Float.valueOf(blenderInputStream.readFloat()); data[i] = Float.valueOf(blenderInputStream.readFloat());
} }
value = new DynamicArray<Number>(tableSizes, data); value = new DynamicArray<Number>(tableSizes, data);
} }
break; break;
case DOUBLE: case DOUBLE:
if(dataToRead == 1) { if (dataToRead == 1) {
value = Double.valueOf(blenderInputStream.readDouble()); value = Double.valueOf(blenderInputStream.readDouble());
} else { } else {
Number[] data = new Number[dataToRead]; Number[] data = new Number[dataToRead];
for(int i = 0; i < dataToRead; ++i) { for (int i = 0; i < dataToRead; ++i) {
data[i] = Double.valueOf(blenderInputStream.readDouble()); data[i] = Double.valueOf(blenderInputStream.readDouble());
} }
value = new DynamicArray<Number>(tableSizes, data); value = new DynamicArray<Number>(tableSizes, data);
} }
break; break;
case VOID: case VOID:
break; break;
case STRUCTURE: case STRUCTURE:
if(dataToRead == 1) { if (dataToRead == 1) {
Structure structure = dataRepository.getDnaBlockData().getStructure(type); Structure structure = dataRepository.getDnaBlockData().getStructure(type);
structure.fill(blenderInputStream); structure.fill(blenderInputStream);
value = structure; value = structure;
} else { } else {
Structure[] data = new Structure[dataToRead]; Structure[] data = new Structure[dataToRead];
for(int i = 0; i < dataToRead; ++i) { for (int i = 0; i < dataToRead; ++i) {
Structure structure = dataRepository.getDnaBlockData().getStructure(type); Structure structure = dataRepository.getDnaBlockData().getStructure(type);
structure.fill(blenderInputStream); structure.fill(blenderInputStream);
data[i] = structure; data[i] = structure;
} }
value = new DynamicArray<Structure>(tableSizes, data); value = new DynamicArray<Structure>(tableSizes, data);
} }
break; break;
default: default:
throw new IllegalStateException("Unimplemented filling of type: " + type); throw new IllegalStateException("Unimplemented filling of type: " + type);
} }
} }
/** /**
* This method parses the field name to determine how the field should be used. * This method parses the field name to determine how the field should be used.
* @param nameBuilder * @param nameBuilder
* the name of the field (given as StringBuilder) * the name of the field (given as StringBuilder)
* @throws BlenderFileException * @throws BlenderFileException
* this exception is thrown if the names contain errors * this exception is thrown if the names contain errors
*/ */
private void parseField(StringBuilder nameBuilder) throws BlenderFileException { private void parseField(StringBuilder nameBuilder) throws BlenderFileException {
this.removeWhitespaces(nameBuilder); this.removeWhitespaces(nameBuilder);
//veryfying if the name is a pointer //veryfying if the name is a pointer
int pointerIndex = nameBuilder.indexOf("*"); int pointerIndex = nameBuilder.indexOf("*");
while(pointerIndex >= 0) { while (pointerIndex >= 0) {
++pointerLevel; ++pointerLevel;
nameBuilder.deleteCharAt(pointerIndex); nameBuilder.deleteCharAt(pointerIndex);
pointerIndex = nameBuilder.indexOf("*"); pointerIndex = nameBuilder.indexOf("*");
} }
//veryfying if the name is a function pointer //veryfying if the name is a function pointer
if(nameBuilder.indexOf("(") >= 0) { if (nameBuilder.indexOf("(") >= 0) {
function = true; function = true;
this.removeCharacter(nameBuilder, '('); this.removeCharacter(nameBuilder, '(');
this.removeCharacter(nameBuilder, ')'); this.removeCharacter(nameBuilder, ')');
} else { } else {
//veryfying if the name is a table //veryfying if the name is a table
int tableStartIndex = 0; int tableStartIndex = 0;
List<Integer> lengths = new ArrayList<Integer>(3);//3 dimensions will be enough in most cases List<Integer> lengths = new ArrayList<Integer>(3);//3 dimensions will be enough in most cases
do { do {
tableStartIndex = nameBuilder.indexOf("["); tableStartIndex = nameBuilder.indexOf("[");
if(tableStartIndex > 0) { if (tableStartIndex > 0) {
int tableStopIndex = nameBuilder.indexOf("]"); int tableStopIndex = nameBuilder.indexOf("]");
if(tableStopIndex < 0) { if (tableStopIndex < 0) {
throw new BlenderFileException("Invalid structure name: " + name); throw new BlenderFileException("Invalid structure name: " + name);
} }
try { try {
lengths.add(Integer.valueOf(nameBuilder.substring(tableStartIndex + 1, tableStopIndex))); lengths.add(Integer.valueOf(nameBuilder.substring(tableStartIndex + 1, tableStopIndex)));
} catch(NumberFormatException e) { } catch (NumberFormatException e) {
throw new BlenderFileException("Invalid structure name caused by invalid table length: " + name, e); throw new BlenderFileException("Invalid structure name caused by invalid table length: " + name, e);
} }
nameBuilder.delete(tableStartIndex, tableStopIndex + 1); nameBuilder.delete(tableStartIndex, tableStopIndex + 1);
} }
} while(tableStartIndex > 0); } while (tableStartIndex > 0);
if(!lengths.isEmpty()) { if (!lengths.isEmpty()) {
tableSizes = new int[lengths.size()]; tableSizes = new int[lengths.size()];
for(int i = 0; i < tableSizes.length; ++i) { for (int i = 0; i < tableSizes.length; ++i) {
tableSizes[i] = lengths.get(i).intValue(); tableSizes[i] = lengths.get(i).intValue();
} }
} }
} }
name = nameBuilder.toString(); name = nameBuilder.toString();
} }
/** /**
* This method removes the required character from the text. * This method removes the required character from the text.
* @param text * @param text
* the text we remove characters from * the text we remove characters from
* @param toRemove * @param toRemove
* the character to be removed * the character to be removed
*/ */
private void removeCharacter(StringBuilder text, char toRemove) { private void removeCharacter(StringBuilder text, char toRemove) {
for(int i = 0; i < text.length(); ++i) { for (int i = 0; i < text.length(); ++i) {
if(text.charAt(i) == toRemove) { if (text.charAt(i) == toRemove) {
text.deleteCharAt(i); text.deleteCharAt(i);
--i; --i;
} }
} }
} }
/** /**
* This method removes all whitespaces from the text. * This method removes all whitespaces from the text.
* @param text * @param text
* the text we remove whitespaces from * the text we remove whitespaces from
*/ */
private void removeWhitespaces(StringBuilder text) { private void removeWhitespaces(StringBuilder text) {
for(int i = 0; i < text.length(); ++i) { for (int i = 0; i < text.length(); ++i) {
if(Character.isWhitespace(text.charAt(i))) { if (Character.isWhitespace(text.charAt(i))) {
text.deleteCharAt(i); text.deleteCharAt(i);
--i; --i;
} }
} }
} }
@Override @Override
public String toString() { public String toString() {
StringBuilder result = new StringBuilder(); StringBuilder result = new StringBuilder();
if(function) { if (function) {
result.append('('); result.append('(');
} }
for(int i = 0; i < pointerLevel; ++i) { for (int i = 0; i < pointerLevel; ++i) {
result.append('*'); result.append('*');
} }
result.append(name); result.append(name);
if(tableSizes != null) { if (tableSizes != null) {
for(int i = 0; i < tableSizes.length; ++i) { for (int i = 0; i < tableSizes.length; ++i) {
result.append('[').append(tableSizes[i]).append(']'); result.append('[').append(tableSizes[i]).append(']');
} }
} }
if(function) { if (function) {
result.append(")()"); result.append(")()");
} }
//insert appropriate amount of spaces to format the output corrently //insert appropriate amount of spaces to format the output corrently
int nameLength = result.length(); int nameLength = result.length();
result.append(' ');//at least one space is a must result.append(' ');//at least one space is a must
for(int i = 1; i < NAME_LENGTH - nameLength; ++i) {//we start from i=1 because one space is already added for (int i = 1; i < NAME_LENGTH - nameLength; ++i) {//we start from i=1 because one space is already added
result.append(' '); result.append(' ');
} }
result.append(type); result.append(type);
nameLength = result.length(); nameLength = result.length();
for(int i = 0; i < NAME_LENGTH + TYPE_LENGTH - nameLength; ++i) { for (int i = 0; i < NAME_LENGTH + TYPE_LENGTH - nameLength; ++i) {
result.append(' '); result.append(' ');
} }
if(value instanceof Character) { if (value instanceof Character) {
result.append(" = ").append((int)((Character)value).charValue()); result.append(" = ").append((int) ((Character) value).charValue());
} else { } else {
result.append(" = ").append(value != null ? value.toString() : "null"); result.append(" = ").append(value != null ? value.toString() : "null");
} }
return result.toString(); return result.toString();
} }
} }

@ -41,161 +41,160 @@ import com.jme3.scene.plugins.blender.utils.DataRepository;
* @author Marcin Roguski * @author Marcin Roguski
*/ */
public class FileBlockHeader { public class FileBlockHeader {
public static final int BLOCK_TE00 = 'T' << 24 | 'E' << 16; //TE00
public static final int BLOCK_ME00 = 'M' << 24 | 'E' << 16; //ME00
public static final int BLOCK_SR00 = 'S' << 24 | 'R' << 16; //SR00
public static final int BLOCK_CA00 = 'C' << 24 | 'A' << 16; //CA00
public static final int BLOCK_LA00 = 'L' << 24 | 'A' << 16; //LA00
public static final int BLOCK_OB00 = 'O' << 24 | 'B' << 16; //OB00
public static final int BLOCK_MA00 = 'M' << 24 | 'A' << 16; //MA00
public static final int BLOCK_SC00 = 'S' << 24 | 'C' << 16; //SC00
public static final int BLOCK_WO00 = 'W' << 24 | 'O' << 16; //WO00
public static final int BLOCK_TX00 = 'T' << 24 | 'X' << 16; //TX00
public static final int BLOCK_IP00 = 'I' << 24 | 'P' << 16; //IP00
public static final int BLOCK_AC00 = 'A' << 24 | 'C' << 16; //AC00
public static final int BLOCK_GLOB = 'G' << 24 | 'L' << 16 | 'O' << 8 | 'B'; //GLOB public static final int BLOCK_TE00 = 'T' << 24 | 'E' << 16; //TE00
public static final int BLOCK_REND = 'R' << 24 | 'E' << 16 | 'N' << 8 | 'D'; //REND public static final int BLOCK_ME00 = 'M' << 24 | 'E' << 16; //ME00
public static final int BLOCK_DATA = 'D' << 24 | 'A' << 16 | 'T' << 8 | 'A'; //DATA public static final int BLOCK_SR00 = 'S' << 24 | 'R' << 16; //SR00
public static final int BLOCK_DNA1 = 'D' << 24 | 'N' << 16 | 'A' << 8 | '1'; //DNA1 public static final int BLOCK_CA00 = 'C' << 24 | 'A' << 16; //CA00
public static final int BLOCK_ENDB = 'E' << 24 | 'N' << 16 | 'D' << 8 | 'B'; //ENDB public static final int BLOCK_LA00 = 'L' << 24 | 'A' << 16; //LA00
public static final int BLOCK_OB00 = 'O' << 24 | 'B' << 16; //OB00
/** Identifier of the file-block [4 bytes]. */ public static final int BLOCK_MA00 = 'M' << 24 | 'A' << 16; //MA00
private int code; public static final int BLOCK_SC00 = 'S' << 24 | 'C' << 16; //SC00
/** Total length of the data after the file-block-header [4 bytes]. */ public static final int BLOCK_WO00 = 'W' << 24 | 'O' << 16; //WO00
private int size; public static final int BLOCK_TX00 = 'T' << 24 | 'X' << 16; //TX00
/** public static final int BLOCK_IP00 = 'I' << 24 | 'P' << 16; //IP00
* Memory address the structure was located when written to disk [4 or 8 bytes (defined in file header as a pointer public static final int BLOCK_AC00 = 'A' << 24 | 'C' << 16; //AC00
* size)]. public static final int BLOCK_GLOB = 'G' << 24 | 'L' << 16 | 'O' << 8 | 'B'; //GLOB
*/ public static final int BLOCK_REND = 'R' << 24 | 'E' << 16 | 'N' << 8 | 'D'; //REND
private long oldMemoryAddress; public static final int BLOCK_DATA = 'D' << 24 | 'A' << 16 | 'T' << 8 | 'A'; //DATA
/** Index of the SDNA structure [4 bytes]. */ public static final int BLOCK_DNA1 = 'D' << 24 | 'N' << 16 | 'A' << 8 | '1'; //DNA1
private int sdnaIndex; public static final int BLOCK_ENDB = 'E' << 24 | 'N' << 16 | 'D' << 8 | 'B'; //ENDB
/** Number of structure located in this file-block [4 bytes]. */ /** Identifier of the file-block [4 bytes]. */
private int count; private int code;
/** Start position of the block's data in the stream. */ /** Total length of the data after the file-block-header [4 bytes]. */
private int blockPosition; private int size;
/**
/** * Memory address the structure was located when written to disk [4 or 8 bytes (defined in file header as a pointer
* Constructor. Loads the block header from the given stream during instance creation. * size)].
* @param inputStream */
* the stream we read the block header from private long oldMemoryAddress;
* @param dataRepository /** Index of the SDNA structure [4 bytes]. */
* the data repository private int sdnaIndex;
* @throws BlenderFileException /** Number of structure located in this file-block [4 bytes]. */
* this exception is thrown when the pointer size is neither 4 nor 8 private int count;
*/ /** Start position of the block's data in the stream. */
public FileBlockHeader(BlenderInputStream inputStream, DataRepository dataRepository) throws BlenderFileException { private int blockPosition;
inputStream.alignPosition(4);
code = inputStream.readByte() << 24 | inputStream.readByte() << 16 | /**
inputStream.readByte() << 8 | inputStream.readByte(); * Constructor. Loads the block header from the given stream during instance creation.
size = inputStream.readInt(); * @param inputStream
oldMemoryAddress = inputStream.readPointer(); * the stream we read the block header from
sdnaIndex = inputStream.readInt(); * @param dataRepository
count = inputStream.readInt(); * the data repository
blockPosition = inputStream.getPosition(); * @throws BlenderFileException
if(FileBlockHeader.BLOCK_DNA1 == code) { * this exception is thrown when the pointer size is neither 4 nor 8
dataRepository.setBlockData(new DnaBlockData(inputStream, dataRepository)); */
} else { public FileBlockHeader(BlenderInputStream inputStream, DataRepository dataRepository) throws BlenderFileException {
inputStream.setPosition(blockPosition + size); inputStream.alignPosition(4);
dataRepository.addFileBlockHeader(Long.valueOf(oldMemoryAddress), this); code = inputStream.readByte() << 24 | inputStream.readByte() << 16
} | inputStream.readByte() << 8 | inputStream.readByte();
} size = inputStream.readInt();
oldMemoryAddress = inputStream.readPointer();
/** sdnaIndex = inputStream.readInt();
* This method returns the structure described by the header filled with appropriate data. count = inputStream.readInt();
* @param dataRepository blockPosition = inputStream.getPosition();
* the data repository if (FileBlockHeader.BLOCK_DNA1 == code) {
* @return structure filled with data dataRepository.setBlockData(new DnaBlockData(inputStream, dataRepository));
* @throws BlenderFileException } else {
*/ inputStream.setPosition(blockPosition + size);
public Structure getStructure(DataRepository dataRepository) throws BlenderFileException { dataRepository.addFileBlockHeader(Long.valueOf(oldMemoryAddress), this);
dataRepository.getInputStream().setPosition(blockPosition); }
Structure structure = dataRepository.getDnaBlockData().getStructure(sdnaIndex); }
structure.fill(dataRepository.getInputStream());
return structure; /**
} * This method returns the structure described by the header filled with appropriate data.
* @param dataRepository
/** * the data repository
* This method returns the code of this data block. * @return structure filled with data
* @return the code of this data block * @throws BlenderFileException
*/ */
public int getCode() { public Structure getStructure(DataRepository dataRepository) throws BlenderFileException {
return code; dataRepository.getInputStream().setPosition(blockPosition);
} Structure structure = dataRepository.getDnaBlockData().getStructure(sdnaIndex);
structure.fill(dataRepository.getInputStream());
/** return structure;
* This method returns the size of the data stored in this block. }
* @return the size of the data stored in this block
*/ /**
public int getSize() { * This method returns the code of this data block.
return size; * @return the code of this data block
} */
public int getCode() {
/** return code;
* This method returns the memory address. }
* @return the memory address
*/ /**
public long getOldMemoryAddress() { * This method returns the size of the data stored in this block.
return oldMemoryAddress; * @return the size of the data stored in this block
} */
public int getSize() {
/** return size;
* This method returns the sdna index. }
* @return the sdna index
*/ /**
public int getSdnaIndex() { * This method returns the memory address.
return sdnaIndex; * @return the memory address
} */
public long getOldMemoryAddress() {
/** return oldMemoryAddress;
* This data returns the number of structure stored in the data block after this header. }
* @return the number of structure stored in the data block after this header
*/ /**
public int getCount() { * This method returns the sdna index.
return count; * @return the sdna index
} */
public int getSdnaIndex() {
/** return sdnaIndex;
* This method returns the start position of the data block in the blend file stream. }
* @return the start position of the data block
*/ /**
public int getBlockPosition() { * This data returns the number of structure stored in the data block after this header.
return blockPosition; * @return the number of structure stored in the data block after this header
} */
public int getCount() {
/** return count;
* This method indicates if the block is the last block in the file. }
* @return true if this block is the last one in the file nad false otherwise
*/ /**
public boolean isLastBlock() { * This method returns the start position of the data block in the blend file stream.
return FileBlockHeader.BLOCK_ENDB == code; * @return the start position of the data block
} */
public int getBlockPosition() {
/** return blockPosition;
* This method indicates if the block is the SDNA block. }
* @return true if this block is the SDNA block and false otherwise
*/ /**
public boolean isDnaBlock() { * This method indicates if the block is the last block in the file.
return FileBlockHeader.BLOCK_DNA1 == code; * @return true if this block is the last one in the file nad false otherwise
} */
public boolean isLastBlock() {
@Override return FileBlockHeader.BLOCK_ENDB == code;
public String toString() { }
return "FILE BLOCK HEADER [" + this.codeToString(code) + " : " + size + " : " + oldMemoryAddress + " : " + sdnaIndex + " : " + count + "]";
} /**
* This method indicates if the block is the SDNA block.
/** * @return true if this block is the SDNA block and false otherwise
* This method transforms the coded bloch id into a string value. */
* @param code public boolean isDnaBlock() {
* the id of the block return FileBlockHeader.BLOCK_DNA1 == code;
* @return the string value of the block id }
*/
protected String codeToString(int code) { @Override
char c1 = (char)((code & 0xFF000000) >> 24); public String toString() {
char c2 = (char)((code & 0xFF0000) >> 16); return "FILE BLOCK HEADER [" + this.codeToString(code) + " : " + size + " : " + oldMemoryAddress + " : " + sdnaIndex + " : " + count + "]";
char c3 = (char)((code & 0xFF00) >> 8); }
char c4 = (char)(code & 0xFF);
return String.valueOf(c1) + c2 + c3 + c4; /**
} * This method transforms the coded bloch id into a string value.
* @param code
* the id of the block
* @return the string value of the block id
*/
protected String codeToString(int code) {
char c1 = (char) ((code & 0xFF000000) >> 24);
char c2 = (char) ((code & 0xFF0000) >> 16);
char c3 = (char) ((code & 0xFF00) >> 8);
char c4 = (char) (code & 0xFF);
return String.valueOf(c1) + c2 + c3 + c4;
}
} }

@ -46,266 +46,269 @@ import com.jme3.scene.plugins.blender.utils.Pointer;
* @author Marcin Roguski * @author Marcin Roguski
*/ */
public class Structure implements Cloneable { public class Structure implements Cloneable {
/** The data repository. */
private DataRepository dataRepository;
/** The address of the block that fills the structure. */
private transient Long oldMemoryAddress;
/** The type of the structure. */
private String type;
/**
* The fields of the structure. Each field consists of a pair: name-type.
*/
private Field[] fields;
/** /** The data repository. */
* Constructor that copies the data of the structure. private DataRepository dataRepository;
* @param structure /** The address of the block that fills the structure. */
* the structure to copy. private transient Long oldMemoryAddress;
* @param dataRepository /** The type of the structure. */
* the data repository of the structure private String type;
* @throws CloneNotSupportedException /**
* this exception should never be thrown * The fields of the structure. Each field consists of a pair: name-type.
*/ */
private Structure(Structure structure, DataRepository dataRepository) throws CloneNotSupportedException { private Field[] fields;
type = structure.type;
fields = new Field[structure.fields.length];
for(int i = 0; i < fields.length; ++i) {
fields[i] = (Field)structure.fields[i].clone();
}
this.dataRepository = dataRepository;
this.oldMemoryAddress = structure.oldMemoryAddress;
}
/** /**
* Constructor. Loads the structure from the given stream during instance creation. * Constructor that copies the data of the structure.
* @param inputStream * @param structure
* the stream we read the structure from * the structure to copy.
* @param names * @param dataRepository
* the names from which the name of structure and its fields will be taken * the data repository of the structure
* @param types * @throws CloneNotSupportedException
* the names of types for the structure * this exception should never be thrown
* @param dataRepository */
* the data repository private Structure(Structure structure, DataRepository dataRepository) throws CloneNotSupportedException {
* @throws BlenderFileException type = structure.type;
* this exception occurs if the amount of fields, defined in the file, is negative fields = new Field[structure.fields.length];
*/ for (int i = 0; i < fields.length; ++i) {
public Structure(BlenderInputStream inputStream, String[] names, String[] types, DataRepository dataRepository) throws BlenderFileException { fields[i] = (Field) structure.fields[i].clone();
int nameIndex = inputStream.readShort(); }
type = types[nameIndex]; this.dataRepository = dataRepository;
this.dataRepository = dataRepository; this.oldMemoryAddress = structure.oldMemoryAddress;
int fieldsAmount = inputStream.readShort(); }
if(fieldsAmount < 0) {
throw new BlenderFileException("The amount of fields of " + this.type + " structure cannot be negative!");
}
if(fieldsAmount > 0) {
fields = new Field[fieldsAmount];
for(int i = 0; i < fieldsAmount; ++i) {
int typeIndex = inputStream.readShort();
nameIndex = inputStream.readShort();
fields[i] = new Field(names[nameIndex], types[typeIndex], dataRepository);
}
}
this.oldMemoryAddress = Long.valueOf(-1L);
}
/** /**
* This method fills the structure with data. * Constructor. Loads the structure from the given stream during instance creation.
* @param inputStream * @param inputStream
* the stream we read data from, its read cursor should be placed at the start position of the data for the * the stream we read the structure from
* structure * @param names
* @throws BlenderFileException * the names from which the name of structure and its fields will be taken
* an exception is thrown when the blend file is somehow invalid or corrupted * @param types
*/ * the names of types for the structure
public void fill(BlenderInputStream inputStream) throws BlenderFileException { * @param dataRepository
int position = inputStream.getPosition(); * the data repository
inputStream.setPosition(position - 8 - inputStream.getPointerSize()); * @throws BlenderFileException
this.oldMemoryAddress = Long.valueOf(inputStream.readPointer()); * this exception occurs if the amount of fields, defined in the file, is negative
inputStream.setPosition(position); */
for(Field field : fields) { public Structure(BlenderInputStream inputStream, String[] names, String[] types, DataRepository dataRepository) throws BlenderFileException {
field.fill(inputStream); int nameIndex = inputStream.readShort();
} type = types[nameIndex];
} this.dataRepository = dataRepository;
int fieldsAmount = inputStream.readShort();
if (fieldsAmount < 0) {
throw new BlenderFileException("The amount of fields of " + this.type + " structure cannot be negative!");
}
if (fieldsAmount > 0) {
fields = new Field[fieldsAmount];
for (int i = 0; i < fieldsAmount; ++i) {
int typeIndex = inputStream.readShort();
nameIndex = inputStream.readShort();
fields[i] = new Field(names[nameIndex], types[typeIndex], dataRepository);
}
}
this.oldMemoryAddress = Long.valueOf(-1L);
}
/** /**
* This method returns the value of the filed with a given name. * This method fills the structure with data.
* @param fieldName * @param inputStream
* the name of the field * the stream we read data from, its read cursor should be placed at the start position of the data for the
* @return the value of the field or null if no field with a given name is found * structure
*/ * @throws BlenderFileException
public Object getFieldValue(String fieldName) { * an exception is thrown when the blend file is somehow invalid or corrupted
for(Field field : fields) { */
if(field.name.equalsIgnoreCase(fieldName)) { public void fill(BlenderInputStream inputStream) throws BlenderFileException {
return field.value; int position = inputStream.getPosition();
} inputStream.setPosition(position - 8 - inputStream.getPointerSize());
} this.oldMemoryAddress = Long.valueOf(inputStream.readPointer());
return null; inputStream.setPosition(position);
} for (Field field : fields) {
field.fill(inputStream);
}
}
/** /**
* This method returns the value of the filed with a given name. The structure is considered to have flat fields * This method returns the value of the filed with a given name.
* only (no substructures). * @param fieldName
* @param fieldName * the name of the field
* the name of the field * @return the value of the field or null if no field with a given name is found
* @return the value of the field or null if no field with a given name is found */
*/ public Object getFieldValue(String fieldName) {
public Object getFlatFieldValue(String fieldName) { for (Field field : fields) {
for(Field field : fields) { if (field.name.equalsIgnoreCase(fieldName)) {
Object value = field.value; return field.value;
if(field.name.equalsIgnoreCase(fieldName)) { }
return value; }
} else if(value instanceof Structure) { return null;
value = ((Structure)value).getFlatFieldValue(fieldName); }
if(value != null) {//we can compare references here, since we use one static object as a NULL field value
return value;
}
}
}
return null;
}
/** /**
* This methos should be used on structures that are of a 'ListBase' type. It creates a List of structures that are * This method returns the value of the filed with a given name. The structure is considered to have flat fields
* held by this structure within the blend file. * only (no substructures).
* @param dataRepository * @param fieldName
* the data repository * the name of the field
* @return a list of filled structures * @return the value of the field or null if no field with a given name is found
* @throws BlenderFileException */
* this exception is thrown when the blend file structure is somehow invalid or corrupted public Object getFlatFieldValue(String fieldName) {
* @throws IllegalArgumentException for (Field field : fields) {
* this exception is thrown if the type of the structure is not 'ListBase' Object value = field.value;
*/ if (field.name.equalsIgnoreCase(fieldName)) {
public List<Structure> evaluateListBase(DataRepository dataRepository) throws BlenderFileException { return value;
if(!"ListBase".equals(this.type)) { } else if (value instanceof Structure) {
throw new IllegalStateException("This structure is not of type: 'ListBase'"); value = ((Structure) value).getFlatFieldValue(fieldName);
} if (value != null) {//we can compare references here, since we use one static object as a NULL field value
Pointer first = (Pointer)this.getFieldValue("first"); return value;
Pointer last = (Pointer)this.getFieldValue("last"); }
long currentAddress = 0; }
long lastAddress = last.getOldMemoryAddress(); }
List<Structure> result = new LinkedList<Structure>(); return null;
while(currentAddress != lastAddress) { }
currentAddress = first.getOldMemoryAddress();
Structure structure = first.fetchData(dataRepository.getInputStream()).get(0);
result.add(structure);
first = (Pointer)structure.getFlatFieldValue("next");
}
return result;
}
/** /**
* This method returns the type of the structure. * This methos should be used on structures that are of a 'ListBase' type. It creates a List of structures that are
* @return the type of the structure * held by this structure within the blend file.
*/ * @param dataRepository
public String getType() { * the data repository
return type; * @return a list of filled structures
} * @throws BlenderFileException
* this exception is thrown when the blend file structure is somehow invalid or corrupted
* @throws IllegalArgumentException
* this exception is thrown if the type of the structure is not 'ListBase'
*/
public List<Structure> evaluateListBase(DataRepository dataRepository) throws BlenderFileException {
if (!"ListBase".equals(this.type)) {
throw new IllegalStateException("This structure is not of type: 'ListBase'");
}
Pointer first = (Pointer) this.getFieldValue("first");
Pointer last = (Pointer) this.getFieldValue("last");
long currentAddress = 0;
long lastAddress = last.getOldMemoryAddress();
List<Structure> result = new LinkedList<Structure>();
while (currentAddress != lastAddress) {
currentAddress = first.getOldMemoryAddress();
Structure structure = first.fetchData(dataRepository.getInputStream()).get(0);
result.add(structure);
first = (Pointer) structure.getFlatFieldValue("next");
}
return result;
}
/** /**
* This method returns the amount of fields for the current structure. * This method returns the type of the structure.
* @return the amount of fields for the current structure * @return the type of the structure
*/ */
public int getFieldsAmount() { public String getType() {
return fields.length; return type;
} }
/** /**
* This method returns the field name of the given index. * This method returns the amount of fields for the current structure.
* @param fieldIndex * @return the amount of fields for the current structure
* the index of the field */
* @return the field name of the given index public int getFieldsAmount() {
*/ return fields.length;
public String getFieldName(int fieldIndex) { }
return fields[fieldIndex].name;
}
/** /**
* This method returns the field type of the given index. * This method returns the field name of the given index.
* @param fieldIndex * @param fieldIndex
* the index of the field * the index of the field
* @return the field type of the given index * @return the field name of the given index
*/ */
public String getFieldType(int fieldIndex) { public String getFieldName(int fieldIndex) {
return fields[fieldIndex].type; return fields[fieldIndex].name;
} }
/** /**
* This method returns the address of the structure. The strucutre should be filled with data otherwise an exception * This method returns the field type of the given index.
* is thrown. * @param fieldIndex
* @return the address of the feature stored in this structure * the index of the field
*/ * @return the field type of the given index
public Long getOldMemoryAddress() { */
if(oldMemoryAddress.longValue() == -1L) { public String getFieldType(int fieldIndex) {
throw new IllegalStateException("Call the 'fill' method and fill the structure with data first!"); return fields[fieldIndex].type;
} }
return oldMemoryAddress;
}
/** /**
* This method returns the name of the structure. If the structure has an ID field then the name is returned. * This method returns the address of the structure. The strucutre should be filled with data otherwise an exception
* Otherwise the name does not exists and the method returns null. * is thrown.
* @return the name of the structure read from the ID field or null * @return the address of the feature stored in this structure
*/ */
public String getName() { public Long getOldMemoryAddress() {
Structure id = (Structure)this.getFieldValue("ID"); if (oldMemoryAddress.longValue() == -1L) {
return id == null ? null : id.getFieldValue("name").toString().substring(2);//blender adds 2-charactes as a name prefix throw new IllegalStateException("Call the 'fill' method and fill the structure with data first!");
} }
return oldMemoryAddress;
}
@Override /**
public String toString() { * This method returns the name of the structure. If the structure has an ID field then the name is returned.
StringBuilder result = new StringBuilder("struct ").append(type).append(" {\n"); * Otherwise the name does not exists and the method returns null.
for(int i = 0; i < fields.length; ++i) { * @return the name of the structure read from the ID field or null
result.append(fields[i].toString()).append('\n'); */
} public String getName() {
return result.append('}').toString(); Structure id = (Structure) this.getFieldValue("ID");
} return id == null ? null : id.getFieldValue("name").toString().substring(2);//blender adds 2-charactes as a name prefix
}
@Override @Override
public Object clone() throws CloneNotSupportedException { public String toString() {
return new Structure(this, dataRepository); StringBuilder result = new StringBuilder("struct ").append(type).append(" {\n");
} for (int i = 0; i < fields.length; ++i) {
result.append(fields[i].toString()).append('\n');
}
return result.append('}').toString();
}
/** @Override
* This enum enumerates all known data types that can be found in the blend file. public Object clone() throws CloneNotSupportedException {
* @author Marcin Roguski return new Structure(this, dataRepository);
*/ }
/*package*/static enum DataType {
CHARACTER, SHORT, INTEGER, LONG, FLOAT, DOUBLE, VOID, STRUCTURE, POINTER;
/** The map containing the known primary types. */ /**
private static final Map<String, DataType> PRIMARY_TYPES = new HashMap<String, DataType>(10); * This enum enumerates all known data types that can be found in the blend file.
static { * @author Marcin Roguski
PRIMARY_TYPES.put("char", CHARACTER); */
PRIMARY_TYPES.put("uchar", CHARACTER); /*package*/
PRIMARY_TYPES.put("short", SHORT); static enum DataType {
PRIMARY_TYPES.put("ushort", SHORT);
PRIMARY_TYPES.put("int", INTEGER);
PRIMARY_TYPES.put("long", LONG);
PRIMARY_TYPES.put("ulong", LONG);
PRIMARY_TYPES.put("float", FLOAT);
PRIMARY_TYPES.put("double", DOUBLE);
PRIMARY_TYPES.put("void", VOID);
}
/** CHARACTER, SHORT, INTEGER, LONG, FLOAT, DOUBLE, VOID, STRUCTURE, POINTER;
* This method returns the data type that is appropriate to the given type name. WARNING! The type recognition /** The map containing the known primary types. */
* is case sensitive! private static final Map<String, DataType> PRIMARY_TYPES = new HashMap<String, DataType>(10);
* @param type
* the type name of the data static {
* @param dataRepository PRIMARY_TYPES.put("char", CHARACTER);
* the data repository PRIMARY_TYPES.put("uchar", CHARACTER);
* @return appropriate enum value to the given type name PRIMARY_TYPES.put("short", SHORT);
* @throws BlenderFileException PRIMARY_TYPES.put("ushort", SHORT);
* this exception is thrown if the given type name does not exist in the blend file PRIMARY_TYPES.put("int", INTEGER);
*/ PRIMARY_TYPES.put("long", LONG);
public static DataType getDataType(String type, DataRepository dataRepository) throws BlenderFileException { PRIMARY_TYPES.put("ulong", LONG);
DataType result = PRIMARY_TYPES.get(type); PRIMARY_TYPES.put("float", FLOAT);
if(result != null) { PRIMARY_TYPES.put("double", DOUBLE);
return result; PRIMARY_TYPES.put("void", VOID);
} }
if(dataRepository.getDnaBlockData().hasStructure(type)) {
return STRUCTURE; /**
} * This method returns the data type that is appropriate to the given type name. WARNING! The type recognition
throw new BlenderFileException("Unknown data type: " + type); * is case sensitive!
} * @param type
} * the type name of the data
* @param dataRepository
* the data repository
* @return appropriate enum value to the given type name
* @throws BlenderFileException
* this exception is thrown if the given type name does not exist in the blend file
*/
public static DataType getDataType(String type, DataRepository dataRepository) throws BlenderFileException {
DataType result = PRIMARY_TYPES.get(type);
if (result != null) {
return result;
}
if (dataRepository.getDnaBlockData().hasStructure(type)) {
return STRUCTURE;
}
throw new BlenderFileException("Unknown data type: " + type);
}
}
} }

@ -36,39 +36,41 @@ package com.jme3.scene.plugins.blender.exception;
* @author Marcin Roguski * @author Marcin Roguski
*/ */
public class BlenderFileException extends Exception { public class BlenderFileException extends Exception {
private static final long serialVersionUID = 7573482836437866767L;
/** private static final long serialVersionUID = 7573482836437866767L;
* Constructor. Creates an exception with no description.
*/
public BlenderFileException() {}
/** /**
* Constructor. Creates an exception containing the given message. * Constructor. Creates an exception with no description.
* @param message */
* the message describing the problem that occured public BlenderFileException() {
*/ }
public BlenderFileException(String message) {
super(message);
}
/** /**
* Constructor. Creates an exception that is based upon other thrown object. It contains the whole stacktrace then. * Constructor. Creates an exception containing the given message.
* @param throwable * @param message
* an exception/error that occured * the message describing the problem that occured
*/ */
public BlenderFileException(Throwable throwable) { public BlenderFileException(String message) {
super(throwable); super(message);
} }
/** /**
* Constructor. Creates an exception with both a message and stacktrace. * Constructor. Creates an exception that is based upon other thrown object. It contains the whole stacktrace then.
* @param message * @param throwable
* the message describing the problem that occured * an exception/error that occured
* @param throwable */
* an exception/error that occured public BlenderFileException(Throwable throwable) {
*/ super(throwable);
public BlenderFileException(String message, Throwable throwable) { }
super(message, throwable);
} /**
* Constructor. Creates an exception with both a message and stacktrace.
* @param message
* the message describing the problem that occured
* @param throwable
* an exception/error that occured
*/
public BlenderFileException(String message, Throwable throwable) {
super(message, throwable);
}
} }

@ -46,87 +46,87 @@ import com.jme3.scene.plugins.blender.utils.BlenderInputStream;
import com.jme3.scene.plugins.blender.utils.DataRepository; import com.jme3.scene.plugins.blender.utils.DataRepository;
import com.jme3.scene.plugins.blender.utils.Pointer; import com.jme3.scene.plugins.blender.utils.Pointer;
/** /**
* This class defines the methods to calculate certain aspects of animation and armature functionalities. * This class defines the methods to calculate certain aspects of animation and armature functionalities.
* @author Marcin Roguski * @author Marcin Roguski
*/ */
public class ArmatureHelper extends com.jme3.scene.plugins.blender.helpers.v249.ArmatureHelper { public class ArmatureHelper extends com.jme3.scene.plugins.blender.helpers.v249.ArmatureHelper {
private static final Logger LOGGER = Logger.getLogger(ArmatureHelper.class.getName());
/** private static final Logger LOGGER = Logger.getLogger(ArmatureHelper.class.getName());
* This constructor parses the given blender version and stores the result. Some functionalities may differ in
* different blender versions. /**
* @param blenderVersion * This constructor parses the given blender version and stores the result. Some functionalities may differ in
* the version read from the blend file * different blender versions.
*/ * @param blenderVersion
public ArmatureHelper(String blenderVersion) { * the version read from the blend file
super(blenderVersion); */
} public ArmatureHelper(String blenderVersion) {
super(blenderVersion);
}
@Override @Override
public BoneTrack[] getTracks(Structure actionStructure, DataRepository dataRepository, String objectName, String animationName) throws BlenderFileException { public BoneTrack[] getTracks(Structure actionStructure, DataRepository dataRepository, String objectName, String animationName) throws BlenderFileException {
if(blenderVersion<250) { if (blenderVersion < 250) {
return super.getTracks(actionStructure, dataRepository, objectName, animationName); return super.getTracks(actionStructure, dataRepository, objectName, animationName);
} }
LOGGER.log(Level.INFO, "Getting tracks!"); LOGGER.log(Level.INFO, "Getting tracks!");
int fps = dataRepository.getBlenderKey().getFps(); int fps = dataRepository.getBlenderKey().getFps();
int[] animationFrames = dataRepository.getBlenderKey().getAnimationFrames(objectName, animationName); int[] animationFrames = dataRepository.getBlenderKey().getAnimationFrames(objectName, animationName);
Structure groups = (Structure)actionStructure.getFieldValue("groups"); Structure groups = (Structure) actionStructure.getFieldValue("groups");
List<Structure> actionGroups = groups.evaluateListBase(dataRepository);//bActionGroup List<Structure> actionGroups = groups.evaluateListBase(dataRepository);//bActionGroup
if(actionGroups != null && actionGroups.size() > 0 && (bonesMap == null || bonesMap.size() == 0)) { if (actionGroups != null && actionGroups.size() > 0 && (bonesMap == null || bonesMap.size() == 0)) {
throw new IllegalStateException("No bones found! Cannot proceed to calculating tracks!"); throw new IllegalStateException("No bones found! Cannot proceed to calculating tracks!");
} }
List<BoneTrack> tracks = new ArrayList<BoneTrack>(); List<BoneTrack> tracks = new ArrayList<BoneTrack>();
for(Structure actionGroup : actionGroups) { for (Structure actionGroup : actionGroups) {
String name = actionGroup.getFieldValue("name").toString(); String name = actionGroup.getFieldValue("name").toString();
Integer boneIndex = bonesMap.get(name); Integer boneIndex = bonesMap.get(name);
if(boneIndex != null) { if (boneIndex != null) {
List<Structure> channels = ((Structure)actionGroup.getFieldValue("channels")).evaluateListBase(dataRepository); List<Structure> channels = ((Structure) actionGroup.getFieldValue("channels")).evaluateListBase(dataRepository);
BezierCurve[] bezierCurves = new BezierCurve[channels.size()]; BezierCurve[] bezierCurves = new BezierCurve[channels.size()];
int channelCounter = 0; int channelCounter = 0;
for(Structure c : channels) { for (Structure c : channels) {
//reading rna path first //reading rna path first
BlenderInputStream bis = dataRepository.getInputStream(); BlenderInputStream bis = dataRepository.getInputStream();
int currentPosition = bis.getPosition(); int currentPosition = bis.getPosition();
Pointer pRnaPath = (Pointer) c.getFieldValue("rna_path"); Pointer pRnaPath = (Pointer) c.getFieldValue("rna_path");
FileBlockHeader dataFileBlock = dataRepository.getFileBlock(pRnaPath.getOldMemoryAddress()); FileBlockHeader dataFileBlock = dataRepository.getFileBlock(pRnaPath.getOldMemoryAddress());
bis.setPosition(dataFileBlock.getBlockPosition()); bis.setPosition(dataFileBlock.getBlockPosition());
String rnaPath = bis.readString(); String rnaPath = bis.readString();
bis.setPosition(currentPosition); bis.setPosition(currentPosition);
int arrayIndex = ((Number)c.getFieldValue("array_index")).intValue(); int arrayIndex = ((Number) c.getFieldValue("array_index")).intValue();
int type = this.getCurveType(rnaPath, arrayIndex); int type = this.getCurveType(rnaPath, arrayIndex);
Pointer pBezTriple = (Pointer)c.getFieldValue("bezt"); Pointer pBezTriple = (Pointer) c.getFieldValue("bezt");
List<Structure> bezTriples = pBezTriple.fetchData(dataRepository.getInputStream()); List<Structure> bezTriples = pBezTriple.fetchData(dataRepository.getInputStream());
bezierCurves[channelCounter++] = new BezierCurve(type, bezTriples, 2); bezierCurves[channelCounter++] = new BezierCurve(type, bezTriples, 2);
} }
Ipo ipo = new Ipo(bezierCurves); Ipo ipo = new Ipo(bezierCurves);
tracks.add(ipo.calculateTrack(boneIndex.intValue(), animationFrames[0], animationFrames[1], fps)); tracks.add(ipo.calculateTrack(boneIndex.intValue(), animationFrames[0], animationFrames[1], fps));
} }
} }
return tracks.toArray(new BoneTrack[tracks.size()]); return tracks.toArray(new BoneTrack[tracks.size()]);
} }
/** /**
* This method parses the information stored inside the curve rna path and returns the proper type * This method parses the information stored inside the curve rna path and returns the proper type
* of the curve. * of the curve.
* @param rnaPath the curve's rna path * @param rnaPath the curve's rna path
* @param arrayIndex the array index of the stored data * @param arrayIndex the array index of the stored data
* @return the type of the curve * @return the type of the curve
*/ */
protected int getCurveType(String rnaPath, int arrayIndex) { protected int getCurveType(String rnaPath, int arrayIndex) {
if(rnaPath.endsWith(".location")) { if (rnaPath.endsWith(".location")) {
return Ipo.AC_LOC_X + arrayIndex; return Ipo.AC_LOC_X + arrayIndex;
} }
if(rnaPath.endsWith(".rotation_quaternion")) { if (rnaPath.endsWith(".rotation_quaternion")) {
return Ipo.AC_QUAT_W + arrayIndex; return Ipo.AC_QUAT_W + arrayIndex;
} }
if(rnaPath.endsWith(".scale")) { if (rnaPath.endsWith(".scale")) {
return Ipo.AC_SIZE_X + arrayIndex; return Ipo.AC_SIZE_X + arrayIndex;
} }
throw new IllegalStateException("Unknown curve rna path: " + rnaPath); throw new IllegalStateException("Unknown curve rna path: " + rnaPath);
} }
} }

@ -12,40 +12,41 @@ import com.jme3.scene.plugins.blender.exception.BlenderFileException;
* @author Marcin Roguski * @author Marcin Roguski
*/ */
public class CameraHelper extends com.jme3.scene.plugins.blender.helpers.v249.CameraHelper { public class CameraHelper extends com.jme3.scene.plugins.blender.helpers.v249.CameraHelper {
private static final Logger LOGGER = Logger.getLogger(CameraHelper.class.getName());
/** private static final Logger LOGGER = Logger.getLogger(CameraHelper.class.getName());
* This constructor parses the given blender version and stores the result. Some functionalities may differ in
* different blender versions.
* @param blenderVersion
* the version read from the blend file
*/
public CameraHelper(String blenderVersion) {
super(blenderVersion);
}
@Override /**
public Camera toCamera(Structure structure) throws BlenderFileException { * This constructor parses the given blender version and stores the result. Some functionalities may differ in
if(blenderVersion<250) { * different blender versions.
return super.toCamera(structure); * @param blenderVersion
} * the version read from the blend file
Camera result = new Camera(DEFAULT_CAM_WIDTH, DEFAULT_CAM_HEIGHT); */
int type = ((Number)structure.getFieldValue("type")).intValue(); public CameraHelper(String blenderVersion) {
if(type != 0 && type != 1) { super(blenderVersion);
LOGGER.log(Level.WARNING, "Unknown camera type: " + type + ". Perspective camera is being used!"); }
type = 0;
} @Override
//type==0 - perspective; type==1 - orthographic; perspective is used as default public Camera toCamera(Structure structure) throws BlenderFileException {
result.setParallelProjection(type == 1); if (blenderVersion < 250) {
float aspect = 0; return super.toCamera(structure);
float clipsta = ((Number)structure.getFieldValue("clipsta")).floatValue(); }
float clipend = ((Number)structure.getFieldValue("clipend")).floatValue(); Camera result = new Camera(DEFAULT_CAM_WIDTH, DEFAULT_CAM_HEIGHT);
if(type == 0) { int type = ((Number) structure.getFieldValue("type")).intValue();
aspect = ((Number)structure.getFieldValue("lens")).floatValue(); if (type != 0 && type != 1) {
} else { LOGGER.log(Level.WARNING, "Unknown camera type: {0}. Perspective camera is being used!", type);
aspect = ((Number)structure.getFieldValue("ortho_scale")).floatValue(); type = 0;
} }
result.setFrustumPerspective(45, aspect, clipsta, clipend); //type==0 - perspective; type==1 - orthographic; perspective is used as default
return result; result.setParallelProjection(type == 1);
} float aspect = 0;
float clipsta = ((Number) structure.getFieldValue("clipsta")).floatValue();
float clipend = ((Number) structure.getFieldValue("clipend")).floatValue();
if (type == 0) {
aspect = ((Number) structure.getFieldValue("lens")).floatValue();
} else {
aspect = ((Number) structure.getFieldValue("ortho_scale")).floatValue();
}
result.setFrustumPerspective(45, aspect, clipsta, clipend);
return result;
}
} }

@ -11,27 +11,28 @@ import com.jme3.scene.plugins.blender.utils.DataRepository;
* @author Marcin Roguski * @author Marcin Roguski
*/ */
public class ConstraintHelper extends com.jme3.scene.plugins.blender.helpers.v249.ConstraintHelper { public class ConstraintHelper extends com.jme3.scene.plugins.blender.helpers.v249.ConstraintHelper {
private static final Logger LOGGER = Logger.getLogger(ConstraintHelper.class.getName());
/** private static final Logger LOGGER = Logger.getLogger(ConstraintHelper.class.getName());
* Helper constructor. It's main task is to generate the affection functions. These functions are common to all
* ConstraintHelper instances. Unfortunately this constructor might grow large. If it becomes too large - I shall
* consider refactoring. The constructor parses the given blender version and stores the result. Some
* functionalities may differ in different blender versions.
* @param blenderVersion
* the version read from the blend file
*/
public ConstraintHelper(String blenderVersion, DataRepository dataRepository) {
super(blenderVersion, dataRepository);
}
@Override /**
public void loadConstraints(Structure objectStructure, DataRepository dataRepository) throws BlenderFileException { * Helper constructor. It's main task is to generate the affection functions. These functions are common to all
if(blenderVersion<250) { * ConstraintHelper instances. Unfortunately this constructor might grow large. If it becomes too large - I shall
super.loadConstraints(objectStructure, dataRepository); * consider refactoring. The constructor parses the given blender version and stores the result. Some
} else { * functionalities may differ in different blender versions.
LOGGER.warning("Loading of constraints not yet implemented for version 2.5x !"); * @param blenderVersion
//TODO: to implement * the version read from the blend file
} */
} public ConstraintHelper(String blenderVersion, DataRepository dataRepository) {
super(blenderVersion, dataRepository);
}
@Override
public void loadConstraints(Structure objectStructure, DataRepository dataRepository) throws BlenderFileException {
if (blenderVersion < 250) {
super.loadConstraints(objectStructure, dataRepository);
} else {
LOGGER.warning("Loading of constraints not yet implemented for version 2.5x !");
//TODO: to implement
}
}
} }

@ -5,13 +5,14 @@ package com.jme3.scene.plugins.blender.helpers;
* @author Marcin Roguski * @author Marcin Roguski
*/ */
public class CurvesHelper extends com.jme3.scene.plugins.blender.helpers.v249.CurvesHelper { public class CurvesHelper extends com.jme3.scene.plugins.blender.helpers.v249.CurvesHelper {
/**
* This constructor parses the given blender version and stores the result. Some functionalities may differ in /**
* different blender versions. * This constructor parses the given blender version and stores the result. Some functionalities may differ in
* @param blenderVersion * different blender versions.
* the version read from the blend file * @param blenderVersion
*/ * the version read from the blend file
public CurvesHelper(String blenderVersion) { */
super(blenderVersion); public CurvesHelper(String blenderVersion) {
} super(blenderVersion);
}
} }

@ -6,13 +6,14 @@ package com.jme3.scene.plugins.blender.helpers;
* @author Marcin Roguski * @author Marcin Roguski
*/ */
public class IpoHelper extends com.jme3.scene.plugins.blender.helpers.v249.IpoHelper { public class IpoHelper extends com.jme3.scene.plugins.blender.helpers.v249.IpoHelper {
/**
* This constructor parses the given blender version and stores the result. Some functionalities may differ in /**
* different blender versions. * This constructor parses the given blender version and stores the result. Some functionalities may differ in
* @param blenderVersion * different blender versions.
* the version read from the blend file * @param blenderVersion
*/ * the version read from the blend file
public IpoHelper(String blenderVersion) { */
super(blenderVersion); public IpoHelper(String blenderVersion) {
} super(blenderVersion);
}
} }

@ -36,13 +36,14 @@ package com.jme3.scene.plugins.blender.helpers;
* @author Marcin Roguski * @author Marcin Roguski
*/ */
public class LightHelper extends com.jme3.scene.plugins.blender.helpers.v249.LightHelper { public class LightHelper extends com.jme3.scene.plugins.blender.helpers.v249.LightHelper {
/**
* This constructor parses the given blender version and stores the result. Some functionalities may differ in /**
* different blender versions. * This constructor parses the given blender version and stores the result. Some functionalities may differ in
* @param blenderVersion * different blender versions.
* the version read from the blend file * @param blenderVersion
*/ * the version read from the blend file
public LightHelper(String blenderVersion) { */
super(blenderVersion); public LightHelper(String blenderVersion) {
} super(blenderVersion);
}
} }

@ -32,13 +32,14 @@
package com.jme3.scene.plugins.blender.helpers; package com.jme3.scene.plugins.blender.helpers;
public class MaterialHelper extends com.jme3.scene.plugins.blender.helpers.v249.MaterialHelper { public class MaterialHelper extends com.jme3.scene.plugins.blender.helpers.v249.MaterialHelper {
/**
* This constructor parses the given blender version and stores the result. Some functionalities may differ in /**
* different blender versions. * This constructor parses the given blender version and stores the result. Some functionalities may differ in
* @param blenderVersion * different blender versions.
* the version read from the blend file * @param blenderVersion
*/ * the version read from the blend file
public MaterialHelper(String blenderVersion) { */
super(blenderVersion); public MaterialHelper(String blenderVersion) {
} super(blenderVersion);
}
} }

@ -36,13 +36,14 @@ package com.jme3.scene.plugins.blender.helpers;
* @author Marcin Roguski * @author Marcin Roguski
*/ */
public class MeshHelper extends com.jme3.scene.plugins.blender.helpers.v249.MeshHelper { public class MeshHelper extends com.jme3.scene.plugins.blender.helpers.v249.MeshHelper {
/**
* This constructor parses the given blender version and stores the result. Some functionalities may differ in /**
* different blender versions. * This constructor parses the given blender version and stores the result. Some functionalities may differ in
* @param blenderVersion * different blender versions.
* the version read from the blend file * @param blenderVersion
*/ * the version read from the blend file
public MeshHelper(String blenderVersion) { */
super(blenderVersion); public MeshHelper(String blenderVersion) {
} super(blenderVersion);
}
} }

@ -36,13 +36,14 @@ package com.jme3.scene.plugins.blender.helpers;
* @author Marcin Roguski * @author Marcin Roguski
*/ */
public class ModifierHelper extends com.jme3.scene.plugins.blender.helpers.v249.ModifierHelper { public class ModifierHelper extends com.jme3.scene.plugins.blender.helpers.v249.ModifierHelper {
/**
* This constructor parses the given blender version and stores the result. Some functionalities may differ in /**
* different blender versions. * This constructor parses the given blender version and stores the result. Some functionalities may differ in
* @param blenderVersion * different blender versions.
* the version read from the blend file * @param blenderVersion
*/ * the version read from the blend file
public ModifierHelper(String blenderVersion) { */
super(blenderVersion); public ModifierHelper(String blenderVersion) {
} super(blenderVersion);
}
} }

@ -39,13 +39,14 @@ package com.jme3.scene.plugins.blender.helpers;
* @author Marcin Roguski (Kaelthas) * @author Marcin Roguski (Kaelthas)
*/ */
public class NoiseHelper extends com.jme3.scene.plugins.blender.helpers.v249.NoiseHelper { public class NoiseHelper extends com.jme3.scene.plugins.blender.helpers.v249.NoiseHelper {
/**
* Constructor. Stores the blender version number and loads the constants needed for computations. /**
* * Constructor. Stores the blender version number and loads the constants needed for computations.
* @param blenderVersion *
* the number of blender version * @param blenderVersion
*/ * the number of blender version
public NoiseHelper(String blenderVersion) { */
super(blenderVersion); public NoiseHelper(String blenderVersion) {
} super(blenderVersion);
}
} }

@ -36,13 +36,14 @@ package com.jme3.scene.plugins.blender.helpers;
* @author Marcin Roguski * @author Marcin Roguski
*/ */
public class ObjectHelper extends com.jme3.scene.plugins.blender.helpers.v249.ObjectHelper { public class ObjectHelper extends com.jme3.scene.plugins.blender.helpers.v249.ObjectHelper {
/**
* This constructor parses the given blender version and stores the result. Some functionalities may differ in /**
* different blender versions. * This constructor parses the given blender version and stores the result. Some functionalities may differ in
* @param blenderVersion * different blender versions.
* the version read from the blend file * @param blenderVersion
*/ * the version read from the blend file
public ObjectHelper(String blenderVersion) { */
super(blenderVersion); public ObjectHelper(String blenderVersion) {
} super(blenderVersion);
}
} }

@ -5,13 +5,14 @@ package com.jme3.scene.plugins.blender.helpers;
* @author Marcin Roguski (Kaelthas) * @author Marcin Roguski (Kaelthas)
*/ */
public class ParticlesHelper extends com.jme3.scene.plugins.blender.helpers.v249.ParticlesHelper { public class ParticlesHelper extends com.jme3.scene.plugins.blender.helpers.v249.ParticlesHelper {
/**
* This constructor parses the given blender version and stores the result. Some functionalities may differ in /**
* different blender versions. * This constructor parses the given blender version and stores the result. Some functionalities may differ in
* @param blenderVersion * different blender versions.
* the version read from the blend file * @param blenderVersion
*/ * the version read from the blend file
public ParticlesHelper(String blenderVersion) { */
super(blenderVersion); public ParticlesHelper(String blenderVersion) {
} super(blenderVersion);
}
} }

@ -44,39 +44,41 @@ import com.jme3.texture.Texture;
* @author Marcin Roguski * @author Marcin Roguski
*/ */
public class TextureHelper extends com.jme3.scene.plugins.blender.helpers.v249.TextureHelper { public class TextureHelper extends com.jme3.scene.plugins.blender.helpers.v249.TextureHelper {
private static final Logger LOGGER = Logger.getLogger(TextureHelper.class.getName());
public static final int TEX_POINTDENSITY = 14;
public static final int TEX_VOXELDATA = 15;
/**
* This constructor parses the given blender version and stores the result. Some functionalities may differ in
* different blender versions.
* @param blenderVersion
* the version read from the blend file
*/
public TextureHelper(String blenderVersion) {
super(blenderVersion);
}
@Override private static final Logger LOGGER = Logger.getLogger(TextureHelper.class.getName());
public Texture getTexture(Structure tex, DataRepository dataRepository) throws BlenderFileException { public static final int TEX_POINTDENSITY = 14;
if(blenderVersion<250) { public static final int TEX_VOXELDATA = 15;
return super.getTexture(tex, dataRepository);
} /**
Texture result = (Texture) dataRepository.getLoadedFeature(tex.getOldMemoryAddress(), LoadedFeatureDataType.LOADED_FEATURE); * This constructor parses the given blender version and stores the result. Some functionalities may differ in
if (result != null) { * different blender versions.
return result; * @param blenderVersion
} * the version read from the blend file
int type = ((Number)tex.getFieldValue("type")).intValue(); */
switch(type) { public TextureHelper(String blenderVersion) {
case TEX_POINTDENSITY: super(blenderVersion);
LOGGER.warning("Point density texture loading currently not supported!"); }
break;
case TEX_VOXELDATA: @Override
LOGGER.warning("Voxel data texture loading currently not supported!"); public Texture getTexture(Structure tex, DataRepository dataRepository) throws BlenderFileException {
break; if (blenderVersion < 250) {
default: return super.getTexture(tex, dataRepository);
result = super.getTexture(tex, dataRepository); }
} Texture result = (Texture) dataRepository.getLoadedFeature(tex.getOldMemoryAddress(), LoadedFeatureDataType.LOADED_FEATURE);
return result; if (result != null) {
} return result;
}
int type = ((Number) tex.getFieldValue("type")).intValue();
switch (type) {
case TEX_POINTDENSITY:
LOGGER.warning("Point density texture loading currently not supported!");
break;
case TEX_VOXELDATA:
LOGGER.warning("Voxel data texture loading currently not supported!");
break;
default:
result = super.getTexture(tex, dataRepository);
}
return result;
}
} }

@ -60,311 +60,312 @@ import com.jme3.scene.plugins.blender.utils.Pointer;
* @author Marcin Roguski * @author Marcin Roguski
*/ */
public class ArmatureHelper extends AbstractBlenderHelper { public class ArmatureHelper extends AbstractBlenderHelper {
private static final Logger LOGGER = Logger.getLogger(ArmatureHelper.class.getName());
/** private static final Logger LOGGER = Logger.getLogger(ArmatureHelper.class.getName());
* The map of the bones. Maps a bone name to its index in the armature. Should be cleared after the object had been /**
* read. TODO: probably bones can have identical names in different armatures * The map of the bones. Maps a bone name to its index in the armature. Should be cleared after the object had been
*/ * read. TODO: probably bones can have identical names in different armatures
protected Map<String, Integer> bonesMap = new HashMap<String, Integer>(); */
/** A map of bones and their old memory addresses. */ protected Map<String, Integer> bonesMap = new HashMap<String, Integer>();
protected Map<Bone, Long> bonesOMAs = new HashMap<Bone, Long>(); /** A map of bones and their old memory addresses. */
/** This list contains bones hierarchy and their matrices. It is later converted into jme bones. */ protected Map<Bone, Long> bonesOMAs = new HashMap<Bone, Long>();
protected List<BoneTransformationData> boneDataRoots = new ArrayList<BoneTransformationData>(); /** This list contains bones hierarchy and their matrices. It is later converted into jme bones. */
protected List<BoneTransformationData> boneDataRoots = new ArrayList<BoneTransformationData>();
/** /**
* This constructor parses the given blender version and stores the result. Some functionalities may differ in * This constructor parses the given blender version and stores the result. Some functionalities may differ in
* different blender versions. * different blender versions.
* @param blenderVersion * @param blenderVersion
* the version read from the blend file * the version read from the blend file
*/ */
public ArmatureHelper(String blenderVersion) { public ArmatureHelper(String blenderVersion) {
super(blenderVersion); super(blenderVersion);
} }
/** /**
* This method returns the old memory address of a bone. If the bone does not exist in the blend file - zero is * This method returns the old memory address of a bone. If the bone does not exist in the blend file - zero is
* returned. * returned.
* @param bone * @param bone
* the bone whose old memory address we seek * the bone whose old memory address we seek
* @return the old memory address of the given bone * @return the old memory address of the given bone
*/ */
public Long getBoneOMA(Bone bone) { public Long getBoneOMA(Bone bone) {
Long result = bonesOMAs.get(bone); Long result = bonesOMAs.get(bone);
if(result == null) { if (result == null) {
result = Long.valueOf(0); result = Long.valueOf(0);
} }
return result; return result;
} }
/** /**
* This method reads the bones and returns an empty skeleton. Bones should be assigned later. * This method reads the bones and returns an empty skeleton. Bones should be assigned later.
* @param structure * @param structure
* armature structure * armature structure
* @param dataRepository * @param dataRepository
* the data repository * the data repository
* @return an empty skeleton, bones are stored within the helper object * @return an empty skeleton, bones are stored within the helper object
* @throws BlenderFileException * @throws BlenderFileException
* this exception is thrown when the blender file is somehow corrupted * this exception is thrown when the blender file is somehow corrupted
*/ */
public Skeleton toArmature(Structure structure, DataRepository dataRepository) throws BlenderFileException { public Skeleton toArmature(Structure structure, DataRepository dataRepository) throws BlenderFileException {
LOGGER.log(Level.INFO, "Converting structure to Armature!"); LOGGER.log(Level.INFO, "Converting structure to Armature!");
Structure bonebase = (Structure)structure.getFieldValue("bonebase"); Structure bonebase = (Structure) structure.getFieldValue("bonebase");
List<Structure> bonesStructures = bonebase.evaluateListBase(dataRepository); List<Structure> bonesStructures = bonebase.evaluateListBase(dataRepository);
for(Structure boneStructure : bonesStructures) { for (Structure boneStructure : bonesStructures) {
BoneTransformationData rootBoneTransformationData = this.readBoneAndItsChildren(boneStructure, null, dataRepository); BoneTransformationData rootBoneTransformationData = this.readBoneAndItsChildren(boneStructure, null, dataRepository);
boneDataRoots.add(rootBoneTransformationData); boneDataRoots.add(rootBoneTransformationData);
} }
return new Skeleton();//bones are assigned later return new Skeleton();//bones are assigned later
} }
/** /**
* This method returns a map where the key is the object's group index that is used by a bone and the key is the * This method returns a map where the key is the object's group index that is used by a bone and the key is the
* bone index in the armature. * bone index in the armature.
* @param poseStructure * @param poseStructure
* a bPose structure of the object * a bPose structure of the object
* @return bone group-to-index map * @return bone group-to-index map
* @throws BlenderFileException * @throws BlenderFileException
* this exception is thrown when the blender file is somehow corrupted * this exception is thrown when the blender file is somehow corrupted
*/ */
public Map<Integer, Integer> getGroupToBoneIndexMap(Structure defBaseStructure, DataRepository dataRepository) throws BlenderFileException { public Map<Integer, Integer> getGroupToBoneIndexMap(Structure defBaseStructure, DataRepository dataRepository) throws BlenderFileException {
Map<Integer, Integer> result = null; Map<Integer, Integer> result = null;
if(bonesMap != null && bonesMap.size() != 0) { if (bonesMap != null && bonesMap.size() != 0) {
result = new HashMap<Integer, Integer>(); result = new HashMap<Integer, Integer>();
List<Structure> deformGroups = defBaseStructure.evaluateListBase(dataRepository);//bDeformGroup List<Structure> deformGroups = defBaseStructure.evaluateListBase(dataRepository);//bDeformGroup
int groupIndex = 0;//TODO: consider many armatures attached to one object in the future !!! int groupIndex = 0;//TODO: consider many armatures attached to one object in the future !!!
for(Structure deformGroup : deformGroups) { for (Structure deformGroup : deformGroups) {
String deformGroupName = deformGroup.getFieldValue("name").toString(); String deformGroupName = deformGroup.getFieldValue("name").toString();
Integer boneIndex = bonesMap.get(deformGroupName); Integer boneIndex = bonesMap.get(deformGroupName);
if(boneIndex != null) { if (boneIndex != null) {
result.put(Integer.valueOf(groupIndex), boneIndex); result.put(Integer.valueOf(groupIndex), boneIndex);
} }
++groupIndex; ++groupIndex;
} }
} }
return result; return result;
} }
/** /**
* This method reads the tracks of the armature object. * This method reads the tracks of the armature object.
* @param actionStructure * @param actionStructure
* @param dataRepository * @param dataRepository
* the data repository * the data repository
* @param objectName * @param objectName
* the name of animation owner * the name of animation owner
* @param animationName * @param animationName
* the name of the animation * the name of the animation
* @return a list of tracks for the armature * @return a list of tracks for the armature
* @throws BlenderFileException * @throws BlenderFileException
* this exception is thrown when the blender file is somehow corrupted * this exception is thrown when the blender file is somehow corrupted
*/ */
public BoneTrack[] getTracks(Structure actionStructure, DataRepository dataRepository, String objectName, String animationName) throws BlenderFileException { public BoneTrack[] getTracks(Structure actionStructure, DataRepository dataRepository, String objectName, String animationName) throws BlenderFileException {
LOGGER.log(Level.INFO, "Getting tracks!"); LOGGER.log(Level.INFO, "Getting tracks!");
IpoHelper ipoHelper = dataRepository.getHelper(IpoHelper.class); IpoHelper ipoHelper = dataRepository.getHelper(IpoHelper.class);
int fps = dataRepository.getBlenderKey().getFps(); int fps = dataRepository.getBlenderKey().getFps();
int[] animationFrames = dataRepository.getBlenderKey().getAnimationFrames(objectName, animationName); int[] animationFrames = dataRepository.getBlenderKey().getAnimationFrames(objectName, animationName);
Structure chanbase = (Structure)actionStructure.getFieldValue("chanbase"); Structure chanbase = (Structure) actionStructure.getFieldValue("chanbase");
List<Structure> actionChannels = chanbase.evaluateListBase(dataRepository);//bActionChannel List<Structure> actionChannels = chanbase.evaluateListBase(dataRepository);//bActionChannel
if(actionChannels != null && actionChannels.size() > 0 && (bonesMap == null || bonesMap.size() == 0)) { if (actionChannels != null && actionChannels.size() > 0 && (bonesMap == null || bonesMap.size() == 0)) {
throw new IllegalStateException("No bones found! Cannot proceed to calculating tracks!"); throw new IllegalStateException("No bones found! Cannot proceed to calculating tracks!");
} }
List<BoneTrack> tracks = new ArrayList<BoneTrack>(); List<BoneTrack> tracks = new ArrayList<BoneTrack>();
for(Structure bActionChannel : actionChannels) { for (Structure bActionChannel : actionChannels) {
String name = bActionChannel.getFieldValue("name").toString(); String name = bActionChannel.getFieldValue("name").toString();
Integer boneIndex = bonesMap.get(name); Integer boneIndex = bonesMap.get(name);
if(boneIndex != null) { if (boneIndex != null) {
Pointer p = (Pointer)bActionChannel.getFieldValue("ipo"); Pointer p = (Pointer) bActionChannel.getFieldValue("ipo");
if(!p.isNull()) { if (!p.isNull()) {
Structure ipoStructure = p.fetchData(dataRepository.getInputStream()).get(0); Structure ipoStructure = p.fetchData(dataRepository.getInputStream()).get(0);
Ipo ipo = ipoHelper.createIpo(ipoStructure, dataRepository); Ipo ipo = ipoHelper.createIpo(ipoStructure, dataRepository);
tracks.add(ipo.calculateTrack(boneIndex.intValue(), animationFrames[0], animationFrames[1], fps)); tracks.add(ipo.calculateTrack(boneIndex.intValue(), animationFrames[0], animationFrames[1], fps));
} }
} }
} }
return tracks.toArray(new BoneTrack[tracks.size()]); return tracks.toArray(new BoneTrack[tracks.size()]);
} }
/** /**
* This bone returns transformation matrix of the bone that is relative to * This bone returns transformation matrix of the bone that is relative to
* its armature object. * its armature object.
* @param boneStructure the bone's structure * @param boneStructure the bone's structure
* @return bone's transformation matrix in armature space * @return bone's transformation matrix in armature space
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
protected Matrix4f getArmatureMatrix(Structure boneStructure) { protected Matrix4f getArmatureMatrix(Structure boneStructure) {
DynamicArray<Number> boneMat = (DynamicArray<Number>)boneStructure.getFieldValue("arm_mat"); DynamicArray<Number> boneMat = (DynamicArray<Number>) boneStructure.getFieldValue("arm_mat");
Matrix4f m = new Matrix4f(); Matrix4f m = new Matrix4f();
for(int i = 0; i < 4; ++i) { for (int i = 0; i < 4; ++i) {
for(int j = 0; j < 4; ++j) { for (int j = 0; j < 4; ++j) {
m.set(i, j, boneMat.get(j, i).floatValue()); m.set(i, j, boneMat.get(j, i).floatValue());
} }
} }
return m; return m;
} }
/** /**
* This method reads the bone with its children. * This method reads the bone with its children.
* @param boneStructure * @param boneStructure
* a structure containing the bone data * a structure containing the bone data
* @param parent * @param parent
* the bone parent; if null then we read the root bone * the bone parent; if null then we read the root bone
* @param dataRepository * @param dataRepository
* the data repository * the data repository
* @return the bone transformation data; contains bone chierarchy and the bone's matrices * @return the bone transformation data; contains bone chierarchy and the bone's matrices
* @throws BlenderFileException * @throws BlenderFileException
* this exception is thrown when the blender file is somehow corrupted * this exception is thrown when the blender file is somehow corrupted
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
protected BoneTransformationData readBoneAndItsChildren(Structure boneStructure, BoneTransformationData parent, DataRepository dataRepository) throws BlenderFileException { protected BoneTransformationData readBoneAndItsChildren(Structure boneStructure, BoneTransformationData parent, DataRepository dataRepository) throws BlenderFileException {
String name = boneStructure.getFieldValue("name").toString(); String name = boneStructure.getFieldValue("name").toString();
Bone bone = new Bone(name); Bone bone = new Bone(name);
int bonesAmount = bonesOMAs.size(); int bonesAmount = bonesOMAs.size();
bonesOMAs.put(bone, boneStructure.getOldMemoryAddress()); bonesOMAs.put(bone, boneStructure.getOldMemoryAddress());
if(bonesAmount == bonesOMAs.size()) { if (bonesAmount == bonesOMAs.size()) {
throw new IllegalStateException("Two bones has the same hash value and thereforw a bone was overriden in the bones<->OMA map! Improve the hash algorithm!"); throw new IllegalStateException("Two bones has the same hash value and thereforw a bone was overriden in the bones<->OMA map! Improve the hash algorithm!");
} }
Matrix4f boneArmatureMatrix = this.getArmatureMatrix(boneStructure); Matrix4f boneArmatureMatrix = this.getArmatureMatrix(boneStructure);
DynamicArray<Float> sizeArray = (DynamicArray<Float>) boneStructure.getFieldValue("size"); DynamicArray<Float> sizeArray = (DynamicArray<Float>) boneStructure.getFieldValue("size");
Vector3f size = new Vector3f(sizeArray.get(0), sizeArray.get(1), sizeArray.get(2)); Vector3f size = new Vector3f(sizeArray.get(0), sizeArray.get(1), sizeArray.get(2));
BoneTransformationData boneTransformationData = new BoneTransformationData(boneArmatureMatrix, size, bone, parent); BoneTransformationData boneTransformationData = new BoneTransformationData(boneArmatureMatrix, size, bone, parent);
dataRepository.addLoadedFeatures(boneStructure.getOldMemoryAddress(), name, boneStructure, bone); dataRepository.addLoadedFeatures(boneStructure.getOldMemoryAddress(), name, boneStructure, bone);
Structure childbase = (Structure)boneStructure.getFieldValue("childbase"); Structure childbase = (Structure) boneStructure.getFieldValue("childbase");
List<Structure> children = childbase.evaluateListBase(dataRepository);//Bone List<Structure> children = childbase.evaluateListBase(dataRepository);//Bone
for(Structure boneChild : children) { for (Structure boneChild : children) {
this.readBoneAndItsChildren(boneChild, boneTransformationData, dataRepository); this.readBoneAndItsChildren(boneChild, boneTransformationData, dataRepository);
} }
return boneTransformationData; return boneTransformationData;
} }
/** /**
* This method assigns transformations to the bone. * This method assigns transformations to the bone.
* @param btd * @param btd
* the bone data containing the bone we assign transformation to * the bone data containing the bone we assign transformation to
* @param additionalRootBoneTransformation * @param additionalRootBoneTransformation
* additional bone transformation which indicates it's mesh parent and armature object transformations * additional bone transformation which indicates it's mesh parent and armature object transformations
* @param boneList * @param boneList
* a list of all read bones * a list of all read bones
*/ */
protected void assignBonesMatrices(BoneTransformationData btd, Matrix4f additionalRootBoneTransformation, List<Bone> boneList) { protected void assignBonesMatrices(BoneTransformationData btd, Matrix4f additionalRootBoneTransformation, List<Bone> boneList) {
LOGGER.info("[" + btd.bone.getName() + "] additionalRootBoneTransformation =\n" + additionalRootBoneTransformation); LOGGER.info("[" + btd.bone.getName() + "] additionalRootBoneTransformation =\n" + additionalRootBoneTransformation);
Matrix4f totalInverseParentMatrix = btd.parent != null ? btd.parent.totalInverseBoneParentMatrix : Matrix4f.IDENTITY; Matrix4f totalInverseParentMatrix = btd.parent != null ? btd.parent.totalInverseBoneParentMatrix : Matrix4f.IDENTITY;
LOGGER.info("[" + btd.bone.getName() + "] totalInverseParentMatrix =\n" + totalInverseParentMatrix); LOGGER.info("[" + btd.bone.getName() + "] totalInverseParentMatrix =\n" + totalInverseParentMatrix);
Matrix4f restMatrix = additionalRootBoneTransformation.mult(btd.boneArmatureMatrix); Matrix4f restMatrix = additionalRootBoneTransformation.mult(btd.boneArmatureMatrix);
LOGGER.info("[" + btd.bone.getName() + "] restMatrix =\n" + restMatrix); LOGGER.info("[" + btd.bone.getName() + "] restMatrix =\n" + restMatrix);
btd.totalInverseBoneParentMatrix = restMatrix.clone().invert(); btd.totalInverseBoneParentMatrix = restMatrix.clone().invert();
restMatrix = totalInverseParentMatrix.mult(restMatrix); restMatrix = totalInverseParentMatrix.mult(restMatrix);
LOGGER.info("[" + btd.bone.getName() + "] resultMatrix =\n" + restMatrix); LOGGER.info("[" + btd.bone.getName() + "] resultMatrix =\n" + restMatrix);
btd.bone.setBindTransforms(restMatrix.toTranslationVector(), restMatrix.toRotationQuat(), btd.size); btd.bone.setBindTransforms(restMatrix.toTranslationVector(), restMatrix.toRotationQuat(), btd.size);
boneList.add(btd.bone); boneList.add(btd.bone);
bonesMap.put(btd.bone.getName(), Integer.valueOf(boneList.size() - 1)); bonesMap.put(btd.bone.getName(), Integer.valueOf(boneList.size() - 1));
if(btd.children != null && btd.children.size() > 0) { if (btd.children != null && btd.children.size() > 0) {
for(BoneTransformationData child : btd.children) { for (BoneTransformationData child : btd.children) {
this.assignBonesMatrices(child, additionalRootBoneTransformation, boneList); this.assignBonesMatrices(child, additionalRootBoneTransformation, boneList);
btd.bone.addChild(child.bone); btd.bone.addChild(child.bone);
} }
} }
} }
/** /**
* This method returns bone transformation data for the bone of a given name. * This method returns bone transformation data for the bone of a given name.
* @param boneName * @param boneName
* the name of the bone * the name of the bone
* @return bone's transformation data * @return bone's transformation data
*/ */
public BoneTransformationData getBoneTransformationDataRoot(int index) { public BoneTransformationData getBoneTransformationDataRoot(int index) {
return boneDataRoots.get(index); return boneDataRoots.get(index);
} }
/** /**
* This method returns the amount of bones transformations roots. * This method returns the amount of bones transformations roots.
* @return the amount of bones transformations roots * @return the amount of bones transformations roots
*/ */
public int getBoneTransformationDataRootsSize() { public int getBoneTransformationDataRootsSize() {
return boneDataRoots.size(); return boneDataRoots.size();
} }
/** /**
* This class holds the data needed later for bone transformation calculation and to bind parent with children. * This class holds the data needed later for bone transformation calculation and to bind parent with children.
* @author Marcin Roguski * @author Marcin Roguski
*/ */
private static class BoneTransformationData { private static class BoneTransformationData {
/** Inverse matrix of bone's parent bone. */
private Matrix4f totalInverseBoneParentMatrix;
/** Bone's matrix in armature's space. */
private Matrix4f boneArmatureMatrix;
/** Bone's size (apparently it is held outside the transformation matrix. */
private Vector3f size;
/** The bone the data applies to. */
private Bone bone;
/** The parent of the above mentioned bone (not assigned yet). */
private BoneTransformationData parent;
/** The children of the current bone. */
private List<BoneTransformationData> children;
/** /** Inverse matrix of bone's parent bone. */
* Private constructor creates the object and assigns the given data. private Matrix4f totalInverseBoneParentMatrix;
* @param boneArmatureMatrix /** Bone's matrix in armature's space. */
* the matrix of the current bone private Matrix4f boneArmatureMatrix;
* @param size /** Bone's size (apparently it is held outside the transformation matrix. */
* the bone's size private Vector3f size;
* @param bone /** The bone the data applies to. */
* the current bone private Bone bone;
* @param parent /** The parent of the above mentioned bone (not assigned yet). */
* the parent structure of the bone private BoneTransformationData parent;
*/ /** The children of the current bone. */
private BoneTransformationData(Matrix4f boneArmatureMatrix, Vector3f size, Bone bone, BoneTransformationData parent) { private List<BoneTransformationData> children;
this.boneArmatureMatrix = boneArmatureMatrix;
this.size = size;
this.bone = bone;
this.parent = parent;
this.children = new ArrayList<ArmatureHelper.BoneTransformationData>();
if(this.parent != null) {
this.parent.children.add(this);
}
}
}
/** /**
* This method creates the whole bones structure. Assignes transformations to bones and combines children with * Private constructor creates the object and assigns the given data.
* parents. * @param boneArmatureMatrix
* @param armatureOMA * the matrix of the current bone
* old memory address of bones' armature object * @param size
* @param additionalRootBoneTransformation * the bone's size
* additional bone transformation which indicates it's mesh parent and armature object transformations * @param bone
* @return * the current bone
*/ * @param parent
public Bone[] buildBonesStructure(Long armatureOMA, Matrix4f additionalRootBoneTransformation) {//TODO: uwzględnić wiele szkieletów * the parent structure of the bone
List<Bone> bones = new ArrayList<Bone>(boneDataRoots.size() + 1); */
bones.add(new Bone(null)); private BoneTransformationData(Matrix4f boneArmatureMatrix, Vector3f size, Bone bone, BoneTransformationData parent) {
for(BoneTransformationData btd : boneDataRoots) { this.boneArmatureMatrix = boneArmatureMatrix;
this.assignBonesMatrices(btd, additionalRootBoneTransformation, bones); this.size = size;
} this.bone = bone;
return bones.toArray(new Bone[bones.size()]); this.parent = parent;
} this.children = new ArrayList<ArmatureHelper.BoneTransformationData>();
if (this.parent != null) {
this.parent.children.add(this);
}
}
}
/** /**
* This method assigns an immovable bone to vertices that have no bone assigned. They have the bone index with the * This method creates the whole bones structure. Assignes transformations to bones and combines children with
* value -1. * parents.
* @param immovableBoneIndex * @param armatureOMA
* the ondex of immovable bone * old memory address of bones' armature object
* @param meshes * @param additionalRootBoneTransformation
* a list of meshes whose vertices will be assigned to immovable bone * additional bone transformation which indicates it's mesh parent and armature object transformations
*/ * @return
public void assignBoneToOrphanedVertices(byte immovableBoneIndex, Mesh[] meshes) { */
//bone indices are common for all the object's meshes (vertex indices specify which are to be used) public Bone[] buildBonesStructure(Long armatureOMA, Matrix4f additionalRootBoneTransformation) {//TODO: uwzględnić wiele szkieletów
VertexBuffer boneIndices = meshes[0].getBuffer(Type.BoneIndex);//common buffer to all the meshes List<Bone> bones = new ArrayList<Bone>(boneDataRoots.size() + 1);
ByteBuffer data = (ByteBuffer)boneIndices.getData(); bones.add(new Bone(null));
for(int i = 0; i < boneIndices.getNumElements(); ++i) { for (BoneTransformationData btd : boneDataRoots) {
if(data.get(i) == -1) { this.assignBonesMatrices(btd, additionalRootBoneTransformation, bones);
data.put(i, immovableBoneIndex); }
} return bones.toArray(new Bone[bones.size()]);
} }
}
@Override /**
public void clearState() { * This method assigns an immovable bone to vertices that have no bone assigned. They have the bone index with the
bonesMap.clear(); * value -1.
boneDataRoots.clear(); * @param immovableBoneIndex
} * the ondex of immovable bone
* @param meshes
* a list of meshes whose vertices will be assigned to immovable bone
*/
public void assignBoneToOrphanedVertices(byte immovableBoneIndex, Mesh[] meshes) {
//bone indices are common for all the object's meshes (vertex indices specify which are to be used)
VertexBuffer boneIndices = meshes[0].getBuffer(Type.BoneIndex);//common buffer to all the meshes
ByteBuffer data = (ByteBuffer) boneIndices.getData();
for (int i = 0; i < boneIndices.getNumElements(); ++i) {
if (data.get(i) == -1) {
data.put(i, immovableBoneIndex);
}
}
}
@Override
public void clearState() {
bonesMap.clear();
boneDataRoots.clear();
}
} }

@ -13,45 +13,46 @@ import com.jme3.scene.plugins.blender.utils.AbstractBlenderHelper;
* @author Marcin Roguski * @author Marcin Roguski
*/ */
public class CameraHelper extends AbstractBlenderHelper { public class CameraHelper extends AbstractBlenderHelper {
private static final Logger LOGGER = Logger.getLogger(CameraHelper.class.getName());
protected static final int DEFAULT_CAM_WIDTH = 100; private static final Logger LOGGER = Logger.getLogger(CameraHelper.class.getName());
protected static final int DEFAULT_CAM_HEIGHT = 100; protected static final int DEFAULT_CAM_WIDTH = 100;
/** protected static final int DEFAULT_CAM_HEIGHT = 100;
* This constructor parses the given blender version and stores the result. Some functionalities may differ in
* different blender versions.
* @param blenderVersion
* the version read from the blend file
*/
public CameraHelper(String blenderVersion) {
super(blenderVersion);
}
/** /**
* This method reads the camera object. * This constructor parses the given blender version and stores the result. Some functionalities may differ in
* @param structure the structure containing the camera data * different blender versions.
* @return the camera object * @param blenderVersion
* @throws BlenderFileException * the version read from the blend file
*/ */
public Camera toCamera(Structure structure) throws BlenderFileException { public CameraHelper(String blenderVersion) {
Camera result = new Camera(DEFAULT_CAM_WIDTH, DEFAULT_CAM_HEIGHT); super(blenderVersion);
int type = ((Number)structure.getFieldValue("type")).intValue(); }
if(type != 0 && type != 1) {
LOGGER.log(Level.WARNING, "Unknown camera type: " + type + ". Perspective camera is being used!"); /**
type = 0; * This method reads the camera object.
} * @param structure the structure containing the camera data
//type==0 - perspective; type==1 - orthographic; perspective is used as default * @return the camera object
result.setParallelProjection(type == 1); * @throws BlenderFileException
float angle = ((Number)structure.getFieldValue("angle")).floatValue(); */
float aspect = 0; public Camera toCamera(Structure structure) throws BlenderFileException {
float clipsta = ((Number)structure.getFieldValue("clipsta")).floatValue(); Camera result = new Camera(DEFAULT_CAM_WIDTH, DEFAULT_CAM_HEIGHT);
float clipend = ((Number)structure.getFieldValue("clipend")).floatValue(); int type = ((Number) structure.getFieldValue("type")).intValue();
if(type == 0) { if (type != 0 && type != 1) {
aspect = ((Number)structure.getFieldValue("lens")).floatValue(); LOGGER.log(Level.WARNING, "Unknown camera type: {0}. Perspective camera is being used!", type);
} else { type = 0;
aspect = ((Number)structure.getFieldValue("ortho_scale")).floatValue(); }
} //type==0 - perspective; type==1 - orthographic; perspective is used as default
result.setFrustumPerspective(angle, aspect, clipsta, clipend); result.setParallelProjection(type == 1);
return result; float angle = ((Number) structure.getFieldValue("angle")).floatValue();
} float aspect = 0;
float clipsta = ((Number) structure.getFieldValue("clipsta")).floatValue();
float clipend = ((Number) structure.getFieldValue("clipend")).floatValue();
if (type == 0) {
aspect = ((Number) structure.getFieldValue("lens")).floatValue();
} else {
aspect = ((Number) structure.getFieldValue("ortho_scale")).floatValue();
}
result.setFrustumPerspective(angle, aspect, clipsta, clipend);
return result;
}
} }

@ -17,98 +17,98 @@ import com.jme3.scene.plugins.blender.utils.Pointer;
* @author Marcin Roguski * @author Marcin Roguski
*/ */
public class IpoHelper extends AbstractBlenderHelper { public class IpoHelper extends AbstractBlenderHelper {
/**
* This constructor parses the given blender version and stores the result. Some functionalities may differ in
* different blender versions.
* @param blenderVersion
* the version read from the blend file
*/
public IpoHelper(String blenderVersion) {
super(blenderVersion);
}
/** /**
* This method creates an ipo object used for interpolation calculations. * This constructor parses the given blender version and stores the result. Some functionalities may differ in
* @param ipoStructure * different blender versions.
* the structure with ipo definition * @param blenderVersion
* @param dataRepository * the version read from the blend file
* the data repository */
* @return the ipo object public IpoHelper(String blenderVersion) {
* @throws BlenderFileException super(blenderVersion);
* this exception is thrown when the blender file is somehow corrupted }
*/
public Ipo createIpo(Structure ipoStructure, DataRepository dataRepository) throws BlenderFileException {
Structure curvebase = (Structure)ipoStructure.getFieldValue("curve");
//preparing bezier curves /**
Ipo result = null; * This method creates an ipo object used for interpolation calculations.
List<Structure> curves = curvebase.evaluateListBase(dataRepository);//IpoCurve * @param ipoStructure
if(curves.size() > 0) { * the structure with ipo definition
BezierCurve[] bezierCurves = new BezierCurve[curves.size()]; * @param dataRepository
int frame = 0; * the data repository
for(Structure curve : curves) { * @return the ipo object
Pointer pBezTriple = (Pointer)curve.getFieldValue("bezt"); * @throws BlenderFileException
List<Structure> bezTriples = pBezTriple.fetchData(dataRepository.getInputStream()); * this exception is thrown when the blender file is somehow corrupted
int type = ((Number)curve.getFieldValue("adrcode")).intValue(); */
bezierCurves[frame++] = new BezierCurve(type, bezTriples, 2); public Ipo createIpo(Structure ipoStructure, DataRepository dataRepository) throws BlenderFileException {
} Structure curvebase = (Structure) ipoStructure.getFieldValue("curve");
curves.clear();
result = new Ipo(bezierCurves);
dataRepository.addLoadedFeatures(ipoStructure.getOldMemoryAddress(), ipoStructure.getName(), ipoStructure, result);
}
return result;
}
/** //preparing bezier curves
* This method creates an ipo with only a single value. No track type is specified so do not use it for calculating Ipo result = null;
* tracks. List<Structure> curves = curvebase.evaluateListBase(dataRepository);//IpoCurve
* @param constValue if (curves.size() > 0) {
* the value of this ipo BezierCurve[] bezierCurves = new BezierCurve[curves.size()];
* @return constant ipo int frame = 0;
*/ for (Structure curve : curves) {
public Ipo createIpo(float constValue) { Pointer pBezTriple = (Pointer) curve.getFieldValue("bezt");
return new ConstIpo(constValue); List<Structure> bezTriples = pBezTriple.fetchData(dataRepository.getInputStream());
} int type = ((Number) curve.getFieldValue("adrcode")).intValue();
bezierCurves[frame++] = new BezierCurve(type, bezTriples, 2);
}
curves.clear();
result = new Ipo(bezierCurves);
dataRepository.addLoadedFeatures(ipoStructure.getOldMemoryAddress(), ipoStructure.getName(), ipoStructure, result);
}
return result;
}
/**
* This method creates an ipo with only a single value. No track type is specified so do not use it for calculating
* tracks.
* @param constValue
* the value of this ipo
* @return constant ipo
*/
public Ipo createIpo(float constValue) {
return new ConstIpo(constValue);
}
/**
* Ipo constant curve. This is a curve with only one value and no specified type. This type of ipo cannot be used to
* calculate tracks. It should only be used to calculate single value for a given frame.
* @author Marcin Roguski
*/
private class ConstIpo extends Ipo {
/** /** The constant value of this ipo. */
* Ipo constant curve. This is a curve with only one value and no specified type. This type of ipo cannot be used to private float constValue;
* calculate tracks. It should only be used to calculate single value for a given frame.
* @author Marcin Roguski
*/
private class ConstIpo extends Ipo {
/** The constant value of this ipo. */
private float constValue;
/** /**
* Constructor. Stores the constant value of this ipo. * Constructor. Stores the constant value of this ipo.
* @param constValue * @param constValue
* the constant value of this ipo * the constant value of this ipo
*/ */
public ConstIpo(float constValue) { public ConstIpo(float constValue) {
super(null); super(null);
this.constValue = constValue; this.constValue = constValue;
} }
@Override @Override
public float calculateValue(int frame) { public float calculateValue(int frame) {
return constValue; return constValue;
} }
@Override @Override
public float calculateValue(int frame, int curveIndex) { public float calculateValue(int frame, int curveIndex) {
return constValue; return constValue;
} }
@Override @Override
public int getCurvesAmount() { public int getCurvesAmount() {
return 0; return 0;
} }
@Override @Override
public BoneTrack calculateTrack(int boneIndex, int startFrame, int stopFrame, int fps) { public BoneTrack calculateTrack(int boneIndex, int startFrame, int stopFrame, int fps) {
throw new IllegalStateException("Constatnt ipo object cannot be used for calculating bone tracks!"); throw new IllegalStateException("Constatnt ipo object cannot be used for calculating bone tracks!");
} }
} }
} }

@ -49,51 +49,52 @@ import com.jme3.scene.plugins.blender.utils.DataRepository.LoadedFeatureDataType
* @author Marcin Roguski * @author Marcin Roguski
*/ */
public class LightHelper extends AbstractBlenderHelper { public class LightHelper extends AbstractBlenderHelper {
private static final Logger LOGGER = Logger.getLogger(LightHelper.class.getName());
/** private static final Logger LOGGER = Logger.getLogger(LightHelper.class.getName());
* This constructor parses the given blender version and stores the result. Some functionalities may differ in
* different blender versions.
* @param blenderVersion
* the version read from the blend file
*/
public LightHelper(String blenderVersion) {
super(blenderVersion);
}
public Light toLight(Structure structure, DataRepository dataRepository) throws BlenderFileException { /**
Light result = (Light)dataRepository.getLoadedFeature(structure.getOldMemoryAddress(), LoadedFeatureDataType.LOADED_FEATURE); * This constructor parses the given blender version and stores the result. Some functionalities may differ in
if(result != null) { * different blender versions.
return result; * @param blenderVersion
} * the version read from the blend file
int type = ((Number)structure.getFieldValue("type")).intValue(); */
switch(type) { public LightHelper(String blenderVersion) {
case 0://Lamp super(blenderVersion);
result = new PointLight(); }
float distance = ((Number)structure.getFieldValue("dist")).floatValue();
((PointLight)result).setRadius(distance); public Light toLight(Structure structure, DataRepository dataRepository) throws BlenderFileException {
break; Light result = (Light) dataRepository.getLoadedFeature(structure.getOldMemoryAddress(), LoadedFeatureDataType.LOADED_FEATURE);
case 1://Sun if (result != null) {
LOGGER.log(Level.WARNING, "'Sun' lamp is not supported in jMonkeyEngine."); return result;
break; }
case 2://Spot int type = ((Number) structure.getFieldValue("type")).intValue();
LOGGER.log(Level.WARNING, "'Spot' lamp is not supported in jMonkeyEngine."); switch (type) {
break; case 0://Lamp
case 3://Hemi result = new PointLight();
LOGGER.log(Level.WARNING, "'Hemi' lamp is not supported in jMonkeyEngine."); float distance = ((Number) structure.getFieldValue("dist")).floatValue();
break; ((PointLight) result).setRadius(distance);
case 4://Area break;
result = new DirectionalLight(); case 1://Sun
break; LOGGER.log(Level.WARNING, "'Sun' lamp is not supported in jMonkeyEngine.");
default: break;
throw new BlenderFileException("Unknown light source type: " + type); case 2://Spot
} LOGGER.log(Level.WARNING, "'Spot' lamp is not supported in jMonkeyEngine.");
if(result != null) { break;
float r = ((Number)structure.getFieldValue("r")).floatValue(); case 3://Hemi
float g = ((Number)structure.getFieldValue("g")).floatValue(); LOGGER.log(Level.WARNING, "'Hemi' lamp is not supported in jMonkeyEngine.");
float b = ((Number)structure.getFieldValue("b")).floatValue(); break;
result.setColor(new ColorRGBA(r, g, b, 0.0f));//TODO: 0 czy 1 ??? case 4://Area
} result = new DirectionalLight();
return result; break;
} default:
throw new BlenderFileException("Unknown light source type: " + type);
}
if (result != null) {
float r = ((Number) structure.getFieldValue("r")).floatValue();
float g = ((Number) structure.getFieldValue("g")).floatValue();
float b = ((Number) structure.getFieldValue("b")).floatValue();
result.setColor(new ColorRGBA(r, g, b, 0.0f));//TODO: 0 czy 1 ???
}
return result;
}
} }

@ -77,465 +77,466 @@ import com.jme3.scene.plugins.ogre.AnimData;
* @author Marcin Roguski * @author Marcin Roguski
*/ */
public class ModifierHelper extends AbstractBlenderHelper { public class ModifierHelper extends AbstractBlenderHelper {
private static final Logger LOGGER = Logger.getLogger(ModifierHelper.class.getName());
private static final Logger LOGGER = Logger.getLogger(ModifierHelper.class.getName());
/**
* This constructor parses the given blender version and stores the result. Some functionalities may differ in /**
* different blender versions. * This constructor parses the given blender version and stores the result. Some functionalities may differ in
* @param blenderVersion * different blender versions.
* the version read from the blend file * @param blenderVersion
*/ * the version read from the blend file
public ModifierHelper(String blenderVersion) { */
super(blenderVersion); public ModifierHelper(String blenderVersion) {
} super(blenderVersion);
}
/**
* This method applies modifier to the object. /**
* @param node * This method applies modifier to the object.
* the loaded object * @param node
* @param modifier * the loaded object
* the modifier to apply * @param modifier
* @param dataRepository * the modifier to apply
* the data repository * @param dataRepository
* @return the node to whom the modifier was applied * the data repository
*/ * @return the node to whom the modifier was applied
public Node applyModifier(Node node, Modifier modifier, DataRepository dataRepository) { */
if(Modifier.ARMATURE_MODIFIER_DATA.equals(modifier.getType())) { public Node applyModifier(Node node, Modifier modifier, DataRepository dataRepository) {
return this.applyArmatureModifierData(node, modifier, dataRepository); if (Modifier.ARMATURE_MODIFIER_DATA.equals(modifier.getType())) {
} else if(Modifier.ARRAY_MODIFIER_DATA.equals(modifier.getType())) { return this.applyArmatureModifierData(node, modifier, dataRepository);
return this.applyArrayModifierData(node, modifier, dataRepository); } else if (Modifier.ARRAY_MODIFIER_DATA.equals(modifier.getType())) {
} else if(Modifier.PARTICLE_MODIFIER_DATA.equals(modifier.getType())) { return this.applyArrayModifierData(node, modifier, dataRepository);
return this.applyParticleSystemModifierData(node, modifier, dataRepository); } else if (Modifier.PARTICLE_MODIFIER_DATA.equals(modifier.getType())) {
} else { return this.applyParticleSystemModifierData(node, modifier, dataRepository);
LOGGER.warning("Modifier: " + modifier.getType() + " not yet implemented!!!"); } else {
return node; LOGGER.warning("Modifier: " + modifier.getType() + " not yet implemented!!!");
} return node;
} }
}
/**
* This method reads the given object's modifiers. /**
* @param objectStructure * This method reads the given object's modifiers.
* the object structure * @param objectStructure
* @param dataRepository * the object structure
* the data repository * @param dataRepository
* @param converter * the data repository
* the converter object (in some cases we need to read an object first before loading the modifier) * @param converter
* @throws BlenderFileException * the converter object (in some cases we need to read an object first before loading the modifier)
* this exception is thrown when the blender file is somehow corrupted * @throws BlenderFileException
*/ * this exception is thrown when the blender file is somehow corrupted
@SuppressWarnings("unchecked") */
public void readModifiers(Structure objectStructure, DataRepository dataRepository) throws BlenderFileException { @SuppressWarnings("unchecked")
Structure modifiersListBase = (Structure)objectStructure.getFieldValue("modifiers"); public void readModifiers(Structure objectStructure, DataRepository dataRepository) throws BlenderFileException {
List<Structure> modifiers = modifiersListBase.evaluateListBase(dataRepository); Structure modifiersListBase = (Structure) objectStructure.getFieldValue("modifiers");
for(Structure modifier : modifiers) { List<Structure> modifiers = modifiersListBase.evaluateListBase(dataRepository);
Object loadedModifier = null; for (Structure modifier : modifiers) {
Object modifierAdditionalData = null; Object loadedModifier = null;
if(Modifier.ARRAY_MODIFIER_DATA.equals(modifier.getType())) {//****************ARRAY MODIFIER Object modifierAdditionalData = null;
Map<String, Object> params = new HashMap<String, Object>(); if (Modifier.ARRAY_MODIFIER_DATA.equals(modifier.getType())) {//****************ARRAY MODIFIER
Map<String, Object> params = new HashMap<String, Object>();
Number fittype = (Number) modifier.getFieldValue("fit_type");
params.put("fittype", fittype); Number fittype = (Number) modifier.getFieldValue("fit_type");
switch(fittype.intValue()) { params.put("fittype", fittype);
case 0://FIXED COUNT switch (fittype.intValue()) {
params.put("count", modifier.getFieldValue("count")); case 0://FIXED COUNT
break; params.put("count", modifier.getFieldValue("count"));
case 1://FIXED LENGTH break;
params.put("length", modifier.getFieldValue("length")); case 1://FIXED LENGTH
break; params.put("length", modifier.getFieldValue("length"));
case 2://FITCURVE break;
//TODO: implement after loading curves is added; warning will be generated during modifier applying case 2://FITCURVE
break; //TODO: implement after loading curves is added; warning will be generated during modifier applying
default: break;
assert false : "Unknown array modifier fit type: " + fittype; default:
} assert false : "Unknown array modifier fit type: " + fittype;
}
//offset parameters
int offsettype = ((Number) modifier.getFieldValue("offset_type")).intValue(); //offset parameters
if((offsettype & 0x01) != 0) {//Constant offset int offsettype = ((Number) modifier.getFieldValue("offset_type")).intValue();
DynamicArray<Number> offsetArray = (DynamicArray<Number>)modifier.getFieldValue("offset"); if ((offsettype & 0x01) != 0) {//Constant offset
float[] offset = new float[] {offsetArray.get(0).floatValue(), offsetArray.get(1).floatValue(), offsetArray.get(2).floatValue()}; DynamicArray<Number> offsetArray = (DynamicArray<Number>) modifier.getFieldValue("offset");
params.put("offset", offset); float[] offset = new float[]{offsetArray.get(0).floatValue(), offsetArray.get(1).floatValue(), offsetArray.get(2).floatValue()};
} params.put("offset", offset);
if((offsettype & 0x02) != 0) {//Relative offset }
DynamicArray<Number> scaleArray = (DynamicArray<Number>)modifier.getFieldValue("scale"); if ((offsettype & 0x02) != 0) {//Relative offset
float[] scale = new float[] {scaleArray.get(0).floatValue(), scaleArray.get(1).floatValue(), scaleArray.get(2).floatValue()}; DynamicArray<Number> scaleArray = (DynamicArray<Number>) modifier.getFieldValue("scale");
params.put("scale", scale); float[] scale = new float[]{scaleArray.get(0).floatValue(), scaleArray.get(1).floatValue(), scaleArray.get(2).floatValue()};
} params.put("scale", scale);
if((offsettype & 0x04) != 0) {//Object offset }
Pointer pOffsetObject = (Pointer)modifier.getFieldValue("offset_ob"); if ((offsettype & 0x04) != 0) {//Object offset
if(!pOffsetObject.isNull()) { Pointer pOffsetObject = (Pointer) modifier.getFieldValue("offset_ob");
params.put("offsetob", pOffsetObject); if (!pOffsetObject.isNull()) {
} params.put("offsetob", pOffsetObject);
} }
}
//start cap and end cap
Pointer pStartCap = (Pointer)modifier.getFieldValue("start_cap"); //start cap and end cap
if(!pStartCap.isNull()) { Pointer pStartCap = (Pointer) modifier.getFieldValue("start_cap");
params.put("startcap", pStartCap); if (!pStartCap.isNull()) {
} params.put("startcap", pStartCap);
Pointer pEndCap = (Pointer)modifier.getFieldValue("end_cap"); }
if(!pEndCap.isNull()) { Pointer pEndCap = (Pointer) modifier.getFieldValue("end_cap");
params.put("endcap", pEndCap); if (!pEndCap.isNull()) {
} params.put("endcap", pEndCap);
loadedModifier = params; }
} else if(Modifier.ARMATURE_MODIFIER_DATA.equals(modifier.getType())) {//****************ARMATURE MODIFIER loadedModifier = params;
Pointer pArmatureObject = (Pointer)modifier.getFieldValue("object"); } else if (Modifier.ARMATURE_MODIFIER_DATA.equals(modifier.getType())) {//****************ARMATURE MODIFIER
if(!pArmatureObject.isNull()) { Pointer pArmatureObject = (Pointer) modifier.getFieldValue("object");
ObjectHelper objectHelper = dataRepository.getHelper(ObjectHelper.class); if (!pArmatureObject.isNull()) {
Structure armatureObject = (Structure)dataRepository.getLoadedFeature(pArmatureObject.getOldMemoryAddress(), LoadedFeatureDataType.LOADED_STRUCTURE); ObjectHelper objectHelper = dataRepository.getHelper(ObjectHelper.class);
if(armatureObject == null) {//we check this first not to fetch the structure unnecessary Structure armatureObject = (Structure) dataRepository.getLoadedFeature(pArmatureObject.getOldMemoryAddress(), LoadedFeatureDataType.LOADED_STRUCTURE);
armatureObject = pArmatureObject.fetchData(dataRepository.getInputStream()).get(0); if (armatureObject == null) {//we check this first not to fetch the structure unnecessary
objectHelper.toObject(armatureObject, dataRepository); armatureObject = pArmatureObject.fetchData(dataRepository.getInputStream()).get(0);
} objectHelper.toObject(armatureObject, dataRepository);
modifierAdditionalData = armatureObject.getOldMemoryAddress(); }
ArmatureHelper armatureHelper = dataRepository.getHelper(ArmatureHelper.class); modifierAdditionalData = armatureObject.getOldMemoryAddress();
ArmatureHelper armatureHelper = dataRepository.getHelper(ArmatureHelper.class);
//changing bones matrices so that they fit the current object (taht is why we need a copy of a skeleton)
Matrix4f armatureObjectMatrix = objectHelper.getTransformationMatrix(armatureObject); //changing bones matrices so that they fit the current object (taht is why we need a copy of a skeleton)
Matrix4f inverseMeshObjectMatrix = objectHelper.getTransformationMatrix(objectStructure).invert(); Matrix4f armatureObjectMatrix = objectHelper.getTransformationMatrix(armatureObject);
Matrix4f additionalRootBoneTransformation = inverseMeshObjectMatrix.multLocal(armatureObjectMatrix); Matrix4f inverseMeshObjectMatrix = objectHelper.getTransformationMatrix(objectStructure).invert();
Bone[] bones = armatureHelper.buildBonesStructure(Long.valueOf(0L), additionalRootBoneTransformation); Matrix4f additionalRootBoneTransformation = inverseMeshObjectMatrix.multLocal(armatureObjectMatrix);
Bone[] bones = armatureHelper.buildBonesStructure(Long.valueOf(0L), additionalRootBoneTransformation);
String objectName = objectStructure.getName();
Set<String> animationNames = dataRepository.getBlenderKey().getAnimationNames(objectName); String objectName = objectStructure.getName();
if(animationNames != null && animationNames.size() > 0) { Set<String> animationNames = dataRepository.getBlenderKey().getAnimationNames(objectName);
ArrayList<BoneAnimation> animations = new ArrayList<BoneAnimation>(); if (animationNames != null && animationNames.size() > 0) {
List<FileBlockHeader> actionHeaders = dataRepository.getFileBlocks(Integer.valueOf(FileBlockHeader.BLOCK_AC00)); ArrayList<BoneAnimation> animations = new ArrayList<BoneAnimation>();
for(FileBlockHeader header : actionHeaders) { List<FileBlockHeader> actionHeaders = dataRepository.getFileBlocks(Integer.valueOf(FileBlockHeader.BLOCK_AC00));
Structure actionStructure = header.getStructure(dataRepository); for (FileBlockHeader header : actionHeaders) {
String actionName = actionStructure.getName(); Structure actionStructure = header.getStructure(dataRepository);
if(animationNames.contains(actionName)) { String actionName = actionStructure.getName();
int[] animationFrames = dataRepository.getBlenderKey().getAnimationFrames(objectName, actionName); if (animationNames.contains(actionName)) {
int fps = dataRepository.getBlenderKey().getFps(); int[] animationFrames = dataRepository.getBlenderKey().getAnimationFrames(objectName, actionName);
float start = (float)animationFrames[0] / (float)fps; int fps = dataRepository.getBlenderKey().getFps();
float stop = (float)animationFrames[1] / (float)fps; float start = (float) animationFrames[0] / (float) fps;
BoneAnimation boneAnimation = new BoneAnimation(actionName, stop - start); float stop = (float) animationFrames[1] / (float) fps;
boneAnimation.setTracks(armatureHelper.getTracks(actionStructure, dataRepository, objectName, actionName)); BoneAnimation boneAnimation = new BoneAnimation(actionName, stop - start);
animations.add(boneAnimation); boneAnimation.setTracks(armatureHelper.getTracks(actionStructure, dataRepository, objectName, actionName));
} animations.add(boneAnimation);
} }
loadedModifier = new AnimData(new Skeleton(bones), animations); }
} loadedModifier = new AnimData(new Skeleton(bones), animations);
} else { }
LOGGER.warning("Unsupported modifier type: " + modifier.getType()); } else {
} LOGGER.warning("Unsupported modifier type: " + modifier.getType());
} else if(Modifier.PARTICLE_MODIFIER_DATA.equals(modifier.getType())) {//****************PARTICLES MODIFIER }
Pointer pParticleSystem = (Pointer) modifier.getFieldValue("psys"); } else if (Modifier.PARTICLE_MODIFIER_DATA.equals(modifier.getType())) {//****************PARTICLES MODIFIER
if(!pParticleSystem.isNull()) { Pointer pParticleSystem = (Pointer) modifier.getFieldValue("psys");
ParticlesHelper particlesHelper = dataRepository.getHelper(ParticlesHelper.class); if (!pParticleSystem.isNull()) {
Structure particleSystem = pParticleSystem.fetchData(dataRepository.getInputStream()).get(0); ParticlesHelper particlesHelper = dataRepository.getHelper(ParticlesHelper.class);
loadedModifier = particlesHelper.toParticleEmitter(particleSystem, dataRepository); Structure particleSystem = pParticleSystem.fetchData(dataRepository.getInputStream()).get(0);
} loadedModifier = particlesHelper.toParticleEmitter(particleSystem, dataRepository);
} }
//adding modifier to the modifier's lists }
if(loadedModifier != null) { //adding modifier to the modifier's lists
dataRepository.addModifier(objectStructure.getOldMemoryAddress(), modifier.getType(), loadedModifier, modifierAdditionalData); if (loadedModifier != null) {
modifierAdditionalData = null; dataRepository.addModifier(objectStructure.getOldMemoryAddress(), modifier.getType(), loadedModifier, modifierAdditionalData);
} modifierAdditionalData = null;
} }
} }
}
/**
* This method applies particles emitter to the given node. /**
* @param node the particles emitter node * This method applies particles emitter to the given node.
* @param modifier the modifier containing the emitter data * @param node the particles emitter node
* @param dataRepository the data repository * @param modifier the modifier containing the emitter data
* @return node with particles' emitter applied * @param dataRepository the data repository
*/ * @return node with particles' emitter applied
protected Node applyParticleSystemModifierData(Node node, Modifier modifier, DataRepository dataRepository) { */
MaterialHelper materialHelper = dataRepository.getHelper(MaterialHelper.class); protected Node applyParticleSystemModifierData(Node node, Modifier modifier, DataRepository dataRepository) {
ParticleEmitter emitter = (ParticleEmitter) modifier.getJmeModifierRepresentation(); MaterialHelper materialHelper = dataRepository.getHelper(MaterialHelper.class);
emitter = emitter.clone(); ParticleEmitter emitter = (ParticleEmitter) modifier.getJmeModifierRepresentation();
emitter = emitter.clone();
//veryfying the alpha function for particles' texture
Integer alphaFunction = MaterialHelper.ALPHA_MASK_HYPERBOLE; //veryfying the alpha function for particles' texture
char nameSuffix = emitter.getName().charAt(emitter.getName().length()-1); Integer alphaFunction = MaterialHelper.ALPHA_MASK_HYPERBOLE;
if(nameSuffix=='B' || nameSuffix=='N') { char nameSuffix = emitter.getName().charAt(emitter.getName().length() - 1);
alphaFunction = MaterialHelper.ALPHA_MASK_NONE; if (nameSuffix == 'B' || nameSuffix == 'N') {
} alphaFunction = MaterialHelper.ALPHA_MASK_NONE;
//removing the type suffix from the name }
emitter.setName(emitter.getName().substring(0, emitter.getName().length()-1)); //removing the type suffix from the name
emitter.setName(emitter.getName().substring(0, emitter.getName().length() - 1));
//applying emitter shape
EmitterShape emitterShape = emitter.getShape(); //applying emitter shape
List<Mesh> meshes = new ArrayList<Mesh>(); EmitterShape emitterShape = emitter.getShape();
for(Spatial spatial : node.getChildren()) { List<Mesh> meshes = new ArrayList<Mesh>();
if(spatial instanceof Geometry) { for (Spatial spatial : node.getChildren()) {
Mesh mesh = ((Geometry) spatial).getMesh(); if (spatial instanceof Geometry) {
if(mesh != null) { Mesh mesh = ((Geometry) spatial).getMesh();
meshes.add(mesh); if (mesh != null) {
Material material = materialHelper.getParticlesMaterial(((Geometry) spatial).getMaterial(), alphaFunction, dataRepository); meshes.add(mesh);
emitter.setMaterial(material);//TODO: divide into several pieces Material material = materialHelper.getParticlesMaterial(((Geometry) spatial).getMaterial(), alphaFunction, dataRepository);
} emitter.setMaterial(material);//TODO: divide into several pieces
} }
} }
if(meshes.size()>0 && emitterShape instanceof EmitterMeshVertexShape) { }
((EmitterMeshVertexShape) emitterShape).setMeshes(meshes); if (meshes.size() > 0 && emitterShape instanceof EmitterMeshVertexShape) {
} ((EmitterMeshVertexShape) emitterShape).setMeshes(meshes);
}
node.attachChild(emitter);
return node; node.attachChild(emitter);
} return node;
}
/**
* This method applies ArmatureModifierData to the loaded object. /**
* @param node * This method applies ArmatureModifierData to the loaded object.
* the loaded object * @param node
* @param modifier * the loaded object
* the modifier to apply * @param modifier
* @param dataRepository * the modifier to apply
* the data repository * @param dataRepository
* @return the node to whom the modifier was applied * the data repository
*/ * @return the node to whom the modifier was applied
protected Node applyArmatureModifierData(Node node, Modifier modifier, DataRepository dataRepository) { */
AnimData ad = (AnimData)modifier.getJmeModifierRepresentation(); protected Node applyArmatureModifierData(Node node, Modifier modifier, DataRepository dataRepository) {
ArrayList<BoneAnimation> animList = ad.anims; AnimData ad = (AnimData) modifier.getJmeModifierRepresentation();
Long modifierArmatureObject = (Long)modifier.getAdditionalData(); ArrayList<BoneAnimation> animList = ad.anims;
if(animList != null && animList.size() > 0) { Long modifierArmatureObject = (Long) modifier.getAdditionalData();
ConstraintHelper constraintHelper = dataRepository.getHelper(ConstraintHelper.class); if (animList != null && animList.size() > 0) {
Constraint[] constraints = constraintHelper.getConstraints(modifierArmatureObject); ConstraintHelper constraintHelper = dataRepository.getHelper(ConstraintHelper.class);
HashMap<String, BoneAnimation> anims = new HashMap<String, BoneAnimation>(); Constraint[] constraints = constraintHelper.getConstraints(modifierArmatureObject);
for(int i = 0; i < animList.size(); ++i) { HashMap<String, BoneAnimation> anims = new HashMap<String, BoneAnimation>();
BoneAnimation boneAnimation = this.cloneBoneAnimation(animList.get(i)); for (int i = 0; i < animList.size(); ++i) {
BoneAnimation boneAnimation = this.cloneBoneAnimation(animList.get(i));
//baking constraints into animations
if(constraints != null && constraints.length > 0) { //baking constraints into animations
for(Constraint constraint : constraints) { if (constraints != null && constraints.length > 0) {
constraint.affectAnimation(ad.skeleton, boneAnimation); for (Constraint constraint : constraints) {
} constraint.affectAnimation(ad.skeleton, boneAnimation);
} }
}
anims.put(boneAnimation.getName(), boneAnimation);
} anims.put(boneAnimation.getName(), boneAnimation);
}
//getting meshes
Mesh[] meshes = null; //getting meshes
List<Mesh> meshesList = new ArrayList<Mesh>(); Mesh[] meshes = null;
List<Spatial> children = node.getChildren(); List<Mesh> meshesList = new ArrayList<Mesh>();
for(Spatial child : children) { List<Spatial> children = node.getChildren();
if(child instanceof Geometry) { for (Spatial child : children) {
meshesList.add(((Geometry)child).getMesh()); if (child instanceof Geometry) {
} meshesList.add(((Geometry) child).getMesh());
} }
if(meshesList.size() > 0) { }
meshes = meshesList.toArray(new Mesh[meshesList.size()]); if (meshesList.size() > 0) {
} meshes = meshesList.toArray(new Mesh[meshesList.size()]);
}
//applying the control to the node
SkeletonControl skeletonControl = new SkeletonControl(meshes, ad.skeleton); //applying the control to the node
AnimControl control = node.getControl(AnimControl.class); SkeletonControl skeletonControl = new SkeletonControl(meshes, ad.skeleton);
AnimControl control = node.getControl(AnimControl.class);
if(control == null) {
control = new AnimControl(ad.skeleton); if (control == null) {
} else { control = new AnimControl(ad.skeleton);
//merging skeletons } else {
Skeleton controlSkeleton = control.getSkeleton(); //merging skeletons
int boneIndexIncrease = controlSkeleton.getBoneCount(); Skeleton controlSkeleton = control.getSkeleton();
Skeleton skeleton = this.merge(controlSkeleton, ad.skeleton); int boneIndexIncrease = controlSkeleton.getBoneCount();
Skeleton skeleton = this.merge(controlSkeleton, ad.skeleton);
//merging animations
HashMap<String, BoneAnimation> animations = new HashMap<String, BoneAnimation>(); //merging animations
for(String animationName : control.getAnimationNames()) { HashMap<String, BoneAnimation> animations = new HashMap<String, BoneAnimation>();
animations.put(animationName, control.getAnim(animationName)); for (String animationName : control.getAnimationNames()) {
} animations.put(animationName, control.getAnim(animationName));
for(Entry<String, BoneAnimation> animEntry : anims.entrySet()) { }
BoneAnimation ba = animEntry.getValue(); for (Entry<String, BoneAnimation> animEntry : anims.entrySet()) {
for(int i = 0; i < ba.getTracks().length; ++i) { BoneAnimation ba = animEntry.getValue();
BoneTrack bt = ba.getTracks()[i]; for (int i = 0; i < ba.getTracks().length; ++i) {
int newBoneIndex = bt.getTargetBoneIndex() + boneIndexIncrease; BoneTrack bt = ba.getTracks()[i];
ba.getTracks()[i] = new BoneTrack(newBoneIndex, bt.getTimes(), bt.getTranslations(), bt.getRotations(), bt.getScales()); int newBoneIndex = bt.getTargetBoneIndex() + boneIndexIncrease;
} ba.getTracks()[i] = new BoneTrack(newBoneIndex, bt.getTimes(), bt.getTranslations(), bt.getRotations(), bt.getScales());
animations.put(animEntry.getKey(), animEntry.getValue()); }
} animations.put(animEntry.getKey(), animEntry.getValue());
}
//replacing the control
node.removeControl(control); //replacing the control
control = new AnimControl(skeleton); node.removeControl(control);
} control = new AnimControl(skeleton);
control.setAnimations(anims); }
node.addControl(control); control.setAnimations(anims);
node.addControl(skeletonControl); node.addControl(control);
} node.addControl(skeletonControl);
return node; }
} return node;
}
/**
* This method applies the array modifier to the node. /**
* @param node * This method applies the array modifier to the node.
* the object the modifier will be applied to * @param node
* @param modifier * the object the modifier will be applied to
* the modifier to be applied * @param modifier
* @param dataRepository * the modifier to be applied
* the data repository * @param dataRepository
* @return object node with arry modifier applied * the data repository
*/ * @return object node with arry modifier applied
@SuppressWarnings("unchecked") */
protected Node applyArrayModifierData(Node node, Modifier modifier, DataRepository dataRepository) { @SuppressWarnings("unchecked")
Map<String, Object> modifierData = (Map<String, Object>) modifier.getJmeModifierRepresentation(); protected Node applyArrayModifierData(Node node, Modifier modifier, DataRepository dataRepository) {
int fittype = ((Number)modifierData.get("fittype")).intValue(); Map<String, Object> modifierData = (Map<String, Object>) modifier.getJmeModifierRepresentation();
float[] offset = (float[])modifierData.get("offset"); int fittype = ((Number) modifierData.get("fittype")).intValue();
if(offset==null) {//the node will be repeated several times in the same place float[] offset = (float[]) modifierData.get("offset");
offset = new float[] {0.0f, 0.0f, 0.0f}; if (offset == null) {//the node will be repeated several times in the same place
} offset = new float[]{0.0f, 0.0f, 0.0f};
float[] scale = (float[])modifierData.get("scale"); }
if(scale==null) {//the node will be repeated several times in the same place float[] scale = (float[]) modifierData.get("scale");
scale = new float[] {0.0f, 0.0f, 0.0f}; if (scale == null) {//the node will be repeated several times in the same place
} else { scale = new float[]{0.0f, 0.0f, 0.0f};
//getting bounding box } else {
node.updateModelBound(); //getting bounding box
BoundingVolume boundingVolume = node.getWorldBound(); node.updateModelBound();
if(boundingVolume instanceof BoundingBox) { BoundingVolume boundingVolume = node.getWorldBound();
scale[0] *= ((BoundingBox) boundingVolume).getXExtent() * 2.0f; if (boundingVolume instanceof BoundingBox) {
scale[1] *= ((BoundingBox) boundingVolume).getYExtent() * 2.0f; scale[0] *= ((BoundingBox) boundingVolume).getXExtent() * 2.0f;
scale[2] *= ((BoundingBox) boundingVolume).getZExtent() * 2.0f; scale[1] *= ((BoundingBox) boundingVolume).getYExtent() * 2.0f;
} else if(boundingVolume instanceof BoundingSphere) { scale[2] *= ((BoundingBox) boundingVolume).getZExtent() * 2.0f;
float radius = ((BoundingSphere) boundingVolume).getRadius(); } else if (boundingVolume instanceof BoundingSphere) {
scale[0] *= radius * 2.0f; float radius = ((BoundingSphere) boundingVolume).getRadius();
scale[1] *= radius * 2.0f; scale[0] *= radius * 2.0f;
scale[2] *= radius * 2.0f; scale[1] *= radius * 2.0f;
} else { scale[2] *= radius * 2.0f;
throw new IllegalStateException("Unknown bounding volume type: " + boundingVolume.getClass().getName()); } else {
} throw new IllegalStateException("Unknown bounding volume type: " + boundingVolume.getClass().getName());
} }
}
//adding object's offset
float[] objectOffset = new float[] {0.0f, 0.0f, 0.0f}; //adding object's offset
Pointer pOffsetObject = (Pointer) modifierData.get("offsetob"); float[] objectOffset = new float[]{0.0f, 0.0f, 0.0f};
if(pOffsetObject!=null) { Pointer pOffsetObject = (Pointer) modifierData.get("offsetob");
FileBlockHeader offsetObjectBlock = dataRepository.getFileBlock(pOffsetObject.getOldMemoryAddress()); if (pOffsetObject != null) {
ObjectHelper objectHelper = dataRepository.getHelper(ObjectHelper.class); FileBlockHeader offsetObjectBlock = dataRepository.getFileBlock(pOffsetObject.getOldMemoryAddress());
try {//we take the structure in case the object was not yet loaded ObjectHelper objectHelper = dataRepository.getHelper(ObjectHelper.class);
Structure offsetStructure = offsetObjectBlock.getStructure(dataRepository); try {//we take the structure in case the object was not yet loaded
Vector3f translation = objectHelper.getTransformation(offsetStructure).getTranslation(); Structure offsetStructure = offsetObjectBlock.getStructure(dataRepository);
objectOffset[0] = translation.x; Vector3f translation = objectHelper.getTransformation(offsetStructure).getTranslation();
objectOffset[1] = translation.y; objectOffset[0] = translation.x;
objectOffset[2] = translation.z; objectOffset[1] = translation.y;
} catch (BlenderFileException e) { objectOffset[2] = translation.z;
LOGGER.warning("Problems in blender file structure! Object offset cannot be applied! The problem: " + e.getMessage()); } catch (BlenderFileException e) {
} LOGGER.warning("Problems in blender file structure! Object offset cannot be applied! The problem: " + e.getMessage());
} }
}
//getting start and end caps
Node[] caps = new Node[] {null, null}; //getting start and end caps
Pointer[] pCaps = new Pointer[] {(Pointer) modifierData.get("startcap"), (Pointer) modifierData.get("endcap")}; Node[] caps = new Node[]{null, null};
for(int i=0;i<pCaps.length;++i) { Pointer[] pCaps = new Pointer[]{(Pointer) modifierData.get("startcap"), (Pointer) modifierData.get("endcap")};
if(pCaps[i]!=null) { for (int i = 0; i < pCaps.length; ++i) {
caps[i] = (Node) dataRepository.getLoadedFeature(pCaps[i].getOldMemoryAddress(), LoadedFeatureDataType.LOADED_FEATURE); if (pCaps[i] != null) {
if(caps[i]!=null) { caps[i] = (Node) dataRepository.getLoadedFeature(pCaps[i].getOldMemoryAddress(), LoadedFeatureDataType.LOADED_FEATURE);
caps[i] = (Node) caps[i].clone(); if (caps[i] != null) {
} else { caps[i] = (Node) caps[i].clone();
FileBlockHeader capBlock = dataRepository.getFileBlock(pOffsetObject.getOldMemoryAddress()); } else {
try {//we take the structure in case the object was not yet loaded FileBlockHeader capBlock = dataRepository.getFileBlock(pOffsetObject.getOldMemoryAddress());
Structure capStructure = capBlock.getStructure(dataRepository); try {//we take the structure in case the object was not yet loaded
ObjectHelper objectHelper = dataRepository.getHelper(ObjectHelper.class); Structure capStructure = capBlock.getStructure(dataRepository);
caps[i] = (Node) objectHelper.toObject(capStructure, dataRepository); ObjectHelper objectHelper = dataRepository.getHelper(ObjectHelper.class);
if(caps[i]==null) { caps[i] = (Node) objectHelper.toObject(capStructure, dataRepository);
LOGGER.warning("Cap object '" + capStructure.getName() + "' couldn't be loaded!"); if (caps[i] == null) {
} LOGGER.warning("Cap object '" + capStructure.getName() + "' couldn't be loaded!");
} catch (BlenderFileException e) { }
LOGGER.warning("Problems in blender file structure! Cap object cannot be applied! The problem: " + e.getMessage()); } catch (BlenderFileException e) {
} LOGGER.warning("Problems in blender file structure! Cap object cannot be applied! The problem: " + e.getMessage());
} }
} }
} }
}
Vector3f translationVector = new Vector3f(offset[0] + scale[0] + objectOffset[0],
offset[1] + scale[1] + objectOffset[1], Vector3f translationVector = new Vector3f(offset[0] + scale[0] + objectOffset[0],
offset[2] + scale[2] + objectOffset[2]); offset[1] + scale[1] + objectOffset[1],
offset[2] + scale[2] + objectOffset[2]);
//getting/calculating repeats amount
int count = 0; //getting/calculating repeats amount
if(fittype==0) {//Fixed count int count = 0;
count = ((Number)modifierData.get("count")).intValue() - 1; if (fittype == 0) {//Fixed count
} else if(fittype==1) {//Fixed length count = ((Number) modifierData.get("count")).intValue() - 1;
float length = ((Number)modifierData.get("length")).floatValue(); } else if (fittype == 1) {//Fixed length
if(translationVector.length()>0.0f) { float length = ((Number) modifierData.get("length")).floatValue();
count = (int)(length / translationVector.length()) - 1; if (translationVector.length() > 0.0f) {
} count = (int) (length / translationVector.length()) - 1;
} else if(fittype==2) {//Fit curve }
LOGGER.warning("Fit curve mode in array modifier not yet implemented!");//TODO: implement fit curve } else if (fittype == 2) {//Fit curve
} else { LOGGER.warning("Fit curve mode in array modifier not yet implemented!");//TODO: implement fit curve
throw new IllegalStateException("Unknown fit type: " + fittype); } else {
} throw new IllegalStateException("Unknown fit type: " + fittype);
}
//adding translated nodes and caps
if(count>0) { //adding translated nodes and caps
Node[] arrayNodes = new Node[count]; if (count > 0) {
Vector3f newTranslation = node.getLocalTranslation().clone(); Node[] arrayNodes = new Node[count];
for(int i=0;i<count;++i) { Vector3f newTranslation = node.getLocalTranslation().clone();
newTranslation.addLocal(translationVector); for (int i = 0; i < count; ++i) {
Node nodeClone = (Node) node.clone(); newTranslation.addLocal(translationVector);
nodeClone.setLocalTranslation(newTranslation); Node nodeClone = (Node) node.clone();
arrayNodes[i] = nodeClone; nodeClone.setLocalTranslation(newTranslation);
} arrayNodes[i] = nodeClone;
for(Node nodeClone : arrayNodes) { }
node.attachChild(nodeClone); for (Node nodeClone : arrayNodes) {
} node.attachChild(nodeClone);
if(caps[0]!=null) { }
caps[0].getLocalTranslation().set(node.getLocalTranslation()).subtractLocal(translationVector); if (caps[0] != null) {
node.attachChild(caps[0]); caps[0].getLocalTranslation().set(node.getLocalTranslation()).subtractLocal(translationVector);
} node.attachChild(caps[0]);
if(caps[1]!=null) { }
caps[1].getLocalTranslation().set(newTranslation).addLocal(translationVector); if (caps[1] != null) {
node.attachChild(caps[1]); caps[1].getLocalTranslation().set(newTranslation).addLocal(translationVector);
} node.attachChild(caps[1]);
} }
return node; }
} return node;
}
/**
* This class clones the bone animation data. /**
* @param source * This class clones the bone animation data.
* the source that is to be cloned * @param source
* @return the copy of the given bone animation * the source that is to be cloned
*/ * @return the copy of the given bone animation
protected BoneAnimation cloneBoneAnimation(BoneAnimation source) { */
BoneAnimation result = new BoneAnimation(source.getName(), source.getLength()); protected BoneAnimation cloneBoneAnimation(BoneAnimation source) {
BoneAnimation result = new BoneAnimation(source.getName(), source.getLength());
//copying tracks and applying constraints
BoneTrack[] sourceTracks = source.getTracks(); //copying tracks and applying constraints
BoneTrack[] boneTracks = new BoneTrack[sourceTracks.length]; BoneTrack[] sourceTracks = source.getTracks();
for(int i = 0; i < sourceTracks.length; ++i) { BoneTrack[] boneTracks = new BoneTrack[sourceTracks.length];
int tablesLength = sourceTracks[i].getTimes().length; for (int i = 0; i < sourceTracks.length; ++i) {
int tablesLength = sourceTracks[i].getTimes().length;
Vector3f[] sourceTranslations = sourceTracks[i].getTranslations();
Quaternion[] sourceRotations = sourceTracks[i].getRotations(); Vector3f[] sourceTranslations = sourceTracks[i].getTranslations();
Vector3f[] sourceScales = sourceTracks[i].getScales(); Quaternion[] sourceRotations = sourceTracks[i].getRotations();
Vector3f[] sourceScales = sourceTracks[i].getScales();
Vector3f[] translations = new Vector3f[tablesLength];
Quaternion[] rotations = new Quaternion[tablesLength]; Vector3f[] translations = new Vector3f[tablesLength];
Vector3f[] scales = sourceScales == null ? null : new Vector3f[tablesLength]; Quaternion[] rotations = new Quaternion[tablesLength];
for(int j = 0; j < tablesLength; ++j) { Vector3f[] scales = sourceScales == null ? null : new Vector3f[tablesLength];
translations[j] = sourceTranslations[j].clone(); for (int j = 0; j < tablesLength; ++j) {
rotations[j] = sourceRotations[j].clone(); translations[j] = sourceTranslations[j].clone();
if(sourceScales != null) {//only scales may not be applied rotations[j] = sourceRotations[j].clone();
scales[j] = sourceScales[j].clone(); if (sourceScales != null) {//only scales may not be applied
} scales[j] = sourceScales[j].clone();
} }
boneTracks[i] = new BoneTrack(sourceTracks[i].getTargetBoneIndex(), sourceTracks[i].getTimes(),//times do not change, no need to clone them, }
translations, rotations, scales); boneTracks[i] = new BoneTrack(sourceTracks[i].getTargetBoneIndex(), sourceTracks[i].getTimes(),//times do not change, no need to clone them,
} translations, rotations, scales);
result.setTracks(boneTracks); }
return result; result.setTracks(boneTracks);
} return result;
}
/**
* This method merges two skeletons into one. I assume that each skeleton's 0-indexed bone is objectAnimationBone so /**
* only one such bone should be placed in the result * This method merges two skeletons into one. I assume that each skeleton's 0-indexed bone is objectAnimationBone so
* @param s1 * only one such bone should be placed in the result
* first skeleton * @param s1
* @param s2 * first skeleton
* second skeleton * @param s2
* @return merged skeleton * second skeleton
*/ * @return merged skeleton
protected Skeleton merge(Skeleton s1, Skeleton s2) { */
List<Bone> bones = new ArrayList<Bone>(s1.getBoneCount() + s2.getBoneCount()); protected Skeleton merge(Skeleton s1, Skeleton s2) {
for(int i = 0; i < s1.getBoneCount(); ++i) { List<Bone> bones = new ArrayList<Bone>(s1.getBoneCount() + s2.getBoneCount());
bones.add(s1.getBone(i)); for (int i = 0; i < s1.getBoneCount(); ++i) {
} bones.add(s1.getBone(i));
for(int i = 1; i < s2.getBoneCount(); ++i) {//ommit objectAnimationBone }
bones.add(s2.getBone(i)); for (int i = 1; i < s2.getBoneCount(); ++i) {//ommit objectAnimationBone
} bones.add(s2.getBone(i));
return new Skeleton(bones.toArray(new Bone[bones.size()])); }
} return new Skeleton(bones.toArray(new Bone[bones.size()]));
}
} }

@ -21,205 +21,199 @@ import com.jme3.scene.plugins.blender.utils.Pointer;
* @author Marcin Roguski * @author Marcin Roguski
*/ */
public abstract class AbstractInfluenceFunction { public abstract class AbstractInfluenceFunction {
protected static final Logger LOGGER = Logger.getLogger(AbstractInfluenceFunction.class.getName());
protected static final float IK_SOLVER_ERROR = 0.5f; protected static final Logger LOGGER = Logger.getLogger(AbstractInfluenceFunction.class.getName());
protected static final float IK_SOLVER_ERROR = 0.5f;
//DISTLIMIT //DISTLIMIT
protected static final int LIMITDIST_INSIDE = 0; protected static final int LIMITDIST_INSIDE = 0;
protected static final int LIMITDIST_OUTSIDE = 1; protected static final int LIMITDIST_OUTSIDE = 1;
protected static final int LIMITDIST_ONSURFACE = 2; protected static final int LIMITDIST_ONSURFACE = 2;
//CONSTRAINT_TYPE_LOCLIKE
//CONSTRAINT_TYPE_LOCLIKE protected static final int LOCLIKE_X = 0x01;
protected static final int LOCLIKE_X = 0x01; protected static final int LOCLIKE_Y = 0x02;
protected static final int LOCLIKE_Y = 0x02; protected static final int LOCLIKE_Z = 0x04;
protected static final int LOCLIKE_Z = 0x04; //ROTLIKE
protected static final int ROTLIKE_X = 0x01;
//ROTLIKE protected static final int ROTLIKE_Y = 0x02;
protected static final int ROTLIKE_X = 0x01; protected static final int ROTLIKE_Z = 0x04;
protected static final int ROTLIKE_Y = 0x02; protected static final int ROTLIKE_X_INVERT = 0x10;
protected static final int ROTLIKE_Z = 0x04; protected static final int ROTLIKE_Y_INVERT = 0x20;
protected static final int ROTLIKE_X_INVERT = 0x10; protected static final int ROTLIKE_Z_INVERT = 0x40;
protected static final int ROTLIKE_Y_INVERT = 0x20; protected static final int ROTLIKE_OFFSET = 0x80;
protected static final int ROTLIKE_Z_INVERT = 0x40; //SIZELIKE
protected static final int ROTLIKE_OFFSET = 0x80; protected static final int SIZELIKE_X = 0x01;
protected static final int SIZELIKE_Y = 0x02;
//SIZELIKE protected static final int SIZELIKE_Z = 0x04;
protected static final int SIZELIKE_X = 0x01; protected static final int SIZELIKE_OFFSET = 0x80;
protected static final int SIZELIKE_Y = 0x02;
protected static final int SIZELIKE_Z = 0x04; /* LOCLIKE_TIP is a depreceated option... use headtail=1.0f instead */
protected static final int SIZELIKE_OFFSET = 0x80; //protected static final int LOCLIKE_TIP = 0x08;
protected static final int LOCLIKE_X_INVERT = 0x10;
/* LOCLIKE_TIP is a depreceated option... use headtail=1.0f instead */ protected static final int LOCLIKE_Y_INVERT = 0x20;
//protected static final int LOCLIKE_TIP = 0x08; protected static final int LOCLIKE_Z_INVERT = 0x40;
protected static final int LOCLIKE_X_INVERT = 0x10; protected static final int LOCLIKE_OFFSET = 0x80;
protected static final int LOCLIKE_Y_INVERT = 0x20; //LOCLIMIT, SIZELIMIT
protected static final int LOCLIKE_Z_INVERT = 0x40; protected static final int LIMIT_XMIN = 0x01;
protected static final int LOCLIKE_OFFSET = 0x80; protected static final int LIMIT_XMAX = 0x02;
protected static final int LIMIT_YMIN = 0x04;
//LOCLIMIT, SIZELIMIT protected static final int LIMIT_YMAX = 0x08;
protected static final int LIMIT_XMIN = 0x01; protected static final int LIMIT_ZMIN = 0x10;
protected static final int LIMIT_XMAX = 0x02; protected static final int LIMIT_ZMAX = 0x20;
protected static final int LIMIT_YMIN = 0x04; //ROTLIMIT
protected static final int LIMIT_YMAX = 0x08; protected static final int LIMIT_XROT = 0x01;
protected static final int LIMIT_ZMIN = 0x10; protected static final int LIMIT_YROT = 0x02;
protected static final int LIMIT_ZMAX = 0x20; protected static final int LIMIT_ZROT = 0x04;
/** The type of the constraint. */
//ROTLIMIT protected ConstraintType constraintType;
protected static final int LIMIT_XROT = 0x01; /** The data repository. */
protected static final int LIMIT_YROT = 0x02; protected DataRepository dataRepository;
protected static final int LIMIT_ZROT = 0x04;
/**
/** The type of the constraint. */ * Constructor.
protected ConstraintType constraintType; * @param constraintType
/** The data repository. */ * the type of the current constraint
protected DataRepository dataRepository; * @param dataRepository
* the data repository
/** */
* Constructor. public AbstractInfluenceFunction(ConstraintType constraintType, DataRepository dataRepository) {
* @param constraintType this.constraintType = constraintType;
* the type of the current constraint this.dataRepository = dataRepository;
* @param dataRepository }
* the data repository
*/ /**
public AbstractInfluenceFunction(ConstraintType constraintType, DataRepository dataRepository) { * This method validates the constraint type. It throws an IllegalArgumentException if the constraint type of the
this.constraintType = constraintType; * given structure is invalid.
this.dataRepository = dataRepository; * @param constraintStructure
} * the structure with constraint data
*/
/** protected void validateConstraintType(Structure constraintStructure) {
* This method validates the constraint type. It throws an IllegalArgumentException if the constraint type of the if (!constraintType.getClassName().equalsIgnoreCase(constraintStructure.getType())) {
* given structure is invalid. throw new IllegalArgumentException("Invalud structure type (" + constraintStructure.getType() + ") for the constraint: " + constraintType.getClassName() + '!');
* @param constraintStructure }
* the structure with constraint data }
*/
protected void validateConstraintType(Structure constraintStructure) { /**
if(!constraintType.getClassName().equalsIgnoreCase(constraintStructure.getType())) { * This method affects the bone animation tracks for the given skeleton.
throw new IllegalArgumentException("Invalud structure type (" + constraintStructure.getType() + ") for the constraint: " + constraintType.getClassName() + '!'); * @param skeleton
} * the skeleton containing the affected bones by constraint
} * @param boneAnimation
* the bone animation baked traces
/** * @param constraint
* This method affects the bone animation tracks for the given skeleton. * the constraint
* @param skeleton */
* the skeleton containing the affected bones by constraint public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation, Constraint constraint) {
* @param boneAnimation }
* the bone animation baked traces
* @param constraint /**
* the constraint * This method returns the bone traces for the bone that is affected by the given constraint.
*/ * @param skeleton
public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation, Constraint constraint) {} * the skeleton containing bones
* @param boneAnimation
/** * the bone animation that affects the skeleton
* This method returns the bone traces for the bone that is affected by the given constraint. * @param constraint
* @param skeleton * the affecting constraint
* the skeleton containing bones * @return the bone track for the bone that is being affected by the constraint
* @param boneAnimation */
* the bone animation that affects the skeleton protected BoneTrack getBoneTrack(Skeleton skeleton, BoneAnimation boneAnimation, Constraint constraint) {
* @param constraint Long boneOMA = constraint.getBoneOMA();
* the affecting constraint Bone bone = (Bone) dataRepository.getLoadedFeature(boneOMA, LoadedFeatureDataType.LOADED_FEATURE);
* @return the bone track for the bone that is being affected by the constraint int boneIndex = skeleton.getBoneIndex(bone);
*/ if (boneIndex != -1) {
protected BoneTrack getBoneTrack(Skeleton skeleton, BoneAnimation boneAnimation, Constraint constraint) { //searching for track for this bone
Long boneOMA = constraint.getBoneOMA(); for (BoneTrack boneTrack : boneAnimation.getTracks()) {
Bone bone = (Bone)dataRepository.getLoadedFeature(boneOMA, LoadedFeatureDataType.LOADED_FEATURE); if (boneTrack.getTargetBoneIndex() == boneIndex) {
int boneIndex = skeleton.getBoneIndex(bone); return boneTrack;
if(boneIndex != -1) { }
//searching for track for this bone }
for(BoneTrack boneTrack : boneAnimation.getTracks()) { }
if(boneTrack.getTargetBoneIndex() == boneIndex) { return null;
return boneTrack; }
}
} /**
} * This method returns the target or subtarget object (if specified).
return null; * @param constraint
} * the constraint instance
* @return target or subtarget feature
/** */
* This method returns the target or subtarget object (if specified). protected Object getTarget(Constraint constraint, LoadedFeatureDataType loadedFeatureDataType) {
* @param constraint Long targetOMA = ((Pointer) constraint.getData().getFieldValue("tar")).getOldMemoryAddress();
* the constraint instance Object targetObject = dataRepository.getLoadedFeature(targetOMA, loadedFeatureDataType);
* @return target or subtarget feature String subtargetName = constraint.getData().getFieldValue("subtarget").toString();
*/ if (subtargetName.length() > 0) {
protected Object getTarget(Constraint constraint, LoadedFeatureDataType loadedFeatureDataType) { return dataRepository.getLoadedFeature(subtargetName, loadedFeatureDataType);
Long targetOMA = ((Pointer)constraint.getData().getFieldValue("tar")).getOldMemoryAddress(); }
Object targetObject = dataRepository.getLoadedFeature(targetOMA, loadedFeatureDataType); return targetObject;
String subtargetName = constraint.getData().getFieldValue("subtarget").toString(); }
if(subtargetName.length() > 0) {
return dataRepository.getLoadedFeature(subtargetName, loadedFeatureDataType); /**
} * This method returns target's object location.
return targetObject; * @param constraint
} * the constraint instance
* @return target's object location
/** */
* This method returns target's object location. protected Vector3f getTargetLocation(Constraint constraint) {
* @param constraint Long targetOMA = ((Pointer) constraint.getData().getFieldValue("tar")).getOldMemoryAddress();
* the constraint instance Space targetSpace = constraint.getTargetSpace();
* @return target's object location Node targetObject = (Node) dataRepository.getLoadedFeature(targetOMA, LoadedFeatureDataType.LOADED_FEATURE);
*/ switch (targetSpace) {
protected Vector3f getTargetLocation(Constraint constraint) { case CONSTRAINT_SPACE_LOCAL:
Long targetOMA = ((Pointer)constraint.getData().getFieldValue("tar")).getOldMemoryAddress(); return targetObject.getLocalTranslation();
Space targetSpace = constraint.getTargetSpace(); case CONSTRAINT_SPACE_WORLD:
Node targetObject = (Node)dataRepository.getLoadedFeature(targetOMA, LoadedFeatureDataType.LOADED_FEATURE); return targetObject.getWorldTranslation();
switch(targetSpace) { default:
case CONSTRAINT_SPACE_LOCAL: throw new IllegalStateException("Invalid space type for target object: " + targetSpace.toString());
return targetObject.getLocalTranslation(); }
case CONSTRAINT_SPACE_WORLD: }
return targetObject.getWorldTranslation();
default: /**
throw new IllegalStateException("Invalid space type for target object: " + targetSpace.toString()); * This method returns target's object location in the specified frame.
} * @param constraint
} * the constraint instance
* @param frame
/** * the frame number
* This method returns target's object location in the specified frame. * @return target's object location
* @param constraint */
* the constraint instance protected Vector3f getTargetLocation(Constraint constraint, int frame) {
* @param frame return this.getTargetLocation(constraint);//TODO: implement getting location in a specified frame
* the frame number }
* @return target's object location
*/ /**
protected Vector3f getTargetLocation(Constraint constraint, int frame) { * This method returns target's object rotation.
return this.getTargetLocation(constraint);//TODO: implement getting location in a specified frame * @param constraint
} * the constraint instance
* @return target's object rotation
/** */
* This method returns target's object rotation. protected Quaternion getTargetRotation(Constraint constraint) {
* @param constraint Long targetOMA = ((Pointer) constraint.getData().getFieldValue("tar")).getOldMemoryAddress();
* the constraint instance Space targetSpace = constraint.getTargetSpace();
* @return target's object rotation Node targetObject = (Node) dataRepository.getLoadedFeature(targetOMA, LoadedFeatureDataType.LOADED_FEATURE);
*/ switch (targetSpace) {
protected Quaternion getTargetRotation(Constraint constraint) { case CONSTRAINT_SPACE_LOCAL:
Long targetOMA = ((Pointer)constraint.getData().getFieldValue("tar")).getOldMemoryAddress(); return targetObject.getLocalRotation();
Space targetSpace = constraint.getTargetSpace(); case CONSTRAINT_SPACE_WORLD:
Node targetObject = (Node)dataRepository.getLoadedFeature(targetOMA, LoadedFeatureDataType.LOADED_FEATURE); return targetObject.getWorldRotation();
switch(targetSpace) { default:
case CONSTRAINT_SPACE_LOCAL: throw new IllegalStateException("Invalid space type for target object: " + targetSpace.toString());
return targetObject.getLocalRotation(); }
case CONSTRAINT_SPACE_WORLD: }
return targetObject.getWorldRotation();
default: /**
throw new IllegalStateException("Invalid space type for target object: " + targetSpace.toString()); * This method returns target's object scale.
} * @param constraint
} * the constraint instance
* @return target's object scale
/** */
* This method returns target's object scale. protected Vector3f getTargetScale(Constraint constraint) {
* @param constraint Long targetOMA = ((Pointer) constraint.getData().getFieldValue("tar")).getOldMemoryAddress();
* the constraint instance Space targetSpace = constraint.getTargetSpace();
* @return target's object scale Node targetObject = (Node) dataRepository.getLoadedFeature(targetOMA, LoadedFeatureDataType.LOADED_FEATURE);
*/ switch (targetSpace) {
protected Vector3f getTargetScale(Constraint constraint) { case CONSTRAINT_SPACE_LOCAL:
Long targetOMA = ((Pointer)constraint.getData().getFieldValue("tar")).getOldMemoryAddress(); return targetObject.getLocalScale();
Space targetSpace = constraint.getTargetSpace(); case CONSTRAINT_SPACE_WORLD:
Node targetObject = (Node)dataRepository.getLoadedFeature(targetOMA, LoadedFeatureDataType.LOADED_FEATURE); return targetObject.getWorldScale();
switch(targetSpace) { default:
case CONSTRAINT_SPACE_LOCAL: throw new IllegalStateException("Invalid space type for target object: " + targetSpace.toString());
return targetObject.getLocalScale(); }
case CONSTRAINT_SPACE_WORLD: }
return targetObject.getWorldScale();
default:
throw new IllegalStateException("Invalid space type for target object: " + targetSpace.toString());
}
}
} }

@ -13,125 +13,125 @@ import com.jme3.scene.plugins.blender.utils.DynamicArray;
* @author Marcin Roguski * @author Marcin Roguski
*/ */
public class BezierCurve { public class BezierCurve {
public static final int X_VALUE = 0;
public static final int Y_VALUE = 1;
public static final int Z_VALUE = 2;
/** public static final int X_VALUE = 0;
* The type of the curve. Describes the data it modifies. public static final int Y_VALUE = 1;
* Used in ipos calculations. public static final int Z_VALUE = 2;
*/ /**
private int type; * The type of the curve. Describes the data it modifies.
/** The dimension of the curve. */ * Used in ipos calculations.
private int dimension; */
/** A table of the bezier points. */ private int type;
private float[][][] bezierPoints; /** The dimension of the curve. */
private int dimension;
/** A table of the bezier points. */
private float[][][] bezierPoints;
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public BezierCurve(final int type, final List<Structure> bezTriples, final int dimension) { public BezierCurve(final int type, final List<Structure> bezTriples, final int dimension) {
if(dimension != 2 && dimension != 3) { if (dimension != 2 && dimension != 3) {
throw new IllegalArgumentException("The dimension of the curve should be 2 or 3!"); throw new IllegalArgumentException("The dimension of the curve should be 2 or 3!");
} }
this.type = type; this.type = type;
this.dimension = dimension; this.dimension = dimension;
//first index of the bezierPoints table has the length of triples amount //first index of the bezierPoints table has the length of triples amount
//the second index points to a table od three points of a bezier triple (handle, point, handle) //the second index points to a table od three points of a bezier triple (handle, point, handle)
//the third index specifies the coordinates of the specific point in a bezier triple //the third index specifies the coordinates of the specific point in a bezier triple
bezierPoints = new float[bezTriples.size()][3][dimension]; bezierPoints = new float[bezTriples.size()][3][dimension];
int i = 0, j, k; int i = 0, j, k;
for(Structure bezTriple : bezTriples) { for (Structure bezTriple : bezTriples) {
DynamicArray<Number> vec = (DynamicArray<Number>)bezTriple.getFieldValue("vec"); DynamicArray<Number> vec = (DynamicArray<Number>) bezTriple.getFieldValue("vec");
for(j = 0; j < 3; ++j) { for (j = 0; j < 3; ++j) {
for(k = 0; k < dimension; ++k) { for (k = 0; k < dimension; ++k) {
bezierPoints[i][j][k] = vec.get(j, k).floatValue(); bezierPoints[i][j][k] = vec.get(j, k).floatValue();
} }
} }
++i; ++i;
} }
} }
/** /**
* This method evaluates the data for the specified frame. The Y value is returned. * This method evaluates the data for the specified frame. The Y value is returned.
* @param frame * @param frame
* the frame for which the value is being calculated * the frame for which the value is being calculated
* @param valuePart * @param valuePart
* this param specifies wheather we should return the X, Y or Z part of the result value; it should have * this param specifies wheather we should return the X, Y or Z part of the result value; it should have
* one of the following values: X_VALUE - the X factor of the result Y_VALUE - the Y factor of the result * one of the following values: X_VALUE - the X factor of the result Y_VALUE - the Y factor of the result
* Z_VALUE - the Z factor of the result * Z_VALUE - the Z factor of the result
* @return the value of the curve * @return the value of the curve
*/ */
public float evaluate(int frame, int valuePart) { public float evaluate(int frame, int valuePart) {
for(int i = 0; i < bezierPoints.length - 1; ++i) { for (int i = 0; i < bezierPoints.length - 1; ++i) {
if(frame >= bezierPoints[i][1][0] && frame <= bezierPoints[i + 1][1][0]) { if (frame >= bezierPoints[i][1][0] && frame <= bezierPoints[i + 1][1][0]) {
float t = (frame - bezierPoints[i][1][0]) / (bezierPoints[i + 1][1][0] - bezierPoints[i][1][0]); float t = (frame - bezierPoints[i][1][0]) / (bezierPoints[i + 1][1][0] - bezierPoints[i][1][0]);
float oneMinusT = 1.0f - t; float oneMinusT = 1.0f - t;
float oneMinusT2 = oneMinusT * oneMinusT; float oneMinusT2 = oneMinusT * oneMinusT;
float t2 = t * t; float t2 = t * t;
return bezierPoints[i][1][valuePart] * oneMinusT2 * oneMinusT + 3.0f * bezierPoints[i][2][valuePart] * t * oneMinusT2 + 3.0f * bezierPoints[i + 1][0][valuePart] * t2 * oneMinusT + bezierPoints[i + 1][1][valuePart] * t2 * t; return bezierPoints[i][1][valuePart] * oneMinusT2 * oneMinusT + 3.0f * bezierPoints[i][2][valuePart] * t * oneMinusT2 + 3.0f * bezierPoints[i + 1][0][valuePart] * t2 * oneMinusT + bezierPoints[i + 1][1][valuePart] * t2 * t;
} }
} }
if(frame < bezierPoints[0][1][0]) { if (frame < bezierPoints[0][1][0]) {
return bezierPoints[0][1][1]; return bezierPoints[0][1][1];
} else { //frame>bezierPoints[bezierPoints.length-1][1][0] } else { //frame>bezierPoints[bezierPoints.length-1][1][0]
return bezierPoints[bezierPoints.length - 1][1][1]; return bezierPoints[bezierPoints.length - 1][1][1];
} }
} }
/** /**
* This method returns the frame where last bezier triple center point of the bezier curve is located. * This method returns the frame where last bezier triple center point of the bezier curve is located.
* @return the frame number of the last defined bezier triple point for the curve * @return the frame number of the last defined bezier triple point for the curve
*/ */
public int getLastFrame() { public int getLastFrame() {
return (int)bezierPoints[bezierPoints.length - 1][1][0]; return (int) bezierPoints[bezierPoints.length - 1][1][0];
} }
/** /**
* This method returns the type of the bezier curve. The type describes the parameter that this curve modifies * This method returns the type of the bezier curve. The type describes the parameter that this curve modifies
* (ie. LocationX or rotationW of the feature). * (ie. LocationX or rotationW of the feature).
* @return the type of the bezier curve * @return the type of the bezier curve
*/ */
public int getType() { public int getType() {
return type; return type;
} }
/** /**
* This method returns a list of control points for this curve. * This method returns a list of control points for this curve.
* @return a list of control points for this curve. * @return a list of control points for this curve.
*/ */
public List<Vector3f> getControlPoints() { public List<Vector3f> getControlPoints() {
List<Vector3f> controlPoints = new ArrayList<Vector3f>(bezierPoints.length * 3); List<Vector3f> controlPoints = new ArrayList<Vector3f>(bezierPoints.length * 3);
for(int i = 0;i<bezierPoints.length;++i) { for (int i = 0; i < bezierPoints.length; ++i) {
controlPoints.add(new Vector3f(bezierPoints[i][0][0], bezierPoints[i][0][1], bezierPoints[i][0][2])); controlPoints.add(new Vector3f(bezierPoints[i][0][0], bezierPoints[i][0][1], bezierPoints[i][0][2]));
controlPoints.add(new Vector3f(bezierPoints[i][1][0], bezierPoints[i][1][1], bezierPoints[i][1][2])); controlPoints.add(new Vector3f(bezierPoints[i][1][0], bezierPoints[i][1][1], bezierPoints[i][1][2]));
controlPoints.add(new Vector3f(bezierPoints[i][2][0], bezierPoints[i][2][1], bezierPoints[i][2][2])); controlPoints.add(new Vector3f(bezierPoints[i][2][0], bezierPoints[i][2][1], bezierPoints[i][2][2]));
} }
return controlPoints; return controlPoints;
} }
@Override @Override
public String toString() { public String toString() {
StringBuilder sb = new StringBuilder("Bezier curve: ").append(type).append('\n'); StringBuilder sb = new StringBuilder("Bezier curve: ").append(type).append('\n');
for(int i = 0; i < bezierPoints.length; ++i) { for (int i = 0; i < bezierPoints.length; ++i) {
sb.append(this.toStringBezTriple(i)).append('\n'); sb.append(this.toStringBezTriple(i)).append('\n');
} }
return sb.toString(); return sb.toString();
} }
/** /**
* This method converts the bezier triple of a specified index into text. * This method converts the bezier triple of a specified index into text.
* @param tripleIndex * @param tripleIndex
* index of the triple * index of the triple
* @return text representation of the triple * @return text representation of the triple
*/ */
private String toStringBezTriple(int tripleIndex) { private String toStringBezTriple(int tripleIndex) {
if(this.dimension==2) { if (this.dimension == 2) {
return "[(" + bezierPoints[tripleIndex][0][0] + ", " + bezierPoints[tripleIndex][0][1] + ") (" return "[(" + bezierPoints[tripleIndex][0][0] + ", " + bezierPoints[tripleIndex][0][1] + ") ("
+ bezierPoints[tripleIndex][1][0] + ", " + bezierPoints[tripleIndex][1][1] + ") (" + bezierPoints[tripleIndex][1][0] + ", " + bezierPoints[tripleIndex][1][1] + ") ("
+ bezierPoints[tripleIndex][2][0] + ", " + bezierPoints[tripleIndex][2][1] + ")]"; + bezierPoints[tripleIndex][2][0] + ", " + bezierPoints[tripleIndex][2][1] + ")]";
} else { } else {
return "[(" + bezierPoints[tripleIndex][0][0] + ", " + bezierPoints[tripleIndex][0][1] + ", " + bezierPoints[tripleIndex][0][2] + ") (" return "[(" + bezierPoints[tripleIndex][0][0] + ", " + bezierPoints[tripleIndex][0][1] + ", " + bezierPoints[tripleIndex][0][2] + ") ("
+ bezierPoints[tripleIndex][1][0] + ", " + bezierPoints[tripleIndex][1][1] + ", " + bezierPoints[tripleIndex][1][2] + ") (" + bezierPoints[tripleIndex][1][0] + ", " + bezierPoints[tripleIndex][1][1] + ", " + bezierPoints[tripleIndex][1][2] + ") ("
+ bezierPoints[tripleIndex][2][0] + ", " + bezierPoints[tripleIndex][2][1] + ", " + bezierPoints[tripleIndex][2][2] + ")]"; + bezierPoints[tripleIndex][2][0] + ", " + bezierPoints[tripleIndex][2][1] + ", " + bezierPoints[tripleIndex][2][2] + ")]";
} }
} }
} }

@ -12,151 +12,151 @@ import com.jme3.scene.plugins.blender.utils.Pointer;
* @author Marcin Roguski * @author Marcin Roguski
*/ */
public class Constraint { public class Constraint {
/** The type of this constraint. */
private final ConstraintType type;
/** The name of this constraint. */
private final String name;
/** The old memory address of the constraint's owner. */
private final Long boneOMA;
private final Space ownerSpace; /** The type of this constraint. */
private final ConstraintType type;
/** The name of this constraint. */
private final String name;
/** The old memory address of the constraint's owner. */
private final Long boneOMA;
private final Space ownerSpace;
private final Space targetSpace;
/** The structure with constraint's data. */
private final Structure data;
/** The ipo object defining influence. */
private final Ipo ipo;
/** The influence function of this constraint. */
private final AbstractInfluenceFunction influenceFunction;
private final Space targetSpace; /**
/** The structure with constraint's data. */ * This constructor creates the constraint instance.
private final Structure data; * @param constraintStructure
/** The ipo object defining influence. */ * the constraint's structure (bConstraint clss in blender 2.49).
private final Ipo ipo; * @param influenceFunction
/** The influence function of this constraint. */ * the constraint's influence function (taken from ConstraintHelper)
private final AbstractInfluenceFunction influenceFunction; * @param boneOMA
* the old memory address of the constraint owner
* @param influenceIpo
* the ipo curve of the influence factor
* @param dataRepository
* the data repository
* @throws BlenderFileException
*/
public Constraint(Structure constraintStructure, AbstractInfluenceFunction influenceFunction, Long boneOMA, Space ownerSpace, Space targetSpace, Ipo influenceIpo, DataRepository dataRepository) throws BlenderFileException {
if (influenceFunction == null) {
throw new IllegalArgumentException("Influence function is not defined!");
}
Pointer pData = (Pointer) constraintStructure.getFieldValue("data");
if (!pData.isNull()) {
data = pData.fetchData(dataRepository.getInputStream()).get(0);
} else {
throw new BlenderFileException("The constraint has no data specified!");
}
this.boneOMA = boneOMA;
this.type = ConstraintType.valueOf(((Number) constraintStructure.getFieldValue("type")).intValue());
this.name = constraintStructure.getFieldValue("name").toString();
this.ownerSpace = ownerSpace;
this.targetSpace = targetSpace;
this.ipo = influenceIpo;
this.influenceFunction = influenceFunction;
}
/** /**
* This constructor creates the constraint instance. * This method returns the name of the constraint.
* @param constraintStructure * @return the name of the constraint
* the constraint's structure (bConstraint clss in blender 2.49). */
* @param influenceFunction public String getName() {
* the constraint's influence function (taken from ConstraintHelper) return name;
* @param boneOMA }
* the old memory address of the constraint owner
* @param influenceIpo
* the ipo curve of the influence factor
* @param dataRepository
* the data repository
* @throws BlenderFileException
*/
public Constraint(Structure constraintStructure, AbstractInfluenceFunction influenceFunction, Long boneOMA, Space ownerSpace, Space targetSpace, Ipo influenceIpo, DataRepository dataRepository) throws BlenderFileException {
if(influenceFunction == null) {
throw new IllegalArgumentException("Influence function is not defined!");
}
Pointer pData = (Pointer)constraintStructure.getFieldValue("data");
if(!pData.isNull()) {
data = pData.fetchData(dataRepository.getInputStream()).get(0);
} else {
throw new BlenderFileException("The constraint has no data specified!");
}
this.boneOMA = boneOMA;
this.type = ConstraintType.valueOf(((Number)constraintStructure.getFieldValue("type")).intValue());
this.name = constraintStructure.getFieldValue("name").toString();
this.ownerSpace = ownerSpace;
this.targetSpace = targetSpace;
this.ipo = influenceIpo;
this.influenceFunction = influenceFunction;
}
/** /**
* This method returns the name of the constraint. * This method returns the old memoty address of the bone this constraint affects.
* @return the name of the constraint * @return the old memory address of the bone this constraint affects
*/ */
public String getName() { public Long getBoneOMA() {
return name; return boneOMA;
} }
/** /**
* This method returns the old memoty address of the bone this constraint affects. * This method returns owner's transform space.
* @return the old memory address of the bone this constraint affects * @return owner's transform space
*/ */
public Long getBoneOMA() { public Space getOwnerSpace() {
return boneOMA; return ownerSpace;
} }
/** /**
* This method returns owner's transform space. * This method returns target's transform space.
* @return owner's transform space * @return target's transform space
*/ */
public Space getOwnerSpace() { public Space getTargetSpace() {
return ownerSpace; return targetSpace;
} }
/** /**
* This method returns target's transform space. * This method returns the type of the constraint.
* @return target's transform space * @return the type of the constraint
*/ */
public Space getTargetSpace() { public ConstraintType getType() {
return targetSpace; return type;
} }
/** /**
* This method returns the type of the constraint. * This method returns the constraint's data structure.
* @return the type of the constraint * @return the constraint's data structure
*/ */
public ConstraintType getType() { public Structure getData() {
return type; return data;
} }
/** /**
* This method returns the constraint's data structure. * This method returns the constraint's influcence curve.
* @return the constraint's data structure * @return the constraint's influcence curve
*/ */
public Structure getData() { public Ipo getIpo() {
return data; return ipo;
} }
/** /**
* This method returns the constraint's influcence curve. * This method affects the bone animation tracks for the given skeleton.
* @return the constraint's influcence curve * @param skeleton
*/ * the skeleton containing the affected bones by constraint
public Ipo getIpo() { * @param boneAnimation
return ipo; * the bone animation baked traces
} * @param constraint
* the constraint
*/
public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation) {
influenceFunction.affectAnimation(skeleton, boneAnimation, this);
}
/** /**
* This method affects the bone animation tracks for the given skeleton. * The space of target or owner transformation.
* @param skeleton * @author Marcin Roguski
* the skeleton containing the affected bones by constraint */
* @param boneAnimation public static enum Space {
* the bone animation baked traces
* @param constraint
* the constraint
*/
public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation) {
influenceFunction.affectAnimation(skeleton, boneAnimation, this);
}
/** CONSTRAINT_SPACE_WORLD, CONSTRAINT_SPACE_LOCAL, CONSTRAINT_SPACE_POSE, CONSTRAINT_SPACE_PARLOCAL, CONSTRAINT_SPACE_INVALID;
* The space of target or owner transformation.
* @author Marcin Roguski
*/
public static enum Space {
CONSTRAINT_SPACE_WORLD, CONSTRAINT_SPACE_LOCAL, CONSTRAINT_SPACE_POSE, CONSTRAINT_SPACE_PARLOCAL, CONSTRAINT_SPACE_INVALID;
/** /**
* This method returns the enum instance when given the appropriate value from the blend file. * This method returns the enum instance when given the appropriate value from the blend file.
* @param c * @param c
* the blender's value of the space modifier * the blender's value of the space modifier
* @return the scape enum instance * @return the scape enum instance
*/ */
public static Space valueOf(byte c) { public static Space valueOf(byte c) {
switch(c) { switch (c) {
case 0: case 0:
return CONSTRAINT_SPACE_WORLD; return CONSTRAINT_SPACE_WORLD;
case 1: case 1:
return CONSTRAINT_SPACE_LOCAL; return CONSTRAINT_SPACE_LOCAL;
case 2: case 2:
return CONSTRAINT_SPACE_POSE; return CONSTRAINT_SPACE_POSE;
case 3: case 3:
return CONSTRAINT_SPACE_PARLOCAL; return CONSTRAINT_SPACE_PARLOCAL;
default: default:
return CONSTRAINT_SPACE_INVALID; return CONSTRAINT_SPACE_INVALID;
} }
} }
} }
} }

@ -10,134 +10,136 @@ import java.util.Map;
* @author Marcin Roguski * @author Marcin Roguski
*/ */
public enum ConstraintType { public enum ConstraintType {
/* Invalid/legacy constraint */ /* Invalid/legacy constraint */
CONSTRAINT_TYPE_NULL(0, "bNullConstraint"),
/* Unimplemented non longer :) - during constraints recode, Aligorith */
CONSTRAINT_TYPE_CHILDOF(1, "bChildOfConstraint"),
CONSTRAINT_TYPE_KINEMATIC(3, "bKinematicConstraint"),
CONSTRAINT_TYPE_FOLLOWPATH(4, "bFollowPathConstraint"),
/* Unimplemented no longer :) - Aligorith */
CONSTRAINT_TYPE_ROTLIMIT(5, "bRotLimitConstraint"),
/* Unimplemented no longer :) - Aligorith */
CONSTRAINT_TYPE_LOCLIMIT(6, "bLocLimitConstraint"),
/* Unimplemented no longer :) - Aligorith */
CONSTRAINT_TYPE_SIZELIMIT(7, "bSizeLimitConstraint"),
CONSTRAINT_TYPE_ROTLIKE(8, "bRotateLikeConstraint"),
CONSTRAINT_TYPE_LOCLIKE(9, "bLocateLikeConstraint"),
CONSTRAINT_TYPE_SIZELIKE(10, "bSizeLikeConstraint"),
/* Unimplemented no longer :) - Aligorith. Scripts */
CONSTRAINT_TYPE_PYTHON(11, "bPythonConstraint"),
CONSTRAINT_TYPE_ACTION(12, "bActionConstraint"),
/* New Tracking constraint that locks an axis in place - theeth */
CONSTRAINT_TYPE_LOCKTRACK(13, "bLockTrackConstraint"),
/* limit distance */
CONSTRAINT_TYPE_DISTLIMIT(14, "bDistLimitConstraint"),
/* claiming this to be mine :) is in tuhopuu bjornmose */
CONSTRAINT_TYPE_STRETCHTO(15, "bStretchToConstraint"),
/* floor constraint */
CONSTRAINT_TYPE_MINMAX(16, "bMinMaxConstraint"),
/* rigidbody constraint */
CONSTRAINT_TYPE_RIGIDBODYJOINT(17, "bRigidBodyConstraint"),
/* clampto constraint */
CONSTRAINT_TYPE_CLAMPTO(18, "bClampToConstraint"),
/* transformation (loc/rot/size -> loc/rot/size) constraint */
CONSTRAINT_TYPE_TRANSFORM(19, "bTransformConstraint"),
/* shrinkwrap (loc/rot) constraint */
CONSTRAINT_TYPE_SHRINKWRAP(20, "bShrinkwrapConstraint");
/** The constraint's id (in blender known as 'type'). */ CONSTRAINT_TYPE_NULL(0, "bNullConstraint"),
private int constraintId; /* Unimplemented non longer :) - during constraints recode, Aligorith */
/** The name of constraint class used by blender. */ CONSTRAINT_TYPE_CHILDOF(1, "bChildOfConstraint"),
private String className; CONSTRAINT_TYPE_KINEMATIC(3, "bKinematicConstraint"),
/** The map containing class names and types of constraints. */ CONSTRAINT_TYPE_FOLLOWPATH(4, "bFollowPathConstraint"),
private static Map<String, ConstraintType> typesMap = new HashMap<String, ConstraintType>(ConstraintType.values().length); /* Unimplemented no longer :) - Aligorith */
/** The map containing class names and types of constraints. */ CONSTRAINT_TYPE_ROTLIMIT(5, "bRotLimitConstraint"),
private static Map<Integer, ConstraintType> idsMap = new HashMap<Integer, ConstraintType>(ConstraintType.values().length); /* Unimplemented no longer :) - Aligorith */
static { CONSTRAINT_TYPE_LOCLIMIT(6, "bLocLimitConstraint"),
idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_NULL.constraintId), CONSTRAINT_TYPE_NULL); /* Unimplemented no longer :) - Aligorith */
idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_CHILDOF.constraintId), CONSTRAINT_TYPE_CHILDOF); CONSTRAINT_TYPE_SIZELIMIT(7, "bSizeLimitConstraint"),
idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_KINEMATIC.constraintId), CONSTRAINT_TYPE_KINEMATIC); CONSTRAINT_TYPE_ROTLIKE(8, "bRotateLikeConstraint"),
idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_FOLLOWPATH.constraintId), CONSTRAINT_TYPE_FOLLOWPATH); CONSTRAINT_TYPE_LOCLIKE(9, "bLocateLikeConstraint"),
idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_ROTLIMIT.constraintId), CONSTRAINT_TYPE_ROTLIMIT); CONSTRAINT_TYPE_SIZELIKE(10, "bSizeLikeConstraint"),
idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_LOCLIMIT.constraintId), CONSTRAINT_TYPE_LOCLIMIT); /* Unimplemented no longer :) - Aligorith. Scripts */
idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_SIZELIMIT.constraintId), CONSTRAINT_TYPE_SIZELIMIT); CONSTRAINT_TYPE_PYTHON(11, "bPythonConstraint"),
idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_ROTLIKE.constraintId), CONSTRAINT_TYPE_ROTLIKE); CONSTRAINT_TYPE_ACTION(12, "bActionConstraint"),
idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_LOCLIKE.constraintId), CONSTRAINT_TYPE_LOCLIKE); /* New Tracking constraint that locks an axis in place - theeth */
idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_SIZELIKE.constraintId), CONSTRAINT_TYPE_SIZELIKE); CONSTRAINT_TYPE_LOCKTRACK(13, "bLockTrackConstraint"),
idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_PYTHON.constraintId), CONSTRAINT_TYPE_PYTHON); /* limit distance */
idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_ACTION.constraintId), CONSTRAINT_TYPE_ACTION); CONSTRAINT_TYPE_DISTLIMIT(14, "bDistLimitConstraint"),
idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_LOCKTRACK.constraintId), CONSTRAINT_TYPE_LOCKTRACK); /* claiming this to be mine :) is in tuhopuu bjornmose */
idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_DISTLIMIT.constraintId), CONSTRAINT_TYPE_DISTLIMIT); CONSTRAINT_TYPE_STRETCHTO(15, "bStretchToConstraint"),
idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_STRETCHTO.constraintId), CONSTRAINT_TYPE_STRETCHTO); /* floor constraint */
idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_MINMAX.constraintId), CONSTRAINT_TYPE_MINMAX); CONSTRAINT_TYPE_MINMAX(16, "bMinMaxConstraint"),
idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_RIGIDBODYJOINT.constraintId), CONSTRAINT_TYPE_RIGIDBODYJOINT); /* rigidbody constraint */
idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_CLAMPTO.constraintId), CONSTRAINT_TYPE_CLAMPTO); CONSTRAINT_TYPE_RIGIDBODYJOINT(17, "bRigidBodyConstraint"),
idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_TRANSFORM.constraintId), CONSTRAINT_TYPE_TRANSFORM); /* clampto constraint */
idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_SHRINKWRAP.constraintId), CONSTRAINT_TYPE_SHRINKWRAP); CONSTRAINT_TYPE_CLAMPTO(18, "bClampToConstraint"),
} /* transformation (loc/rot/size -> loc/rot/size) constraint */
/** CONSTRAINT_TYPE_TRANSFORM(19, "bTransformConstraint"),
* Constructor. Stores constraint type and class name. /* shrinkwrap (loc/rot) constraint */
* @param constraintId CONSTRAINT_TYPE_SHRINKWRAP(20, "bShrinkwrapConstraint");
* the constraint's type /** The constraint's id (in blender known as 'type'). */
* @param className private int constraintId;
* the constraint's type name /** The name of constraint class used by blender. */
*/ private String className;
private ConstraintType(int constraintId, String className) { /** The map containing class names and types of constraints. */
this.constraintId = constraintId; private static final Map<String, ConstraintType> typesMap = new HashMap<String, ConstraintType>(ConstraintType.values().length);
this.className = className; /** The map containing class names and types of constraints. */
} private static final Map<Integer, ConstraintType> idsMap = new HashMap<Integer, ConstraintType>(ConstraintType.values().length);
/** static {
* This method returns the type by given constraint id. idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_NULL.constraintId), CONSTRAINT_TYPE_NULL);
* @param constraintId idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_CHILDOF.constraintId), CONSTRAINT_TYPE_CHILDOF);
* the id of the constraint idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_KINEMATIC.constraintId), CONSTRAINT_TYPE_KINEMATIC);
* @return the constraint type enum value idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_FOLLOWPATH.constraintId), CONSTRAINT_TYPE_FOLLOWPATH);
*/ idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_ROTLIMIT.constraintId), CONSTRAINT_TYPE_ROTLIMIT);
public static ConstraintType valueOf(int constraintId) { idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_LOCLIMIT.constraintId), CONSTRAINT_TYPE_LOCLIMIT);
return idsMap.get(Integer.valueOf(constraintId)); idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_SIZELIMIT.constraintId), CONSTRAINT_TYPE_SIZELIMIT);
} idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_ROTLIKE.constraintId), CONSTRAINT_TYPE_ROTLIKE);
idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_LOCLIKE.constraintId), CONSTRAINT_TYPE_LOCLIKE);
idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_SIZELIKE.constraintId), CONSTRAINT_TYPE_SIZELIKE);
idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_PYTHON.constraintId), CONSTRAINT_TYPE_PYTHON);
idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_ACTION.constraintId), CONSTRAINT_TYPE_ACTION);
idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_LOCKTRACK.constraintId), CONSTRAINT_TYPE_LOCKTRACK);
idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_DISTLIMIT.constraintId), CONSTRAINT_TYPE_DISTLIMIT);
idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_STRETCHTO.constraintId), CONSTRAINT_TYPE_STRETCHTO);
idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_MINMAX.constraintId), CONSTRAINT_TYPE_MINMAX);
idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_RIGIDBODYJOINT.constraintId), CONSTRAINT_TYPE_RIGIDBODYJOINT);
idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_CLAMPTO.constraintId), CONSTRAINT_TYPE_CLAMPTO);
idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_TRANSFORM.constraintId), CONSTRAINT_TYPE_TRANSFORM);
idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_SHRINKWRAP.constraintId), CONSTRAINT_TYPE_SHRINKWRAP);
}
/** /**
* This method returns the constraint's id (type). * Constructor. Stores constraint type and class name.
* @return the constraint's id (type) * @param constraintId
*/ * the constraint's type
public int getConstraintId() { * @param className
return constraintId; * the constraint's type name
} */
private ConstraintType(int constraintId, String className) {
this.constraintId = constraintId;
this.className = className;
}
/** /**
* This method returns the constraint's class name. * This method returns the type by given constraint id.
* @return the constraint's class name * @param constraintId
*/ * the id of the constraint
public String getClassName() { * @return the constraint type enum value
return className; */
} public static ConstraintType valueOf(int constraintId) {
return idsMap.get(Integer.valueOf(constraintId));
}
/** /**
* This method returns constraint enum type by the given class name. * This method returns the constraint's id (type).
* @param className * @return the constraint's id (type)
* the blender's constraint class name */
* @return the constraint enum type of the specified class name public int getConstraintId() {
*/ return constraintId;
public static ConstraintType getByBlenderClassName(String className) { }
ConstraintType result = typesMap.get(className);
if(result == null) {
ConstraintType[] constraints = ConstraintType.values();
for(ConstraintType constraint : constraints) {
if(constraint.className.equals(className)) {
return constraint;
}
}
}
return result;
}
/** /**
* This method returns the type value of the last defined constraint. It can be used for allocating tables for * This method returns the constraint's class name.
* storing constraint procedures since not all type values from 0 to the last value are used. * @return the constraint's class name
* @return the type value of the last defined constraint */
*/ public String getClassName() {
public static int getLastDefinedTypeValue() { return className;
return CONSTRAINT_TYPE_SHRINKWRAP.getConstraintId(); }
}
/**
* This method returns constraint enum type by the given class name.
* @param className
* the blender's constraint class name
* @return the constraint enum type of the specified class name
*/
public static ConstraintType getByBlenderClassName(String className) {
ConstraintType result = typesMap.get(className);
if (result == null) {
ConstraintType[] constraints = ConstraintType.values();
for (ConstraintType constraint : constraints) {
if (constraint.className.equals(className)) {
return constraint;
}
}
}
return result;
}
/**
* This method returns the type value of the last defined constraint. It can be used for allocating tables for
* storing constraint procedures since not all type values from 0 to the last value are used.
* @return the type value of the last defined constraint
*/
public static int getLastDefinedTypeValue() {
return CONSTRAINT_TYPE_SHRINKWRAP.getConstraintId();
}
} }

@ -10,167 +10,167 @@ import com.jme3.math.Vector3f;
* @author Marcin Roguski * @author Marcin Roguski
*/ */
public class Ipo { public class Ipo {
public static final int AC_LOC_X = 1;
public static final int AC_LOC_Y = 2;
public static final int AC_LOC_Z = 3;
public static final int OB_ROT_X = 7;
public static final int OB_ROT_Y = 8;
public static final int OB_ROT_Z = 9;
public static final int AC_SIZE_X = 13;
public static final int AC_SIZE_Y = 14;
public static final int AC_SIZE_Z = 15;
public static final int AC_QUAT_W = 25;
public static final int AC_QUAT_X = 26;
public static final int AC_QUAT_Y = 27;
public static final int AC_QUAT_Z = 28;
/** A list of bezier curves for this interpolation object. */ public static final int AC_LOC_X = 1;
private BezierCurve[] bezierCurves; public static final int AC_LOC_Y = 2;
/** Each ipo contains one bone track. */ public static final int AC_LOC_Z = 3;
private BoneTrack calculatedTrack; public static final int OB_ROT_X = 7;
public static final int OB_ROT_Y = 8;
public static final int OB_ROT_Z = 9;
public static final int AC_SIZE_X = 13;
public static final int AC_SIZE_Y = 14;
public static final int AC_SIZE_Z = 15;
public static final int AC_QUAT_W = 25;
public static final int AC_QUAT_X = 26;
public static final int AC_QUAT_Y = 27;
public static final int AC_QUAT_Z = 28;
/** A list of bezier curves for this interpolation object. */
private BezierCurve[] bezierCurves;
/** Each ipo contains one bone track. */
private BoneTrack calculatedTrack;
/** /**
* Constructor. Stores the bezier curves. * Constructor. Stores the bezier curves.
* @param bezierCurves * @param bezierCurves
* a table of bezier curves * a table of bezier curves
*/ */
public Ipo(BezierCurve[] bezierCurves) { public Ipo(BezierCurve[] bezierCurves) {
this.bezierCurves = bezierCurves; this.bezierCurves = bezierCurves;
} }
/** /**
* This method calculates the ipo value for the first curve. * This method calculates the ipo value for the first curve.
* @param frame * @param frame
* the frame for which the value is calculated * the frame for which the value is calculated
* @return calculated ipo value * @return calculated ipo value
*/ */
public float calculateValue(int frame) { public float calculateValue(int frame) {
return this.calculateValue(frame, 0); return this.calculateValue(frame, 0);
} }
/** /**
* This method calculates the ipo value for the curve of the specified index. Make sure you do not exceed the * This method calculates the ipo value for the curve of the specified index. Make sure you do not exceed the
* curves amount. Alway chech the amount of curves before calling this method. * curves amount. Alway chech the amount of curves before calling this method.
* @param frame * @param frame
* the frame for which the value is calculated * the frame for which the value is calculated
* @param curveIndex * @param curveIndex
* the index of the curve * the index of the curve
* @return calculated ipo value * @return calculated ipo value
*/ */
public float calculateValue(int frame, int curveIndex) { public float calculateValue(int frame, int curveIndex) {
return bezierCurves[curveIndex].evaluate(frame, BezierCurve.Y_VALUE); return bezierCurves[curveIndex].evaluate(frame, BezierCurve.Y_VALUE);
} }
/** /**
* This method returns the curves amount. * This method returns the curves amount.
* @return the curves amount * @return the curves amount
*/ */
public int getCurvesAmount() { public int getCurvesAmount() {
return bezierCurves.length; return bezierCurves.length;
} }
/** /**
* This method returns the frame where last bezier triple center point of the specified bezier curve is located. * This method returns the frame where last bezier triple center point of the specified bezier curve is located.
* @return the frame number of the last defined bezier triple point for the specified ipo * @return the frame number of the last defined bezier triple point for the specified ipo
*/ */
public int getLastFrame() { public int getLastFrame() {
int result = 1; int result = 1;
for(int i = 0; i < bezierCurves.length; ++i) { for (int i = 0; i < bezierCurves.length; ++i) {
int tempResult = bezierCurves[i].getLastFrame(); int tempResult = bezierCurves[i].getLastFrame();
if(tempResult > result) { if (tempResult > result) {
result = tempResult; result = tempResult;
} }
} }
return result; return result;
} }
public void modifyTranslation(int frame, Vector3f translation) { public void modifyTranslation(int frame, Vector3f translation) {
if(calculatedTrack!=null) { if (calculatedTrack != null) {
calculatedTrack.getTranslations()[frame].set(translation); calculatedTrack.getTranslations()[frame].set(translation);
} }
} }
public void modifyRotation(int frame, Quaternion rotation) { public void modifyRotation(int frame, Quaternion rotation) {
if(calculatedTrack!=null) { if (calculatedTrack != null) {
calculatedTrack.getRotations()[frame].set(rotation); calculatedTrack.getRotations()[frame].set(rotation);
} }
} }
public void modifyScale(int frame, Vector3f scale) { public void modifyScale(int frame, Vector3f scale) {
if(calculatedTrack!=null) { if (calculatedTrack != null) {
calculatedTrack.getScales()[frame].set(scale); calculatedTrack.getScales()[frame].set(scale);
} }
} }
/** /**
* This method calculates the value of the curves as a bone track between the specified frames. * This method calculates the value of the curves as a bone track between the specified frames.
* @param boneIndex * @param boneIndex
* the index of the bone for which the method calculates the tracks * the index of the bone for which the method calculates the tracks
* @param startFrame * @param startFrame
* the firs frame of tracks (inclusive) * the firs frame of tracks (inclusive)
* @param stopFrame * @param stopFrame
* the last frame of the tracks (inclusive) * the last frame of the tracks (inclusive)
* @param fps * @param fps
* frame rate (frames per second) * frame rate (frames per second)
* @return bone track for the specified bone * @return bone track for the specified bone
*/ */
public BoneTrack calculateTrack(int boneIndex, int startFrame, int stopFrame, int fps) { public BoneTrack calculateTrack(int boneIndex, int startFrame, int stopFrame, int fps) {
//preparing data for track //preparing data for track
int framesAmount = stopFrame - startFrame; int framesAmount = stopFrame - startFrame;
float start = (startFrame - 1.0f) / fps; float start = (startFrame - 1.0f) / fps;
float timeBetweenFrames = 1.0f / fps; float timeBetweenFrames = 1.0f / fps;
float[] times = new float[framesAmount + 1]; float[] times = new float[framesAmount + 1];
Vector3f[] translations = new Vector3f[framesAmount + 1]; Vector3f[] translations = new Vector3f[framesAmount + 1];
float[] translation = new float[3]; float[] translation = new float[3];
Quaternion[] rotations = new Quaternion[framesAmount + 1]; Quaternion[] rotations = new Quaternion[framesAmount + 1];
float[] quaternionRotation = new float[4]; float[] quaternionRotation = new float[4];
float[] objectRotation = new float[3]; float[] objectRotation = new float[3];
boolean bObjectRotation = false; boolean bObjectRotation = false;
Vector3f[] scales = new Vector3f[framesAmount + 1]; Vector3f[] scales = new Vector3f[framesAmount + 1];
float[] scale = new float[3]; float[] scale = new float[3];
//calculating track data //calculating track data
for(int frame = startFrame; frame <= stopFrame; ++frame) { for (int frame = startFrame; frame <= stopFrame; ++frame) {
int index = frame - startFrame; int index = frame - startFrame;
times[index] = start + (frame - 1) * timeBetweenFrames; times[index] = start + (frame - 1) * timeBetweenFrames;
for(int j = 0; j < bezierCurves.length; ++j) { for (int j = 0; j < bezierCurves.length; ++j) {
double value = bezierCurves[j].evaluate(frame, BezierCurve.Y_VALUE); double value = bezierCurves[j].evaluate(frame, BezierCurve.Y_VALUE);
switch(bezierCurves[j].getType()) { switch (bezierCurves[j].getType()) {
case AC_LOC_X: case AC_LOC_X:
case AC_LOC_Y: case AC_LOC_Y:
case AC_LOC_Z: case AC_LOC_Z:
translation[bezierCurves[j].getType() - 1] = (float)value; translation[bezierCurves[j].getType() - 1] = (float) value;
break; break;
case OB_ROT_X: case OB_ROT_X:
case OB_ROT_Y: case OB_ROT_Y:
case OB_ROT_Z: case OB_ROT_Z:
objectRotation[bezierCurves[j].getType() - 7] = (float)value; objectRotation[bezierCurves[j].getType() - 7] = (float) value;
bObjectRotation = true; bObjectRotation = true;
break; break;
case AC_SIZE_X: case AC_SIZE_X:
case AC_SIZE_Y: case AC_SIZE_Y:
case AC_SIZE_Z: case AC_SIZE_Z:
scale[bezierCurves[j].getType() - 13] = (float)value; scale[bezierCurves[j].getType() - 13] = (float) value;
break; break;
case AC_QUAT_W: case AC_QUAT_W:
quaternionRotation[3] = (float)value; quaternionRotation[3] = (float) value;
break; break;
case AC_QUAT_X: case AC_QUAT_X:
case AC_QUAT_Y: case AC_QUAT_Y:
case AC_QUAT_Z: case AC_QUAT_Z:
quaternionRotation[bezierCurves[j].getType() - 26] = (float)value; quaternionRotation[bezierCurves[j].getType() - 26] = (float) value;
break; break;
default: default:
//TODO: error? info? warning? //TODO: error? info? warning?
} }
} }
translations[index] = new Vector3f(translation[0], translation[1], translation[2]); translations[index] = new Vector3f(translation[0], translation[1], translation[2]);
rotations[index] = bObjectRotation ? new Quaternion().fromAngles(objectRotation) : rotations[index] = bObjectRotation ? new Quaternion().fromAngles(objectRotation)
new Quaternion(quaternionRotation[0], quaternionRotation[1], quaternionRotation[2], quaternionRotation[3]); : new Quaternion(quaternionRotation[0], quaternionRotation[1], quaternionRotation[2], quaternionRotation[3]);
scales[index] = new Vector3f(scale[0], scale[1], scale[2]); scales[index] = new Vector3f(scale[0], scale[1], scale[2]);
} }
calculatedTrack = new BoneTrack(boneIndex, times, translations, rotations, scales); calculatedTrack = new BoneTrack(boneIndex, times, translations, rotations, scales);
return calculatedTrack; return calculatedTrack;
} }
} }

@ -7,50 +7,51 @@ package com.jme3.scene.plugins.blender.structures;
* @author Marcin Roguski (Kaelthas) * @author Marcin Roguski (Kaelthas)
*/ */
public class Modifier { public class Modifier {
public static final String ARRAY_MODIFIER_DATA = "ArrayModifierData";
public static final String ARMATURE_MODIFIER_DATA = "ArmatureModifierData";
public static final String PARTICLE_MODIFIER_DATA = "ParticleSystemModifierData";
/** Blender's type of modifier. */ public static final String ARRAY_MODIFIER_DATA = "ArrayModifierData";
private String type; public static final String ARMATURE_MODIFIER_DATA = "ArmatureModifierData";
/** JME modifier representation object. */ public static final String PARTICLE_MODIFIER_DATA = "ParticleSystemModifierData";
private Object jmeModifierRepresentation; /** Blender's type of modifier. */
/** Various additional data used by modifiers.*/ private String type;
private Object additionalData; /** JME modifier representation object. */
/** private Object jmeModifierRepresentation;
* Constructor. Creates modifier object. /** Various additional data used by modifiers.*/
* @param type private Object additionalData;
* blender's type of modifier
* @param modifier
* JME modifier representation object
*/
public Modifier(String type, Object modifier, Object additionalData) {
this.type = type;
this.jmeModifierRepresentation = modifier;
this.additionalData = additionalData;
}
/** /**
* This method returns JME modifier representation object. * Constructor. Creates modifier object.
* @return JME modifier representation object * @param type
*/ * blender's type of modifier
public Object getJmeModifierRepresentation() { * @param modifier
return jmeModifierRepresentation; * JME modifier representation object
} */
public Modifier(String type, Object modifier, Object additionalData) {
this.type = type;
this.jmeModifierRepresentation = modifier;
this.additionalData = additionalData;
}
/** /**
* This method returns blender's type of modifier. * This method returns JME modifier representation object.
* @return blender's type of modifier * @return JME modifier representation object
*/ */
public String getType() { public Object getJmeModifierRepresentation() {
return type; return jmeModifierRepresentation;
} }
/** /**
* This method returns additional data stored in the modifier. * This method returns blender's type of modifier.
* @return the additional data stored in the modifier * @return blender's type of modifier
*/ */
public Object getAdditionalData() { public String getType() {
return additionalData; return type;
} }
/**
* This method returns additional data stored in the modifier.
* @return the additional data stored in the modifier
*/
public Object getAdditionalData() {
return additionalData;
}
} }

@ -42,61 +42,63 @@ import com.jme3.util.BufferUtils;
* @author Marcin Roguski * @author Marcin Roguski
*/ */
public abstract class AbstractBlenderHelper { public abstract class AbstractBlenderHelper {
/** The version of the blend file. */
protected final int blenderVersion;
/** /** The version of the blend file. */
* This constructor parses the given blender version and stores the result. Some functionalities may differ in different blender protected final int blenderVersion;
* versions.
* @param blenderVersion
* the version read from the blend file
*/
public AbstractBlenderHelper(String blenderVersion) {
this.blenderVersion = Integer.parseInt(blenderVersion);
}
/** /**
* This method clears the state of the helper so that it can be used for different calculations of another feature. * This constructor parses the given blender version and stores the result. Some functionalities may differ in different blender
*/ * versions.
public void clearState() { } * @param blenderVersion
* the version read from the blend file
*/
public AbstractBlenderHelper(String blenderVersion) {
this.blenderVersion = Integer.parseInt(blenderVersion);
}
/** /**
* This method should be used to check if the text is blank. Avoid using text.trim().length()==0. This causes that more strings are * This method clears the state of the helper so that it can be used for different calculations of another feature.
* being created and stored in the memory. It can be unwise especially inside loops. */
* @param text public void clearState() {
* the text to be checked }
* @return <b>true</b> if the text is blank and <b>false</b> otherwise
*/
protected boolean isBlank(String text) {
if (text != null) {
for (int i = 0; i < text.length(); ++i) {
if (!Character.isWhitespace(text.charAt(i))) {
return false;
}
}
}
return true;
}
/** /**
* Generate a new FloatBuffer using the given array of float[4] objects. The FloatBuffer will be 4 * data.length * This method should be used to check if the text is blank. Avoid using text.trim().length()==0. This causes that more strings are
* long and contain the vector data as data[0][0], data[0][1], data[0][2], data[0][3], data[1][0]... etc. * being created and stored in the memory. It can be unwise especially inside loops.
* @param data * @param text
* list of float[4] objects to place into a new FloatBuffer * the text to be checked
*/ * @return <b>true</b> if the text is blank and <b>false</b> otherwise
protected FloatBuffer createFloatBuffer(List<float[]> data) { */
if(data == null) { protected boolean isBlank(String text) {
return null; if (text != null) {
} for (int i = 0; i < text.length(); ++i) {
FloatBuffer buff = BufferUtils.createFloatBuffer(4 * data.size()); if (!Character.isWhitespace(text.charAt(i))) {
for(float[] v : data) { return false;
if(v != null) { }
buff.put(v[0]).put(v[1]).put(v[2]).put(v[3]); }
} else { }
buff.put(0).put(0).put(0).put(0); return true;
} }
}
buff.flip(); /**
return buff; * Generate a new FloatBuffer using the given array of float[4] objects. The FloatBuffer will be 4 * data.length
} * long and contain the vector data as data[0][0], data[0][1], data[0][2], data[0][3], data[1][0]... etc.
* @param data
* list of float[4] objects to place into a new FloatBuffer
*/
protected FloatBuffer createFloatBuffer(List<float[]> data) {
if (data == null) {
return null;
}
FloatBuffer buff = BufferUtils.createFloatBuffer(4 * data.size());
for (float[] v : data) {
if (v != null) {
buff.put(v[0]).put(v[1]).put(v[2]).put(v[3]);
} else {
buff.put(0).put(0).put(0).put(0);
}
}
buff.flip();
return buff;
}
} }

@ -52,58 +52,59 @@ import com.jme3.scene.plugins.blender.exception.BlenderFileException;
* the type of material element * the type of material element
*/ */
//TODO: ujednolicić wyrzucane wyjątki //TODO: ujednolicić wyrzucane wyjątki
public interface IBlenderConverter<NodeType, CameraType, LightType, ObjectType, MeshType, MaterialType> { public interface BlenderConverter<NodeType, CameraType, LightType, ObjectType, MeshType, MaterialType> {
/**
* This method reads converts the given structure into scene. The given structure needs to be filled with the
* appropriate data.
* @param structure
* the structure we read the scene from
* @return the scene feature
*/
NodeType toScene(Structure structure);
/** /**
* This method reads converts the given structure into camera. The given structure needs to be filled with the * This method reads converts the given structure into scene. The given structure needs to be filled with the
* appropriate data. * appropriate data.
* @param structure * @param structure
* the structure we read the camera from * the structure we read the scene from
* @return the camera feature * @return the scene feature
*/ */
CameraType toCamera(Structure structure) throws BlenderFileException; NodeType toScene(Structure structure);
/** /**
* This method reads converts the given structure into light. The given structure needs to be filled with the * This method reads converts the given structure into camera. The given structure needs to be filled with the
* appropriate data. * appropriate data.
* @param structure * @param structure
* the structure we read the light from * the structure we read the camera from
* @return the light feature * @return the camera feature
*/ */
LightType toLight(Structure structure) throws BlenderFileException; CameraType toCamera(Structure structure) throws BlenderFileException;
/** /**
* This method reads converts the given structure into objct. The given structure needs to be filled with the * This method reads converts the given structure into light. The given structure needs to be filled with the
* appropriate data. * appropriate data.
* @param structure * @param structure
* the structure we read the object from * the structure we read the light from
* @return the object feature * @return the light feature
*/ */
ObjectType toObject(Structure structure) throws BlenderFileException; LightType toLight(Structure structure) throws BlenderFileException;
/** /**
* This method reads converts the given structure into mesh. The given structure needs to be filled with the * This method reads converts the given structure into objct. The given structure needs to be filled with the
* appropriate data. * appropriate data.
* @param structure * @param structure
* the structure we read the mesh from * the structure we read the object from
* @return the mesh feature * @return the object feature
*/ */
MeshType toMesh(Structure structure) throws BlenderFileException; ObjectType toObject(Structure structure) throws BlenderFileException;
/** /**
* This method reads converts the given structure into material. The given structure needs to be filled with the * This method reads converts the given structure into mesh. The given structure needs to be filled with the
* appropriate data. * appropriate data.
* @param structure * @param structure
* the structure we read the material from * the structure we read the mesh from
* @return the material feature * @return the mesh feature
*/ */
MaterialType toMaterial(Structure structure) throws BlenderFileException; MeshType toMesh(Structure structure) throws BlenderFileException;
/**
* This method reads converts the given structure into material. The given structure needs to be filled with the
* appropriate data.
* @param structure
* the structure we read the material from
* @return the material feature
*/
MaterialType toMaterial(Structure structure) throws BlenderFileException;
} }

@ -46,337 +46,337 @@ import com.jme3.scene.plugins.blender.exception.BlenderFileException;
* @author Marcin Roguski * @author Marcin Roguski
*/ */
public class BlenderInputStream extends InputStream { public class BlenderInputStream extends InputStream {
private static final Logger LOGGER = Logger.getLogger(BlenderInputStream.class.getName());
/** The default size of the blender buffer. */ private static final Logger LOGGER = Logger.getLogger(BlenderInputStream.class.getName());
private static final int DEFAULT_BUFFER_SIZE = 1048576; //1MB /** The default size of the blender buffer. */
/** The application's asset manager. */ private static final int DEFAULT_BUFFER_SIZE = 1048576; //1MB
private AssetManager assetManager; /** The application's asset manager. */
/** private AssetManager assetManager;
* Size of a pointer; all pointers in the file are stored in this format. '_' means 4 bytes and '-' means 8 bytes. /**
*/ * Size of a pointer; all pointers in the file are stored in this format. '_' means 4 bytes and '-' means 8 bytes.
private int pointerSize; */
/** private int pointerSize;
* Type of byte ordering used; 'v' means little endian and 'V' means big endian. /**
*/ * Type of byte ordering used; 'v' means little endian and 'V' means big endian.
private char endianess; */
/** Version of Blender the file was created in; '248' means version 2.48. */ private char endianess;
private String versionNumber; /** Version of Blender the file was created in; '248' means version 2.48. */
/** The buffer we store the read data to. */ private String versionNumber;
protected byte[] cachedBuffer; /** The buffer we store the read data to. */
/** The total size of the stored data. */ protected byte[] cachedBuffer;
protected int size; /** The total size of the stored data. */
/** The current position of the read cursor. */ protected int size;
protected int position; /** The current position of the read cursor. */
protected int position;
/** /**
* Constructor. The input stream is stored and used to read data. * Constructor. The input stream is stored and used to read data.
* @param inputStream * @param inputStream
* the stream we read data from * the stream we read data from
* @param assetManager * @param assetManager
* the application's asset manager * the application's asset manager
* @param endianess * @param endianess
* type of byte ordering used; 'v' means little endian and 'V' means big endian * type of byte ordering used; 'v' means little endian and 'V' means big endian
* @throws BlenderFileException * @throws BlenderFileException
* this exception is thrown if the file header has some invalid data * this exception is thrown if the file header has some invalid data
*/ */
public BlenderInputStream(InputStream inputStream, AssetManager assetManager) throws BlenderFileException { public BlenderInputStream(InputStream inputStream, AssetManager assetManager) throws BlenderFileException {
this.assetManager = assetManager; this.assetManager = assetManager;
//the size value will canche while reading the file; the available() method cannot be counted on //the size value will canche while reading the file; the available() method cannot be counted on
try { try {
size = inputStream.available(); size = inputStream.available();
} catch (IOException e) { } catch (IOException e) {
size = 0; size = 0;
} }
if(size <= 0) { if (size <= 0) {
size = BlenderInputStream.DEFAULT_BUFFER_SIZE; size = BlenderInputStream.DEFAULT_BUFFER_SIZE;
} }
//buffered input stream is used here for much faster file reading //buffered input stream is used here for much faster file reading
BufferedInputStream bufferedInputStream; BufferedInputStream bufferedInputStream;
if(inputStream instanceof BufferedInputStream) { if (inputStream instanceof BufferedInputStream) {
bufferedInputStream = (BufferedInputStream)inputStream; bufferedInputStream = (BufferedInputStream) inputStream;
} else { } else {
bufferedInputStream = new BufferedInputStream(inputStream); bufferedInputStream = new BufferedInputStream(inputStream);
} }
try { try {
this.readStreamToCache(bufferedInputStream); this.readStreamToCache(bufferedInputStream);
} catch (IOException e) { } catch (IOException e) {
throw new BlenderFileException("Problems occured while caching the file!", e); throw new BlenderFileException("Problems occured while caching the file!", e);
} }
try { try {
this.readFileHeader(); this.readFileHeader();
} catch(BlenderFileException e) {//the file might be packed, don't panic, try one more time ;) } catch (BlenderFileException e) {//the file might be packed, don't panic, try one more time ;)
this.decompressFile(); this.decompressFile();
this.position = 0; this.position = 0;
this.readFileHeader(); this.readFileHeader();
} }
} }
/** /**
* This method reads the whole stream into a buffer. * This method reads the whole stream into a buffer.
* @param inputStream * @param inputStream
* the stream to read the file data from * the stream to read the file data from
* @throws IOException * @throws IOException
* an exception is thrown when data read from the stream is invalid or there are problems with i/o * an exception is thrown when data read from the stream is invalid or there are problems with i/o
* operations * operations
*/ */
private void readStreamToCache(InputStream inputStream) throws IOException { private void readStreamToCache(InputStream inputStream) throws IOException {
int data = inputStream.read(); int data = inputStream.read();
cachedBuffer = new byte[size]; cachedBuffer = new byte[size];
size = 0;//this will count the actual size size = 0;//this will count the actual size
while(data != -1) { while (data != -1) {
cachedBuffer[size++] = (byte)data; cachedBuffer[size++] = (byte) data;
if(size >= cachedBuffer.length) {//widen the cached array if (size >= cachedBuffer.length) {//widen the cached array
byte[] newBuffer = new byte[cachedBuffer.length + (cachedBuffer.length >> 1)]; byte[] newBuffer = new byte[cachedBuffer.length + (cachedBuffer.length >> 1)];
System.arraycopy(cachedBuffer, 0, newBuffer, 0, cachedBuffer.length); System.arraycopy(cachedBuffer, 0, newBuffer, 0, cachedBuffer.length);
cachedBuffer = newBuffer; cachedBuffer = newBuffer;
} }
data = inputStream.read(); data = inputStream.read();
} }
} }
/** /**
* This method is used when the blender file is gzipped. It decompresses the data and stores it back into the * This method is used when the blender file is gzipped. It decompresses the data and stores it back into the
* cachedBuffer field. * cachedBuffer field.
*/ */
private void decompressFile() { private void decompressFile() {
GZIPInputStream gis = null; GZIPInputStream gis = null;
try { try {
gis = new GZIPInputStream(new ByteArrayInputStream(cachedBuffer)); gis = new GZIPInputStream(new ByteArrayInputStream(cachedBuffer));
this.readStreamToCache(gis); this.readStreamToCache(gis);
} catch (IOException e) { } catch (IOException e) {
throw new IllegalStateException("IO errors occured where they should NOT! " + throw new IllegalStateException("IO errors occured where they should NOT! "
"The data is already buffered at this point!", e); + "The data is already buffered at this point!", e);
} finally { } finally {
try { try {
if(gis!=null) { if (gis != null) {
gis.close(); gis.close();
} }
} catch(IOException e) { } catch (IOException e) {
LOGGER.warning(e.getMessage()); LOGGER.warning(e.getMessage());
} }
} }
} }
/** /**
* This method loads the header from the given stream during instance creation. * This method loads the header from the given stream during instance creation.
* @param inputStream * @param inputStream
* the stream we read the header from * the stream we read the header from
* @throws BlenderFileException * @throws BlenderFileException
* this exception is thrown if the file header has some invalid data * this exception is thrown if the file header has some invalid data
*/ */
private void readFileHeader() throws BlenderFileException { private void readFileHeader() throws BlenderFileException {
byte[] identifier = new byte[7]; byte[] identifier = new byte[7];
int bytesRead = this.readBytes(identifier); int bytesRead = this.readBytes(identifier);
if(bytesRead != 7) { if (bytesRead != 7) {
throw new BlenderFileException("Error reading header identifier. Only " + bytesRead + " bytes read and there should be 7!"); throw new BlenderFileException("Error reading header identifier. Only " + bytesRead + " bytes read and there should be 7!");
} }
String strIdentifier = new String(identifier); String strIdentifier = new String(identifier);
if(!"BLENDER".equals(strIdentifier)) { if (!"BLENDER".equals(strIdentifier)) {
throw new BlenderFileException("Wrong file identifier: " + strIdentifier + "! Should be 'BLENDER'!"); throw new BlenderFileException("Wrong file identifier: " + strIdentifier + "! Should be 'BLENDER'!");
} }
char pointerSizeSign = (char)this.readByte(); char pointerSizeSign = (char) this.readByte();
if(pointerSizeSign == '-') { if (pointerSizeSign == '-') {
pointerSize = 8; pointerSize = 8;
} else if(pointerSizeSign == '_') { } else if (pointerSizeSign == '_') {
pointerSize = 4; pointerSize = 4;
} else { } else {
throw new BlenderFileException("Invalid pointer size character! Should be '_' or '-' and there is: " + pointerSizeSign); throw new BlenderFileException("Invalid pointer size character! Should be '_' or '-' and there is: " + pointerSizeSign);
} }
endianess = (char)this.readByte(); endianess = (char) this.readByte();
if(endianess != 'v' && endianess != 'V') { if (endianess != 'v' && endianess != 'V') {
throw new BlenderFileException("Unknown endianess value! 'v' or 'V' expected and found: " + endianess); throw new BlenderFileException("Unknown endianess value! 'v' or 'V' expected and found: " + endianess);
} }
byte[] versionNumber = new byte[3]; byte[] versionNumber = new byte[3];
bytesRead = this.readBytes(versionNumber); bytesRead = this.readBytes(versionNumber);
if(bytesRead != 3) { if (bytesRead != 3) {
throw new BlenderFileException("Error reading version numberr. Only " + bytesRead + " bytes read and there should be 3!"); throw new BlenderFileException("Error reading version numberr. Only " + bytesRead + " bytes read and there should be 3!");
} }
this.versionNumber = new String(versionNumber); this.versionNumber = new String(versionNumber);
} }
@Override @Override
public int read() throws IOException { public int read() throws IOException {
return this.readByte(); return this.readByte();
} }
/** /**
* This method reads 1 byte from the stream. * This method reads 1 byte from the stream.
* It works just in the way the read method does. * It works just in the way the read method does.
* It just not throw an exception because at this moment the whole file * It just not throw an exception because at this moment the whole file
* is loaded into buffer, so no need for IOException to be thrown. * is loaded into buffer, so no need for IOException to be thrown.
* @return a byte from the stream (1 bytes read) * @return a byte from the stream (1 bytes read)
*/ */
public int readByte() { public int readByte() {
return cachedBuffer[position++] & 0xFF; return cachedBuffer[position++] & 0xFF;
} }
/** /**
* This method reads a bytes number big enough to fill the table. * This method reads a bytes number big enough to fill the table.
* It does not throw exceptions so it is for internal use only. * It does not throw exceptions so it is for internal use only.
* @param bytes * @param bytes
* an array to be filled with data * an array to be filled with data
* @return number of read bytes (a length of array actually) * @return number of read bytes (a length of array actually)
*/ */
private int readBytes(byte[] bytes) { private int readBytes(byte[] bytes) {
for(int i=0;i<bytes.length;++i) { for (int i = 0; i < bytes.length; ++i) {
bytes[i] = (byte) this.readByte(); bytes[i] = (byte) this.readByte();
} }
return bytes.length; return bytes.length;
} }
/** /**
* This method reads 2-byte number from the stream. * This method reads 2-byte number from the stream.
* @return a number from the stream (2 bytes read) * @return a number from the stream (2 bytes read)
*/ */
public int readShort() { public int readShort() {
int part1 = this.readByte(); int part1 = this.readByte();
int part2 = this.readByte(); int part2 = this.readByte();
if(endianess == 'v') { if (endianess == 'v') {
return (part2 << 8) + part1; return (part2 << 8) + part1;
} else { } else {
return (part1 << 8) + part2; return (part1 << 8) + part2;
} }
} }
/** /**
* This method reads 4-byte number from the stream. * This method reads 4-byte number from the stream.
* @return a number from the stream (4 bytes read) * @return a number from the stream (4 bytes read)
*/ */
public int readInt() { public int readInt() {
int part1 = this.readByte(); int part1 = this.readByte();
int part2 = this.readByte(); int part2 = this.readByte();
int part3 = this.readByte(); int part3 = this.readByte();
int part4 = this.readByte(); int part4 = this.readByte();
if(endianess == 'v') { if (endianess == 'v') {
return (part4 << 24) + (part3 << 16) + (part2 << 8) + part1; return (part4 << 24) + (part3 << 16) + (part2 << 8) + part1;
} else { } else {
return (part1 << 24) + (part2 << 16) + (part3 << 8) + part4; return (part1 << 24) + (part2 << 16) + (part3 << 8) + part4;
} }
} }
/** /**
* This method reads 4-byte floating point number (float) from the stream. * This method reads 4-byte floating point number (float) from the stream.
* @return a number from the stream (4 bytes read) * @return a number from the stream (4 bytes read)
*/ */
public float readFloat() { public float readFloat() {
int intValue = this.readInt(); int intValue = this.readInt();
return Float.intBitsToFloat(intValue); return Float.intBitsToFloat(intValue);
} }
/** /**
* This method reads 8-byte number from the stream. * This method reads 8-byte number from the stream.
* @return a number from the stream (8 bytes read) * @return a number from the stream (8 bytes read)
*/ */
public long readLong() { public long readLong() {
long part1 = this.readInt(); long part1 = this.readInt();
long part2 = this.readInt(); long part2 = this.readInt();
long result = -1; long result = -1;
if(endianess == 'v') { if (endianess == 'v') {
result = part2 << 32 | part1; result = part2 << 32 | part1;
} else { } else {
result = part1 << 32 | part2; result = part1 << 32 | part2;
} }
return result; return result;
} }
/** /**
* This method reads 8-byte floating point number (double) from the stream. * This method reads 8-byte floating point number (double) from the stream.
* @return a number from the stream (8 bytes read) * @return a number from the stream (8 bytes read)
*/ */
public double readDouble() { public double readDouble() {
long longValue = this.readLong(); long longValue = this.readLong();
return Double.longBitsToDouble(longValue); return Double.longBitsToDouble(longValue);
} }
/** /**
* This method reads the pointer value. Depending on the pointer size defined in the header, the stream reads either * This method reads the pointer value. Depending on the pointer size defined in the header, the stream reads either
* 4 or 8 bytes of data. * 4 or 8 bytes of data.
* @return the pointer value * @return the pointer value
*/ */
public long readPointer() { public long readPointer() {
if(pointerSize == 4) { if (pointerSize == 4) {
return this.readInt(); return this.readInt();
} }
return this.readLong(); return this.readLong();
} }
/** /**
* This method reads the string. It assumes the string is terminated with zero in the stream. * This method reads the string. It assumes the string is terminated with zero in the stream.
* @return the string read from the stream * @return the string read from the stream
*/ */
public String readString() { public String readString() {
StringBuilder stringBuilder = new StringBuilder(); StringBuilder stringBuilder = new StringBuilder();
int data = this.readByte(); int data = this.readByte();
while(data != 0) { while (data != 0) {
stringBuilder.append((char)data); stringBuilder.append((char) data);
data = this.readByte(); data = this.readByte();
} }
return stringBuilder.toString(); return stringBuilder.toString();
} }
/** /**
* This method sets the current position of the read cursor. * This method sets the current position of the read cursor.
* @param position * @param position
* the position of the read cursor * the position of the read cursor
*/ */
public void setPosition(int position) { public void setPosition(int position) {
this.position = position; this.position = position;
} }
/** /**
* This method returns the position of the read cursor. * This method returns the position of the read cursor.
* @return the position of the read cursor * @return the position of the read cursor
*/ */
public int getPosition() { public int getPosition() {
return position; return position;
} }
/** /**
* This method returns the blender version number where the file was created. * This method returns the blender version number where the file was created.
* @return blender version number * @return blender version number
*/ */
public String getVersionNumber() { public String getVersionNumber() {
return versionNumber; return versionNumber;
} }
/** /**
* This method returns the size of the pointer. * This method returns the size of the pointer.
* @return the size of the pointer * @return the size of the pointer
*/ */
public int getPointerSize() { public int getPointerSize() {
return pointerSize; return pointerSize;
} }
/** /**
* This method returns the application's asset manager. * This method returns the application's asset manager.
* @return the application's asset manager * @return the application's asset manager
*/ */
public AssetManager getAssetManager() { public AssetManager getAssetManager() {
return assetManager; return assetManager;
} }
/** /**
* This method aligns cursor position forward to a given amount of bytes. * This method aligns cursor position forward to a given amount of bytes.
* @param bytesAmount * @param bytesAmount
* the byte amount to which we aligh the cursor * the byte amount to which we aligh the cursor
*/ */
public void alignPosition(int bytesAmount) { public void alignPosition(int bytesAmount) {
if(bytesAmount <= 0) { if (bytesAmount <= 0) {
throw new IllegalArgumentException("Alignment byte number shoulf be positivbe!"); throw new IllegalArgumentException("Alignment byte number shoulf be positivbe!");
} }
long move = position % bytesAmount; long move = position % bytesAmount;
if(move > 0) { if (move > 0) {
position += bytesAmount - move; position += bytesAmount - move;
} }
} }
@Override @Override
public void close() throws IOException { public void close() throws IOException {
// cachedBuffer = null; // cachedBuffer = null;
// size = position = 0; // size = position = 0;
} }
} }

@ -54,347 +54,347 @@ import com.jme3.scene.plugins.blender.structures.Modifier;
* @author Marcin Roguski * @author Marcin Roguski
*/ */
public class DataRepository { public class DataRepository {
/** The blender key. */
private BlenderKey blenderKey; /** The blender key. */
/** The header of the file block. */ private BlenderKey blenderKey;
private DnaBlockData dnaBlockData; /** The header of the file block. */
/** The input stream of the blend file. */ private DnaBlockData dnaBlockData;
private BlenderInputStream inputStream; /** The input stream of the blend file. */
/** The asset manager. */ private BlenderInputStream inputStream;
private AssetManager assetManager; /** The asset manager. */
/** A map containing the file block headers. The key is the old pointer address. */ private AssetManager assetManager;
private Map<Long, FileBlockHeader> fileBlockHeadersByOma = new HashMap<Long, FileBlockHeader>(); /** A map containing the file block headers. The key is the old pointer address. */
/** A map containing the file block headers. The key is the block code. */ private Map<Long, FileBlockHeader> fileBlockHeadersByOma = new HashMap<Long, FileBlockHeader>();
private Map<Integer, List<FileBlockHeader>> fileBlockHeadersByCode = new HashMap<Integer, List<FileBlockHeader>>(); /** A map containing the file block headers. The key is the block code. */
/** private Map<Integer, List<FileBlockHeader>> fileBlockHeadersByCode = new HashMap<Integer, List<FileBlockHeader>>();
* This map stores the loaded features by their old memory address. The first object in the value table is the /**
* loaded structure and the second - the structure already converted into proper data. * This map stores the loaded features by their old memory address. The first object in the value table is the
*/ * loaded structure and the second - the structure already converted into proper data.
private Map<Long, Object[]> loadedFeatures = new HashMap<Long, Object[]>(); */
/** private Map<Long, Object[]> loadedFeatures = new HashMap<Long, Object[]>();
* This map stores the loaded features by their name. Only features with ID structure can be stored here. /**
* The first object in the value table is the * This map stores the loaded features by their name. Only features with ID structure can be stored here.
* loaded structure and the second - the structure already converted into proper data. * The first object in the value table is the
*/ * loaded structure and the second - the structure already converted into proper data.
private Map<String, Object[]> loadedFeaturesByName = new HashMap<String, Object[]>(); */
/** A stack that hold the parent structure of currently loaded feature. */ private Map<String, Object[]> loadedFeaturesByName = new HashMap<String, Object[]>();
private Stack<Structure> parentStack = new Stack<Structure>(); /** A stack that hold the parent structure of currently loaded feature. */
/** A map storing loaded ipos. The key is the ipo's owner old memory address and the value is the ipo. */ private Stack<Structure> parentStack = new Stack<Structure>();
private Map<Long, Ipo> loadedIpos = new HashMap<Long, Ipo>(); /** A map storing loaded ipos. The key is the ipo's owner old memory address and the value is the ipo. */
/** A list of modifiers for the specified object. */ private Map<Long, Ipo> loadedIpos = new HashMap<Long, Ipo>();
protected Map<Long, List<Modifier>> modifiers = new HashMap<Long, List<Modifier>>(); /** A list of modifiers for the specified object. */
/** A map og helpers that perform loading. */ protected Map<Long, List<Modifier>> modifiers = new HashMap<Long, List<Modifier>>();
private Map<String, AbstractBlenderHelper> helpers = new HashMap<String, AbstractBlenderHelper>(); /** A map og helpers that perform loading. */
private Map<String, AbstractBlenderHelper> helpers = new HashMap<String, AbstractBlenderHelper>();
/**
* This method sets the blender key. /**
* @param blenderKey * This method sets the blender key.
* the blender key * @param blenderKey
*/ * the blender key
public void setBlenderKey(BlenderKey blenderKey) { */
this.blenderKey = blenderKey; public void setBlenderKey(BlenderKey blenderKey) {
} this.blenderKey = blenderKey;
}
/**
* This method returns the blender key. /**
* @return the blender key * This method returns the blender key.
*/ * @return the blender key
public BlenderKey getBlenderKey() { */
return blenderKey; public BlenderKey getBlenderKey() {
} return blenderKey;
}
/**
* This method sets the dna block data. /**
* @param dnaBlockData * This method sets the dna block data.
* the dna block data * @param dnaBlockData
*/ * the dna block data
public void setBlockData(DnaBlockData dnaBlockData) { */
this.dnaBlockData = dnaBlockData; public void setBlockData(DnaBlockData dnaBlockData) {
} this.dnaBlockData = dnaBlockData;
}
/**
* This method returns the dna block data. /**
* @return the dna block data * This method returns the dna block data.
*/ * @return the dna block data
public DnaBlockData getDnaBlockData() { */
return dnaBlockData; public DnaBlockData getDnaBlockData() {
} return dnaBlockData;
}
/**
* This method returns the asset manager. /**
* @return the asset manager * This method returns the asset manager.
*/ * @return the asset manager
public AssetManager getAssetManager() { */
return assetManager; public AssetManager getAssetManager() {
} return assetManager;
}
/**
* This method sets the asset manager. /**
* @param assetManager * This method sets the asset manager.
* the asset manager * @param assetManager
*/ * the asset manager
public void setAssetManager(AssetManager assetManager) { */
this.assetManager = assetManager; public void setAssetManager(AssetManager assetManager) {
} this.assetManager = assetManager;
}
/**
* This method returns the input stream of the blend file. /**
* @return the input stream of the blend file * This method returns the input stream of the blend file.
*/ * @return the input stream of the blend file
public BlenderInputStream getInputStream() { */
return inputStream; public BlenderInputStream getInputStream() {
} return inputStream;
}
/**
* This method sets the input stream of the blend file. /**
* @param inputStream * This method sets the input stream of the blend file.
* the input stream of the blend file * @param inputStream
*/ * the input stream of the blend file
public void setInputStream(BlenderInputStream inputStream) { */
this.inputStream = inputStream; public void setInputStream(BlenderInputStream inputStream) {
} this.inputStream = inputStream;
}
/**
* This method adds a file block header to the map. Its old memory address is the key. /**
* @param oldMemoryAddress * This method adds a file block header to the map. Its old memory address is the key.
* the address of the block header * @param oldMemoryAddress
* @param fileBlockHeader * the address of the block header
* the block header to store * @param fileBlockHeader
*/ * the block header to store
public void addFileBlockHeader(Long oldMemoryAddress, FileBlockHeader fileBlockHeader) { */
fileBlockHeadersByOma.put(oldMemoryAddress, fileBlockHeader); public void addFileBlockHeader(Long oldMemoryAddress, FileBlockHeader fileBlockHeader) {
List<FileBlockHeader> headers = fileBlockHeadersByCode.get(Integer.valueOf(fileBlockHeader.getCode())); fileBlockHeadersByOma.put(oldMemoryAddress, fileBlockHeader);
if(headers == null) { List<FileBlockHeader> headers = fileBlockHeadersByCode.get(Integer.valueOf(fileBlockHeader.getCode()));
headers = new ArrayList<FileBlockHeader>(); if (headers == null) {
fileBlockHeadersByCode.put(Integer.valueOf(fileBlockHeader.getCode()), headers); headers = new ArrayList<FileBlockHeader>();
} fileBlockHeadersByCode.put(Integer.valueOf(fileBlockHeader.getCode()), headers);
headers.add(fileBlockHeader); }
} headers.add(fileBlockHeader);
}
/**
* This method returns the block header of a given memory address. If the header is not present then null is /**
* returned. * This method returns the block header of a given memory address. If the header is not present then null is
* @param oldMemoryAddress * returned.
* the address of the block header * @param oldMemoryAddress
* @return loaded header or null if it was not yet loaded * the address of the block header
*/ * @return loaded header or null if it was not yet loaded
public FileBlockHeader getFileBlock(Long oldMemoryAddress) { */
return fileBlockHeadersByOma.get(oldMemoryAddress); public FileBlockHeader getFileBlock(Long oldMemoryAddress) {
} return fileBlockHeadersByOma.get(oldMemoryAddress);
}
/**
* This method returns a list of file blocks' headers of a specified code. /**
* @param code * This method returns a list of file blocks' headers of a specified code.
* the code of file blocks * @param code
* @return a list of file blocks' headers of a specified code * the code of file blocks
*/ * @return a list of file blocks' headers of a specified code
public List<FileBlockHeader> getFileBlocks(Integer code) { */
return fileBlockHeadersByCode.get(code); public List<FileBlockHeader> getFileBlocks(Integer code) {
} return fileBlockHeadersByCode.get(code);
}
/**
* This method clears the saved block headers stored in the features map. /**
*/ * This method clears the saved block headers stored in the features map.
public void clearFileBlocks() { */
fileBlockHeadersByOma.clear(); public void clearFileBlocks() {
fileBlockHeadersByCode.clear(); fileBlockHeadersByOma.clear();
} fileBlockHeadersByCode.clear();
}
/**
* This method adds a helper instance to the helpers' map. /**
* @param <T> * This method adds a helper instance to the helpers' map.
* the type of the helper * @param <T>
* @param clazz * the type of the helper
* helper's class definition * @param clazz
* @param helper * helper's class definition
* the helper instance * @param helper
*/ * the helper instance
public <T> void putHelper(Class<T> clazz, AbstractBlenderHelper helper) { */
helpers.put(clazz.getSimpleName(), helper); public <T> void putHelper(Class<T> clazz, AbstractBlenderHelper helper) {
} helpers.put(clazz.getSimpleName(), helper);
}
@SuppressWarnings("unchecked")
public <T> T getHelper(Class<?> clazz) { @SuppressWarnings("unchecked")
return (T)helpers.get(clazz.getSimpleName()); public <T> T getHelper(Class<?> clazz) {
} return (T) helpers.get(clazz.getSimpleName());
}
/** /**
* This method adds a loaded feature to the map. The key is its unique old memory address. * This method adds a loaded feature to the map. The key is its unique old memory address.
* @param oldMemoryAddress * @param oldMemoryAddress
* the address of the feature * the address of the feature
* @param featureName the name of the feature * @param featureName the name of the feature
* @param structure * @param structure
* the filled structure of the feature * the filled structure of the feature
* @param feature * @param feature
* the feature we want to store * the feature we want to store
*/ */
public void addLoadedFeatures(Long oldMemoryAddress, String featureName, Structure structure, Object feature) { public void addLoadedFeatures(Long oldMemoryAddress, String featureName, Structure structure, Object feature) {
if(oldMemoryAddress == null || structure == null || feature == null) { if (oldMemoryAddress == null || structure == null || feature == null) {
throw new IllegalArgumentException("One of the given arguments is null!"); throw new IllegalArgumentException("One of the given arguments is null!");
} }
Object[] storedData = new Object[] {structure, feature}; Object[] storedData = new Object[]{structure, feature};
loadedFeatures.put(oldMemoryAddress, storedData); loadedFeatures.put(oldMemoryAddress, storedData);
if(featureName!=null) { if (featureName != null) {
loadedFeaturesByName.put(featureName, storedData); loadedFeaturesByName.put(featureName, storedData);
} }
} }
/** /**
* This method returns the feature of a given memory address. If the feature is not yet loaded then null is * This method returns the feature of a given memory address. If the feature is not yet loaded then null is
* returned. * returned.
* @param oldMemoryAddress * @param oldMemoryAddress
* the address of the feature * the address of the feature
* @param loadedFeatureDataType * @param loadedFeatureDataType
* the type of data we want to retreive it can be either filled structure or already converted feature * the type of data we want to retreive it can be either filled structure or already converted feature
* @return loaded feature or null if it was not yet loaded * @return loaded feature or null if it was not yet loaded
*/ */
public Object getLoadedFeature(Long oldMemoryAddress, LoadedFeatureDataType loadedFeatureDataType) { public Object getLoadedFeature(Long oldMemoryAddress, LoadedFeatureDataType loadedFeatureDataType) {
Object[] result = loadedFeatures.get(oldMemoryAddress); Object[] result = loadedFeatures.get(oldMemoryAddress);
if(result != null) { if (result != null) {
return result[loadedFeatureDataType.getIndex()]; return result[loadedFeatureDataType.getIndex()];
} }
return null; return null;
} }
/** /**
* This method returns the feature of a given name. If the feature is not yet loaded then null is * This method returns the feature of a given name. If the feature is not yet loaded then null is
* returned. * returned.
* @param featureName * @param featureName
* the name of the feature * the name of the feature
* @param loadedFeatureDataType * @param loadedFeatureDataType
* the type of data we want to retreive it can be either filled structure or already converted feature * the type of data we want to retreive it can be either filled structure or already converted feature
* @return loaded feature or null if it was not yet loaded * @return loaded feature or null if it was not yet loaded
*/ */
public Object getLoadedFeature(String featureName, LoadedFeatureDataType loadedFeatureDataType) { public Object getLoadedFeature(String featureName, LoadedFeatureDataType loadedFeatureDataType) {
Object[] result = loadedFeaturesByName.get(featureName); Object[] result = loadedFeaturesByName.get(featureName);
if(result != null) { if (result != null) {
return result[loadedFeatureDataType.getIndex()]; return result[loadedFeatureDataType.getIndex()];
} }
return null; return null;
} }
/** /**
* This method clears the saved features stored in the features map. * This method clears the saved features stored in the features map.
*/ */
public void clearLoadedFeatures() { public void clearLoadedFeatures() {
loadedFeatures.clear(); loadedFeatures.clear();
} }
/** /**
* This method adds the structure to the parent stack. * This method adds the structure to the parent stack.
* @param parent * @param parent
* the structure to be added to the stack * the structure to be added to the stack
*/ */
public void pushParent(Structure parent) { public void pushParent(Structure parent) {
parentStack.push(parent); parentStack.push(parent);
} }
/** /**
* This method removes the structure from the top of the parent's stack. * This method removes the structure from the top of the parent's stack.
* @return the structure that was removed from the stack * @return the structure that was removed from the stack
*/ */
public Structure popParent() { public Structure popParent() {
try { try {
return parentStack.pop(); return parentStack.pop();
} catch(EmptyStackException e) { } catch (EmptyStackException e) {
return null; return null;
} }
} }
/** /**
* This method retreives the structure at the top of the parent's stack but does not remove it. * This method retreives the structure at the top of the parent's stack but does not remove it.
* @return the structure from the top of the stack * @return the structure from the top of the stack
*/ */
public Structure peekParent() { public Structure peekParent() {
try { try {
return parentStack.peek(); return parentStack.peek();
} catch(EmptyStackException e) { } catch (EmptyStackException e) {
return null; return null;
} }
} }
public void addIpo(Long ownerOMA, Ipo ipo) { public void addIpo(Long ownerOMA, Ipo ipo) {
loadedIpos.put(ownerOMA, ipo); loadedIpos.put(ownerOMA, ipo);
} }
public Ipo removeIpo(Long ownerOma) { public Ipo removeIpo(Long ownerOma) {
return loadedIpos.remove(ownerOma); return loadedIpos.remove(ownerOma);
} }
public Ipo getIpo(Long ownerOMA) { public Ipo getIpo(Long ownerOMA) {
return loadedIpos.get(ownerOMA); return loadedIpos.get(ownerOMA);
} }
/** /**
* This method adds a new modifier to the list. * This method adds a new modifier to the list.
* @param ownerOMA * @param ownerOMA
* the owner's old memory address * the owner's old memory address
* @param modifierType * @param modifierType
* the type of the modifier * the type of the modifier
* @param loadedModifier * @param loadedModifier
* the loaded modifier object * the loaded modifier object
*/ */
public void addModifier(Long ownerOMA, String modifierType, Object loadedModifier, Object additionalModifierData) { public void addModifier(Long ownerOMA, String modifierType, Object loadedModifier, Object additionalModifierData) {
List<Modifier> objectModifiers = this.modifiers.get(ownerOMA); List<Modifier> objectModifiers = this.modifiers.get(ownerOMA);
if(objectModifiers == null) { if (objectModifiers == null) {
objectModifiers = new ArrayList<Modifier>(); objectModifiers = new ArrayList<Modifier>();
this.modifiers.put(ownerOMA, objectModifiers); this.modifiers.put(ownerOMA, objectModifiers);
} }
objectModifiers.add(new Modifier(modifierType, loadedModifier, additionalModifierData)); objectModifiers.add(new Modifier(modifierType, loadedModifier, additionalModifierData));
} }
/** /**
* This method returns modifiers for the object specified by its old memory address and the modifier type. If no * This method returns modifiers for the object specified by its old memory address and the modifier type. If no
* modifiers are found - empty list is returned. If the type is null - all modifiers for the object are returned. * modifiers are found - empty list is returned. If the type is null - all modifiers for the object are returned.
* @param objectOMA * @param objectOMA
* object's old memory address * object's old memory address
* @param type * @param type
* the type of the modifier * the type of the modifier
* @return the list of object's modifiers * @return the list of object's modifiers
*/ */
public List<Modifier> getModifiers(Long objectOMA, String type) { public List<Modifier> getModifiers(Long objectOMA, String type) {
List<Modifier> result = new ArrayList<Modifier>(); List<Modifier> result = new ArrayList<Modifier>();
List<Modifier> readModifiers = modifiers.get(objectOMA); List<Modifier> readModifiers = modifiers.get(objectOMA);
if(readModifiers != null && readModifiers.size() > 0) { if (readModifiers != null && readModifiers.size() > 0) {
for(Modifier modifier : readModifiers) { for (Modifier modifier : readModifiers) {
if(type==null || type.isEmpty() || modifier.getType().equals(type)) { if (type == null || type.isEmpty() || modifier.getType().equals(type)) {
result.add(modifier); result.add(modifier);
} }
} }
} }
return result; return result;
} }
/** /**
* This metod returns the default material. * This metod returns the default material.
* @return the default material * @return the default material
*/ */
public synchronized Material getDefaultMaterial() { public synchronized Material getDefaultMaterial() {
if(blenderKey.getDefaultMaterial() == null) { if (blenderKey.getDefaultMaterial() == null) {
Material defaultMaterial = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); Material defaultMaterial = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
defaultMaterial.setColor("Color", ColorRGBA.DarkGray); defaultMaterial.setColor("Color", ColorRGBA.DarkGray);
blenderKey.setDefaultMaterial(defaultMaterial); blenderKey.setDefaultMaterial(defaultMaterial);
} }
return blenderKey.getDefaultMaterial(); return blenderKey.getDefaultMaterial();
} }
/** /**
* This enum defines what loaded data type user wants to retreive. It can be either filled structure or already * This enum defines what loaded data type user wants to retreive. It can be either filled structure or already
* converted data. * converted data.
* @author Marcin Roguski * @author Marcin Roguski
*/ */
public static enum LoadedFeatureDataType { public static enum LoadedFeatureDataType {
LOADED_STRUCTURE(0), LOADED_FEATURE(1);
LOADED_STRUCTURE(0), LOADED_FEATURE(1);
private int index; private int index;
private LoadedFeatureDataType(int index) { private LoadedFeatureDataType(int index) {
this.index = index; this.index = index;
} }
public int getIndex() { public int getIndex() {
return index; return index;
} }
} }
} }

@ -40,117 +40,118 @@ import com.jme3.scene.plugins.blender.exception.BlenderFileException;
* the type of stored data in the array * the type of stored data in the array
*/ */
public class DynamicArray<T> implements Cloneable { public class DynamicArray<T> implements Cloneable {
/** An array object that holds the required data. */
private T[] array;
/**
* This table holds the sizes of dimetions of the dynamic table. It's length specifies the table dimension or a
* pointer level. For example: if tableSizes.length == 3 then it either specifies a dynamic table of fixed lengths:
* dynTable[a][b][c], where a,b,c are stored in the tableSizes table.
*/
private int[] tableSizes;
/** /** An array object that holds the required data. */
* Constructor. Builds an empty array of the specified sizes. private T[] array;
* @param tableSizes /**
* the sizes of the table * This table holds the sizes of dimetions of the dynamic table. It's length specifies the table dimension or a
* @throws BlenderFileException * pointer level. For example: if tableSizes.length == 3 then it either specifies a dynamic table of fixed lengths:
* an exception is thrown if one of the sizes is not a positive number * dynTable[a][b][c], where a,b,c are stored in the tableSizes table.
*/ */
@SuppressWarnings("unchecked") private int[] tableSizes;
public DynamicArray(int[] tableSizes) throws BlenderFileException {
this.tableSizes = tableSizes;
int totalSize = 1;
for(int size : tableSizes) {
if(size <= 0) {
throw new BlenderFileException("The size of the table must be positive!");
}
totalSize *= size;
}
this.array = (T[])new Object[totalSize];
}
/** /**
* Constructor. Builds an empty array of the specified sizes. * Constructor. Builds an empty array of the specified sizes.
* @param tableSizes * @param tableSizes
* the sizes of the table * the sizes of the table
* @throws BlenderFileException * @throws BlenderFileException
* an exception is thrown if one of the sizes is not a positive number * an exception is thrown if one of the sizes is not a positive number
*/ */
public DynamicArray(int[] tableSizes, T[] data) throws BlenderFileException { @SuppressWarnings("unchecked")
this.tableSizes = tableSizes; public DynamicArray(int[] tableSizes) throws BlenderFileException {
int totalSize = 1; this.tableSizes = tableSizes;
for(int size : tableSizes) { int totalSize = 1;
if(size <= 0) { for (int size : tableSizes) {
throw new BlenderFileException("The size of the table must be positive!"); if (size <= 0) {
} throw new BlenderFileException("The size of the table must be positive!");
totalSize *= size; }
} totalSize *= size;
if(totalSize != data.length) { }
throw new IllegalArgumentException("The size of the table does not match the size of the given data!"); this.array = (T[]) new Object[totalSize];
} }
this.array = data;
}
@Override /**
public Object clone() throws CloneNotSupportedException { * Constructor. Builds an empty array of the specified sizes.
return super.clone(); * @param tableSizes
} * the sizes of the table
* @throws BlenderFileException
* an exception is thrown if one of the sizes is not a positive number
*/
public DynamicArray(int[] tableSizes, T[] data) throws BlenderFileException {
this.tableSizes = tableSizes;
int totalSize = 1;
for (int size : tableSizes) {
if (size <= 0) {
throw new BlenderFileException("The size of the table must be positive!");
}
totalSize *= size;
}
if (totalSize != data.length) {
throw new IllegalArgumentException("The size of the table does not match the size of the given data!");
}
this.array = data;
}
/** @Override
* This method returns a value on the specified position. The dimension of the table is not taken into public Object clone() throws CloneNotSupportedException {
* consideration. return super.clone();
* @param position }
* the position of the data
* @return required data
*/
public T get(int position) {
return array[position];
}
/** /**
* This method returns a value on the specified position in multidimensional array. Be careful not to exceed the * This method returns a value on the specified position. The dimension of the table is not taken into
* table boundaries. Check the table's dimension first. * consideration.
* @param position * @param position
* the position of the data indices of data position * the position of the data
* @return required data required data * @return required data
*/ */
public T get(int... position) { public T get(int position) {
if(position.length != tableSizes.length) { return array[position];
throw new ArrayIndexOutOfBoundsException("The table accepts " + tableSizes.length + " indexing number(s)!"); }
}
int index = 0;
for(int i = 0; i < position.length - 1; ++i) {
index += position[i] * tableSizes[i + 1];
}
index += position[position.length - 1];
return array[index];
}
/** /**
* This method returns the total amount of data stored in the array. * This method returns a value on the specified position in multidimensional array. Be careful not to exceed the
* @return the total amount of data stored in the array * table boundaries. Check the table's dimension first.
*/ * @param position
public int getTotalSize() { * the position of the data indices of data position
return array.length; * @return required data required data
} */
public T get(int... position) {
if (position.length != tableSizes.length) {
throw new ArrayIndexOutOfBoundsException("The table accepts " + tableSizes.length + " indexing number(s)!");
}
int index = 0;
for (int i = 0; i < position.length - 1; ++i) {
index += position[i] * tableSizes[i + 1];
}
index += position[position.length - 1];
return array[index];
}
@Override /**
public String toString() { * This method returns the total amount of data stored in the array.
StringBuilder result = new StringBuilder(); * @return the total amount of data stored in the array
if(array instanceof Character[]) {//in case of character array we convert it to String */
for(int i = 0; i < array.length && (Character)array[i] != '\0'; ++i) {//strings are terminater with '0' public int getTotalSize() {
result.append(array[i]); return array.length;
} }
} else {
result.append('['); @Override
for(int i = 0; i < array.length; ++i) { public String toString() {
result.append(array[i].toString()); StringBuilder result = new StringBuilder();
if(i + 1 < array.length) { if (array instanceof Character[]) {//in case of character array we convert it to String
result.append(','); for (int i = 0; i < array.length && (Character) array[i] != '\0'; ++i) {//strings are terminater with '0'
} result.append(array[i]);
} }
result.append(']'); } else {
} result.append('[');
return result.toString(); for (int i = 0; i < array.length; ++i) {
} result.append(array[i].toString());
if (i + 1 < array.length) {
result.append(',');
}
}
result.append(']');
}
return result.toString();
}
} }

@ -55,115 +55,115 @@ import com.jme3.scene.plugins.blender.helpers.ObjectHelper;
* This class converts blender file blocks into jMonkeyEngine data structures. * This class converts blender file blocks into jMonkeyEngine data structures.
* @author Marcin Roguski * @author Marcin Roguski
*/ */
public class JmeConverter implements IBlenderConverter<Node, Camera, Light, Object, List<Geometry>, Material> { public class JmeConverter implements BlenderConverter<Node, Camera, Light, Object, List<Geometry>, Material> {
private static final Logger LOGGER = Logger.getLogger(JmeConverter.class.getName());
private final DataRepository dataRepository; private static final Logger LOGGER = Logger.getLogger(JmeConverter.class.getName());
private final DataRepository dataRepository;
/** /**
* Constructor. Creates the loader and checks if the given data is correct. * Constructor. Creates the loader and checks if the given data is correct.
* @param dataRepository * @param dataRepository
* the data repository; it should have the following field set: - asset manager - blender key - dna block * the data repository; it should have the following field set: - asset manager - blender key - dna block
* data - blender input stream Otherwise IllegalArgumentException will be thrown. * data - blender input stream Otherwise IllegalArgumentException will be thrown.
* @param featuresToLoad * @param featuresToLoad
* bitwise flag describing what features are to be loaded * bitwise flag describing what features are to be loaded
* @see FeaturesToLoad FeaturesToLoad * @see FeaturesToLoad FeaturesToLoad
*/ */
public JmeConverter(DataRepository dataRepository) { public JmeConverter(DataRepository dataRepository) {
//validating the given data first //validating the given data first
if(dataRepository.getAssetManager() == null) { if (dataRepository.getAssetManager() == null) {
throw new IllegalArgumentException("Cannot find asset manager!"); throw new IllegalArgumentException("Cannot find asset manager!");
} }
if(dataRepository.getBlenderKey() == null) { if (dataRepository.getBlenderKey() == null) {
throw new IllegalArgumentException("Cannot find blender key!"); throw new IllegalArgumentException("Cannot find blender key!");
} }
if(dataRepository.getDnaBlockData() == null) { if (dataRepository.getDnaBlockData() == null) {
throw new IllegalArgumentException("Cannot find dna block!"); throw new IllegalArgumentException("Cannot find dna block!");
} }
if(dataRepository.getInputStream() == null) { if (dataRepository.getInputStream() == null) {
throw new IllegalArgumentException("Cannot find blender file stream!"); throw new IllegalArgumentException("Cannot find blender file stream!");
} }
this.dataRepository = dataRepository; this.dataRepository = dataRepository;
} }
@Override @Override
public Node toScene(Structure structure) {//TODO: poprawny import sceny public Node toScene(Structure structure) {//TODO: poprawny import sceny
if((dataRepository.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.SCENES) == 0) { if ((dataRepository.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.SCENES) == 0) {
return null; return null;
} }
Structure id = (Structure)structure.getFieldValue("id"); Structure id = (Structure) structure.getFieldValue("id");
String sceneName = id.getFieldValue("name").toString(); String sceneName = id.getFieldValue("name").toString();
//veryfying layers to be loaded //veryfying layers to be loaded
if(dataRepository.getBlenderKey().getLayersToLoad()<0) { if (dataRepository.getBlenderKey().getLayersToLoad() < 0) {
int lay = ((Number)structure.getFieldValue("lay")).intValue(); int lay = ((Number) structure.getFieldValue("lay")).intValue();
dataRepository.getBlenderKey().setLayersToLoad(lay);//load only current layer dataRepository.getBlenderKey().setLayersToLoad(lay);//load only current layer
} }
return new Node(sceneName); return new Node(sceneName);
} }
@Override @Override
public Camera toCamera(Structure structure) throws BlenderFileException { public Camera toCamera(Structure structure) throws BlenderFileException {
if((dataRepository.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.CAMERAS) == 0) { if ((dataRepository.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.CAMERAS) == 0) {
return null; return null;
} }
CameraHelper cameraHelper = dataRepository.getHelper(CameraHelper.class); CameraHelper cameraHelper = dataRepository.getHelper(CameraHelper.class);
return cameraHelper.toCamera(structure); return cameraHelper.toCamera(structure);
} }
@Override @Override
public Light toLight(Structure structure) throws BlenderFileException { public Light toLight(Structure structure) throws BlenderFileException {
if((dataRepository.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.LIGHTS) == 0) { if ((dataRepository.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.LIGHTS) == 0) {
return null; return null;
} }
LightHelper lightHelper = dataRepository.getHelper(LightHelper.class); LightHelper lightHelper = dataRepository.getHelper(LightHelper.class);
return lightHelper.toLight(structure, dataRepository); return lightHelper.toLight(structure, dataRepository);
} }
@Override @Override
public Object toObject(Structure structure) throws BlenderFileException { public Object toObject(Structure structure) throws BlenderFileException {
int lay = ((Number)structure.getFieldValue("lay")).intValue(); int lay = ((Number) structure.getFieldValue("lay")).intValue();
if((lay & dataRepository.getBlenderKey().getLayersToLoad()) == 0 || if ((lay & dataRepository.getBlenderKey().getLayersToLoad()) == 0
(dataRepository.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.OBJECTS) == 0) { || (dataRepository.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.OBJECTS) == 0) {
return null; return null;
} }
ObjectHelper objectHelper = dataRepository.getHelper(ObjectHelper.class); ObjectHelper objectHelper = dataRepository.getHelper(ObjectHelper.class);
return objectHelper.toObject(structure, dataRepository); return objectHelper.toObject(structure, dataRepository);
} }
@Override @Override
public List<Geometry> toMesh(Structure structure) throws BlenderFileException { public List<Geometry> toMesh(Structure structure) throws BlenderFileException {
MeshHelper meshHelper = dataRepository.getHelper(MeshHelper.class); MeshHelper meshHelper = dataRepository.getHelper(MeshHelper.class);
return meshHelper.toMesh(structure, dataRepository); return meshHelper.toMesh(structure, dataRepository);
} }
@Override @Override
public Material toMaterial(Structure structure) throws BlenderFileException { public Material toMaterial(Structure structure) throws BlenderFileException {
if((dataRepository.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.MATERIALS) == 0) { if ((dataRepository.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.MATERIALS) == 0) {
return null; return null;
} }
MaterialHelper materialHelper = dataRepository.getHelper(MaterialHelper.class); MaterialHelper materialHelper = dataRepository.getHelper(MaterialHelper.class);
return materialHelper.toMaterial(structure, dataRepository); return materialHelper.toMaterial(structure, dataRepository);
} }
/** /**
* This method returns the data read from the WORLD file block. The block contains data that can be stored as * This method returns the data read from the WORLD file block. The block contains data that can be stored as
* separate jme features and therefore cannot be returned as a single jME scene feature. * separate jme features and therefore cannot be returned as a single jME scene feature.
* @param structure * @param structure
* the structure with WORLD block data * the structure with WORLD block data
* @return data read from the WORLD block that can be added to the scene * @return data read from the WORLD block that can be added to the scene
*/ */
public WorldData toWorldData(Structure structure) { public WorldData toWorldData(Structure structure) {
WorldData result = new WorldData(); WorldData result = new WorldData();
//reading ambient light //reading ambient light
AmbientLight ambientLight = new AmbientLight(); AmbientLight ambientLight = new AmbientLight();
float ambr = ((Number)structure.getFieldValue("ambr")).floatValue(); float ambr = ((Number) structure.getFieldValue("ambr")).floatValue();
float ambg = ((Number)structure.getFieldValue("ambg")).floatValue(); float ambg = ((Number) structure.getFieldValue("ambg")).floatValue();
float ambb = ((Number)structure.getFieldValue("ambb")).floatValue(); float ambb = ((Number) structure.getFieldValue("ambb")).floatValue();
ambientLight.setColor(new ColorRGBA(ambr, ambg, ambb, 0.0f)); ambientLight.setColor(new ColorRGBA(ambr, ambg, ambb, 0.0f));
result.setAmbientLight(ambientLight); result.setAmbientLight(ambientLight);
return result; return result;
} }
} }

@ -43,133 +43,134 @@ import com.jme3.scene.plugins.blender.exception.BlenderFileException;
* @author Marcin Roguski * @author Marcin Roguski
*/ */
public class Pointer { public class Pointer {
/** The data repository. */
private DataRepository dataRepository;
/** The level of the pointer. */
private int pointerLevel;
/** The address in file it points to. */
private long oldMemoryAddress;
/** This variable indicates if the field is a function pointer. */
public boolean function;
/** /** The data repository. */
* Constructr. Stores the basic data about the pointer. private DataRepository dataRepository;
* @param pointerLevel /** The level of the pointer. */
* the level of the pointer private int pointerLevel;
* @param function /** The address in file it points to. */
* this variable indicates if the field is a function pointer private long oldMemoryAddress;
* @param dataRepository /** This variable indicates if the field is a function pointer. */
* the repository f data; used in fetching the value that the pointer points public boolean function;
*/
public Pointer(int pointerLevel, boolean function, DataRepository dataRepository) {
this.pointerLevel = pointerLevel;
this.function = function;
this.dataRepository = dataRepository;
}
/** /**
* This method fills the pointer with its address value (it doesn't get the actual data yet. Use the 'fetch' method * Constructr. Stores the basic data about the pointer.
* for this. * @param pointerLevel
* @param inputStream * the level of the pointer
* the stream we read the pointer value from * @param function
*/ * this variable indicates if the field is a function pointer
public void fill(BlenderInputStream inputStream) { * @param dataRepository
oldMemoryAddress = inputStream.readPointer(); * the repository f data; used in fetching the value that the pointer points
} */
public Pointer(int pointerLevel, boolean function, DataRepository dataRepository) {
this.pointerLevel = pointerLevel;
this.function = function;
this.dataRepository = dataRepository;
}
/** /**
* This method fetches the data stored under the given address. * This method fills the pointer with its address value (it doesn't get the actual data yet. Use the 'fetch' method
* @param inputStream * for this.
* the stream we read data from * @param inputStream
* @param dataIndices * the stream we read the pointer value from
* the offset of the data in the table pointed by the pointer */
* @return the data read from the file public void fill(BlenderInputStream inputStream) {
* @throws BlenderFileException oldMemoryAddress = inputStream.readPointer();
* this exception is thrown when the blend file structure is somehow invalid or corrupted }
*/
public List<Structure> fetchData(BlenderInputStream inputStream) throws BlenderFileException {
if(oldMemoryAddress == 0) {
throw new NullPointerException("The pointer points to nothing!");
}
List<Structure> structures = null;
FileBlockHeader dataFileBlock = dataRepository.getFileBlock(oldMemoryAddress);
if(pointerLevel > 1) {
int pointersAmount = dataFileBlock.getSize() / inputStream.getPointerSize() * dataFileBlock.getCount();
for(int i = 0; i < pointersAmount; ++i) {
inputStream.setPosition(dataFileBlock.getBlockPosition() + inputStream.getPointerSize() * i);
long oldMemoryAddress = inputStream.readPointer();
if(oldMemoryAddress != 0L) {
Pointer p = new Pointer(pointerLevel - 1, this.function, dataRepository);
p.oldMemoryAddress = oldMemoryAddress;
if(structures == null) {
structures = p.fetchData(inputStream);
} else {
structures.addAll(p.fetchData(inputStream));
}
}
}
} else {
inputStream.setPosition(dataFileBlock.getBlockPosition());
structures = new ArrayList<Structure>(dataFileBlock.getCount());
for(int i = 0; i < dataFileBlock.getCount(); ++i) {
Structure structure = dataRepository.getDnaBlockData().getStructure(dataFileBlock.getSdnaIndex());
structure.fill(inputStream);
structures.add(structure);
}
return structures;
}
return structures;
}
/** /**
* This method indicates if this pointer points to a function. * This method fetches the data stored under the given address.
* @return <b>true</b> if this is a function pointer and <b>false</b> otherwise * @param inputStream
*/ * the stream we read data from
public boolean isFunction() { * @param dataIndices
return function; * the offset of the data in the table pointed by the pointer
} * @return the data read from the file
* @throws BlenderFileException
* this exception is thrown when the blend file structure is somehow invalid or corrupted
*/
public List<Structure> fetchData(BlenderInputStream inputStream) throws BlenderFileException {
if (oldMemoryAddress == 0) {
throw new NullPointerException("The pointer points to nothing!");
}
List<Structure> structures = null;
FileBlockHeader dataFileBlock = dataRepository.getFileBlock(oldMemoryAddress);
if (pointerLevel > 1) {
int pointersAmount = dataFileBlock.getSize() / inputStream.getPointerSize() * dataFileBlock.getCount();
for (int i = 0; i < pointersAmount; ++i) {
inputStream.setPosition(dataFileBlock.getBlockPosition() + inputStream.getPointerSize() * i);
long oldMemoryAddress = inputStream.readPointer();
if (oldMemoryAddress != 0L) {
Pointer p = new Pointer(pointerLevel - 1, this.function, dataRepository);
p.oldMemoryAddress = oldMemoryAddress;
if (structures == null) {
structures = p.fetchData(inputStream);
} else {
structures.addAll(p.fetchData(inputStream));
}
}
}
} else {
inputStream.setPosition(dataFileBlock.getBlockPosition());
structures = new ArrayList<Structure>(dataFileBlock.getCount());
for (int i = 0; i < dataFileBlock.getCount(); ++i) {
Structure structure = dataRepository.getDnaBlockData().getStructure(dataFileBlock.getSdnaIndex());
structure.fill(inputStream);
structures.add(structure);
}
return structures;
}
return structures;
}
/** /**
* This method indicates if this is a null-pointer or not. * This method indicates if this pointer points to a function.
* @return <b>true</b> if the pointer is null and <b>false</b> otherwise * @return <b>true</b> if this is a function pointer and <b>false</b> otherwise
*/ */
public boolean isNull() { public boolean isFunction() {
return oldMemoryAddress == 0; return function;
} }
/** /**
* This method returns the old memory address of the structure pointed by the pointer. * This method indicates if this is a null-pointer or not.
* @return the old memory address of the structure pointed by the pointer * @return <b>true</b> if the pointer is null and <b>false</b> otherwise
*/ */
public long getOldMemoryAddress() { public boolean isNull() {
return oldMemoryAddress; return oldMemoryAddress == 0;
} }
@Override /**
public String toString() { * This method returns the old memory address of the structure pointed by the pointer.
return oldMemoryAddress == 0 ? "{$null$}" : "{$" + oldMemoryAddress + "$}"; * @return the old memory address of the structure pointed by the pointer
} */
public long getOldMemoryAddress() {
return oldMemoryAddress;
}
@Override @Override
public int hashCode() { public String toString() {
return 31 + (int)(oldMemoryAddress ^ oldMemoryAddress >>> 32); return oldMemoryAddress == 0 ? "{$null$}" : "{$" + oldMemoryAddress + "$}";
} }
@Override @Override
public boolean equals(Object obj) { public int hashCode() {
if(this == obj) { return 31 + (int) (oldMemoryAddress ^ oldMemoryAddress >>> 32);
return true; }
}
if(obj == null) { @Override
return false; public boolean equals(Object obj) {
} if (this == obj) {
if(this.getClass() != obj.getClass()) { return true;
return false; }
} if (obj == null) {
Pointer other = (Pointer)obj; return false;
if(oldMemoryAddress != other.oldMemoryAddress) { }
return false; if (this.getClass() != obj.getClass()) {
} return false;
return true; }
} Pointer other = (Pointer) obj;
if (oldMemoryAddress != other.oldMemoryAddress) {
return false;
}
return true;
}
} }

@ -347,7 +347,6 @@ public final class Bone implements Savable {
/** /**
* Set user transform. * Set user transform.
* Combine the given transforms to bone's current transforms * Combine the given transforms to bone's current transforms
* @see setUserControl
*/ */
public void setUserTransforms(Vector3f translation, Quaternion rotation, Vector3f scale) { public void setUserTransforms(Vector3f translation, Quaternion rotation, Vector3f scale) {
if (!userControl) { if (!userControl) {
@ -378,10 +377,9 @@ public final class Bone implements Savable {
} }
/** /**
* Returns teh local transform of this bone combined with the given position and rotation * Returns the local transform of this bone combined with the given position and rotation
* @param position a position * @param position a position
* @param rotation a rotation * @param rotation a rotation
* @return
*/ */
public Transform getCombinedTransform(Vector3f position, Quaternion rotation){ public Transform getCombinedTransform(Vector3f position, Quaternion rotation){
rotation.mult(localPos, tmpTransform.getTranslation()).addLocal(position); rotation.mult(localPos, tmpTransform.getTranslation()).addLocal(position);

@ -234,13 +234,11 @@ public abstract class CompactArray<T> {
* deserialize object * deserialize object
* @param compactIndex compacted object index * @param compactIndex compacted object index
* @param store * @param store
* @return
*/ */
protected abstract T deserialize(int compactIndex, T store); protected abstract T deserialize(int compactIndex, T store);
/** /**
* serialized size of one object element * serialized size of one object element
* @return
*/ */
protected abstract int getTupleSize(); protected abstract int getTupleSize();

@ -10,7 +10,7 @@
The <code>com.jme3.application</code> provides a toolset for jME3 applications The <code>com.jme3.application</code> provides a toolset for jME3 applications
to interact with various components of the engine. Typically, the to interact with various components of the engine. Typically, the
{@link com.jme3.app.Application} class will be extended and the update() method {@link com.jme3.app.Application} class will be extended and the update() method
implemented to provide functionality for the main loop. <br/> implemented to provide functionality for the main loop. <br>
<p> <p>
An <code>Application</code> will typically provide the following services: An <code>Application</code> will typically provide the following services:
<ul> <ul>
@ -37,42 +37,42 @@ An <code>Application</code> will typically provide the following services:
<h3>Usage</h3> <h3>Usage</h3>
An example use of the Application class is as follows<br/> An example use of the Application class is as follows<br>
<br/> <br>
<code> <code>
public class ExampleUse extends Application {<br/> public class ExampleUse extends Application {<br>
<br/> <br>
private Node rootNode = new Node("Root Node");<br/> private Node rootNode = new Node("Root Node");<br>
<br/> <br>
public static void main(String[] args){<br/> public static void main(String[] args){<br>
ExampleUse app = new ExampleUse();<br/> ExampleUse app = new ExampleUse();<br>
app.start();<br/> app.start();<br>
}<br/> }<br>
<br/> <br>
@Override<br/> @Override<br>
public void initialize(){<br/> public void initialize(){<br>
super.initialize();<br/> super.initialize();<br>
<br/> <br>
// attach root node to viewport<br/> // attach root node to viewport<br>
viewPort.attachScene(rootNode);<br/> viewPort.attachScene(rootNode);<br>
}<br/> }<br>
<br/> <br>
@Override<br/> @Override<br>
public void update(){<br/> public void update(){<br>
super.update();<br/> super.update();<br>
<br/> <br>
float tpf = timer.getTimePerFrame();<br/> float tpf = timer.getTimePerFrame();<br>
<br/> <br>
// update rootNode<br/> // update rootNode<br>
rootNode.updateLogicalState(tpf);<br/> rootNode.updateLogicalState(tpf);<br>
rootNode.updateGeometricState();<br/> rootNode.updateGeometricState();<br>
<br/> <br>
// render the viewports<br/> // render the viewports<br>
renderManager.render(tpf);<br/> renderManager.render(tpf);<br>
}<br/> }<br>
}<br/> }<br>
<br/> <br>
</code> </code>
</body> </body>

@ -173,7 +173,6 @@ public class AppStateManager {
/** /**
* Calls render for all attached states, do not call directly. * Calls render for all attached states, do not call directly.
* @param rm The RenderManager
*/ */
public void postRender(){ public void postRender(){
AppState[] array = getArray(); AppState[] array = getArray();

@ -54,7 +54,7 @@ public interface AssetLocator {
* *
* @param manager * @param manager
* @param key * @param key
* @return * @return The {@link AssetInfo} that was located, or null if not found.
*/ */
public AssetInfo locate(AssetManager manager, AssetKey key); public AssetInfo locate(AssetManager manager, AssetKey key);
} }

@ -159,7 +159,7 @@ public interface AssetManager {
* TGA and DDS. * TGA and DDS.
* *
* @param name The name of the texture to load. * @param name The name of the texture to load.
* @return * @return The texture that was loaded
* *
* @see AssetManager#loadAsset(com.jme3.asset.AssetKey) * @see AssetManager#loadAsset(com.jme3.asset.AssetKey)
*/ */
@ -168,7 +168,7 @@ public interface AssetManager {
/** /**
* Load audio file, supported types are WAV or OGG. * Load audio file, supported types are WAV or OGG.
* @param key * @param key
* @return * @return The audio data loaded
* *
* @see AssetManager#loadAsset(com.jme3.asset.AssetKey) * @see AssetManager#loadAsset(com.jme3.asset.AssetKey)
*/ */
@ -178,7 +178,7 @@ public interface AssetManager {
* Load audio file, supported types are WAV or OGG. * Load audio file, supported types are WAV or OGG.
* The file is loaded without stream-mode. * The file is loaded without stream-mode.
* @param name * @param name
* @return * @return The audio data loaded
* *
* @see AssetManager#loadAsset(com.jme3.asset.AssetKey) * @see AssetManager#loadAsset(com.jme3.asset.AssetKey)
*/ */
@ -188,7 +188,7 @@ public interface AssetManager {
* Loads a named model. Models can be jME3 object files (J3O) or * Loads a named model. Models can be jME3 object files (J3O) or
* OgreXML/OBJ files. * OgreXML/OBJ files.
* @param key * @param key
* @return * @return The model that was loaded
* *
* @see AssetManager#loadAsset(com.jme3.asset.AssetKey) * @see AssetManager#loadAsset(com.jme3.asset.AssetKey)
*/ */
@ -198,7 +198,7 @@ public interface AssetManager {
* Loads a named model. Models can be jME3 object files (J3O) or * Loads a named model. Models can be jME3 object files (J3O) or
* OgreXML/OBJ files. * OgreXML/OBJ files.
* @param name * @param name
* @return * @return The model that was loaded
* *
* @see AssetManager#loadAsset(com.jme3.asset.AssetKey) * @see AssetManager#loadAsset(com.jme3.asset.AssetKey)
*/ */
@ -207,7 +207,7 @@ public interface AssetManager {
/** /**
* Load a material (J3M) file. * Load a material (J3M) file.
* @param name * @param name
* @return * @return The material that was loaded
* *
* @see AssetManager#loadAsset(com.jme3.asset.AssetKey) * @see AssetManager#loadAsset(com.jme3.asset.AssetKey)
*/ */
@ -225,7 +225,7 @@ public interface AssetManager {
* and are with the extension "fnt". * and are with the extension "fnt".
* *
* @param name * @param name
* @return * @return The font loaded
* *
* @see AssetManager#loadAsset(com.jme3.asset.AssetKey) * @see AssetManager#loadAsset(com.jme3.asset.AssetKey)
*/ */

@ -70,7 +70,7 @@ public class TextureKey extends AssetKey<Texture> {
/** /**
* Enable smart caching for textures * Enable smart caching for textures
* @return * @return true to enable smart cache
*/ */
@Override @Override
public boolean useSmartCache(){ public boolean useSmartCache(){

@ -48,7 +48,7 @@ public interface AudioRenderer {
/** /**
* Sets the environment, used for reverb effects. * Sets the environment, used for reverb effects.
* *
* @see PointAudioSource#setReverbEnabled(boolean) * @see AudioNode#setReverbEnabled(boolean)
* @param env The environment to set. * @param env The environment to set.
*/ */
public void setEnvironment(Environment env); public void setEnvironment(Environment env);

@ -259,12 +259,8 @@ public class BoundingBox extends BoundingVolume {
* <code>transform</code> modifies the center of the box to reflect the * <code>transform</code> modifies the center of the box to reflect the
* change made via a rotation, translation and scale. * change made via a rotation, translation and scale.
* *
* @param rotate * @param trans
* the rotation change. * the transform to apply
* @param translate
* the translation change.
* @param scale
* the size change.
* @param store * @param store
* box to store result in * box to store result in
*/ */
@ -570,7 +566,7 @@ public class BoundingBox extends BoundingVolume {
* intersects determines if this Bounding Box intersects with another given * intersects determines if this Bounding Box intersects with another given
* bounding volume. If so, true is returned, otherwise, false is returned. * bounding volume. If so, true is returned, otherwise, false is returned.
* *
* @see com.jme.bounding.BoundingVolume#intersects(com.jme.bounding.BoundingVolume) * @see BoundingVolume#intersects(com.jme3.bounding.BoundingVolume)
*/ */
public boolean intersects(BoundingVolume bv) { public boolean intersects(BoundingVolume bv) {
return bv.intersectsBoundingBox(this); return bv.intersectsBoundingBox(this);
@ -579,7 +575,7 @@ public class BoundingBox extends BoundingVolume {
/** /**
* determines if this bounding box intersects a given bounding sphere. * determines if this bounding box intersects a given bounding sphere.
* *
* @see com.jme.bounding.BoundingVolume#intersectsSphere(com.jme.bounding.BoundingSphere) * @see BoundingVolume#intersectsSphere(com.jme3.bounding.BoundingSphere)
*/ */
public boolean intersectsSphere(BoundingSphere bs) { public boolean intersectsSphere(BoundingSphere bs) {
assert Vector3f.isValidVector(center) && Vector3f.isValidVector(bs.center); assert Vector3f.isValidVector(center) && Vector3f.isValidVector(bs.center);
@ -600,7 +596,7 @@ public class BoundingBox extends BoundingVolume {
* two boxes intersect in any way, true is returned. Otherwise, false is * two boxes intersect in any way, true is returned. Otherwise, false is
* returned. * returned.
* *
* @see com.jme.bounding.BoundingVolume#intersectsBoundingBox(com.jme.bounding.BoundingBox) * @see BoundingVolume#intersectsBoundingBox(com.jme3.bounding.BoundingBox)
*/ */
public boolean intersectsBoundingBox(BoundingBox bb) { public boolean intersectsBoundingBox(BoundingBox bb) {
assert Vector3f.isValidVector(center) && Vector3f.isValidVector(bb.center); assert Vector3f.isValidVector(center) && Vector3f.isValidVector(bb.center);
@ -632,7 +628,7 @@ public class BoundingBox extends BoundingVolume {
* determines if this bounding box intersects with a given ray object. If an * determines if this bounding box intersects with a given ray object. If an
* intersection has occurred, true is returned, otherwise false is returned. * intersection has occurred, true is returned, otherwise false is returned.
* *
* @see com.jme.bounding.BoundingVolume#intersects(com.jme.math.Ray) * @see BoundingVolume#intersects(com.jme3.math.Ray)
*/ */
public boolean intersects(Ray ray) { public boolean intersects(Ray ray) {
assert Vector3f.isValidVector(center); assert Vector3f.isValidVector(center);
@ -766,10 +762,11 @@ public class BoundingBox extends BoundingVolume {
/** /**
* C code ported from http://www.cs.lth.se/home/Tomas_Akenine_Moller/code/tribox3.txt * C code ported from http://www.cs.lth.se/home/Tomas_Akenine_Moller/code/tribox3.txt
* *
* @param v1 * @param v1 The first point in the triangle
* @param v2 * @param v2 The second point in the triangle
* @param v3 * @param v3 The third point in the triangle
* @return * @return True if the bounding box intersects the triangle, false
* otherwise.
*/ */
public boolean intersects(Vector3f v1, Vector3f v2, Vector3f v3){ public boolean intersects(Vector3f v1, Vector3f v2, Vector3f v3){
return Intersection.intersect(this, v1, v2, v3); return Intersection.intersect(this, v1, v2, v3);

@ -387,12 +387,8 @@ public class BoundingSphere extends BoundingVolume {
* <code>transform</code> modifies the center of the sphere to reflect the * <code>transform</code> modifies the center of the sphere to reflect the
* change made via a rotation, translation and scale. * change made via a rotation, translation and scale.
* *
* @param rotate * @param trans
* the rotation change. * the transform to apply
* @param translate
* the translation change.
* @param scale
* the size change.
* @param store * @param store
* sphere to store result in * sphere to store result in
* @return BoundingVolume * @return BoundingVolume

@ -32,6 +32,8 @@
package com.jme3.input.controls; package com.jme3.input.controls;
import com.jme3.input.MouseInput;
/** /**
* A <code>MouseButtonTrigger</code> is used as a mapping to receive events * A <code>MouseButtonTrigger</code> is used as a mapping to receive events
* from mouse buttons. It is generally expected for a mouse to have at least * from mouse buttons. It is generally expected for a mouse to have at least

@ -160,7 +160,8 @@ public abstract class Light implements Savable, Cloneable {
this.color.set(color); this.color.set(color);
} }
/**
/*
* Returns true if the light is enabled * Returns true if the light is enabled
* *
* @return true if the light is enabled * @return true if the light is enabled

@ -193,8 +193,7 @@ public class Material implements Cloneable, Savable, Comparable<Material> {
} }
/** /**
* Clones this material. The result * Clones this material. The result is returned.
* @return
*/ */
@Override @Override
public Material clone() { public Material clone() {
@ -219,10 +218,27 @@ public class Material implements Cloneable, Savable, Comparable<Material> {
} }
} }
/**
* Returns the currently active technique.
* <p>
* The technique is selected automatically by the {@link RenderManager}
* based on system capabilities. Users may select their own
* technique by using
* {@link #selectTechnique(java.lang.String, com.jme3.renderer.RenderManager) }.
*
* @return the currently active technique.
*
* @see #selectTechnique(java.lang.String, com.jme3.renderer.RenderManager)
*/
public Technique getActiveTechnique() { public Technique getActiveTechnique() {
return technique; return technique;
} }
/**
* Check if the transparent value marker is set on this material.
* @return True if the transparent value marker is set on this material.
* @see #setTransparent(boolean)
*/
public boolean isTransparent() { public boolean isTransparent() {
return transparent; return transparent;
} }
@ -296,6 +312,13 @@ public class Material implements Cloneable, Savable, Comparable<Material> {
return def; return def;
} }
/**
* Returns the parameter set on this material with the given name,
* returns <code>null</code> if the parameter is not set.
*
* @param name The parameter name to look up.
* @return The MatParam if set, or null if not set.
*/
public MatParam getParam(String name) { public MatParam getParam(String name) {
MatParam param = paramValues.get(name); MatParam param = paramValues.get(name);
if (param instanceof MatParam) { if (param instanceof MatParam) {
@ -304,6 +327,13 @@ public class Material implements Cloneable, Savable, Comparable<Material> {
return null; return null;
} }
/**
* Returns the texture parameter set on this material with the given name,
* returns <code>null</code> if the parameter is not set.
*
* @param name The parameter name to look up.
* @return The MatParamTexture if set, or null if not set.
*/
public MatParamTexture getTextureParam(String name) { public MatParamTexture getTextureParam(String name) {
MatParam param = paramValues.get(name); MatParam param = paramValues.get(name);
if (param instanceof MatParamTexture) { if (param instanceof MatParamTexture) {
@ -312,6 +342,13 @@ public class Material implements Cloneable, Savable, Comparable<Material> {
return null; return null;
} }
/**
* Returns a collection of all parameters set on this material.
*
* @return a collection of all parameters set on this material.
*
* @see #setParam(java.lang.String, com.jme3.shader.VarType, java.lang.Object)
*/
public Collection<MatParam> getParams() { public Collection<MatParam> getParams() {
return paramValues.values(); return paramValues.values();
} }
@ -342,10 +379,11 @@ public class Material implements Cloneable, Savable, Comparable<Material> {
} }
/** /**
* Pass a parameter to the material shader * Pass a parameter to the material shader.
*
* @param name the name of the parameter defined in the material definition (j3md) * @param name the name of the parameter defined in the material definition (j3md)
* @param type the type of the parameter @see com.jme3.shaderVarType * @param type the type of the parameter {@link VarType}
* @param value the value of the param * @param value the value of the parameter
*/ */
public void setParam(String name, VarType type, Object value) { public void setParam(String name, VarType type, Object value) {
name = checkSetParam(type, name); name = checkSetParam(type, name);
@ -363,7 +401,7 @@ public class Material implements Cloneable, Savable, Comparable<Material> {
} }
/** /**
* Clear a parameter from this material. The param must exist * Clear a parameter from this material. The parameter must exist
* @param name the name of the parameter to clear * @param name the name of the parameter to clear
*/ */
public void clearParam(String name) { public void clearParam(String name) {
@ -416,9 +454,18 @@ public class Material implements Cloneable, Savable, Comparable<Material> {
sortingId = -1; sortingId = -1;
} }
/**
* Set a texture parameter.
*
* @param name The name of the parameter
* @param type The variable type {@link VarType}
* @param value The texture value of the parameter.
*
* @throws IllegalArgumentException is value is null
*/
public void setTextureParam(String name, VarType type, Texture value) { public void setTextureParam(String name, VarType type, Texture value) {
if (value == null) { if (value == null) {
throw new NullPointerException(); throw new IllegalArgumentException();
} }
name = checkSetParam(type, name); name = checkSetParam(type, name);
@ -438,8 +485,10 @@ public class Material implements Cloneable, Savable, Comparable<Material> {
} }
/** /**
* Pass a texture to the material shader * Pass a texture to the material shader.
* @param name the name of the texture defined in the material definition (j3md) (for example Texture for Lighting.j3md) *
* @param name the name of the texture defined in the material definition
* (j3md) (for example Texture for Lighting.j3md)
* @param value the Texture object previously loaded by the asset manager * @param value the Texture object previously loaded by the asset manager
*/ */
public void setTexture(String name, Texture value) { public void setTexture(String name, Texture value) {
@ -471,7 +520,8 @@ public class Material implements Cloneable, Savable, Comparable<Material> {
} }
/** /**
* Pass a Matrix4f to the material shader * Pass a Matrix4f to the material shader.
*
* @param name the name of the matrix defined in the material definition (j3md) * @param name the name of the matrix defined in the material definition (j3md)
* @param value the Matrix4f object * @param value the Matrix4f object
*/ */
@ -480,7 +530,8 @@ public class Material implements Cloneable, Savable, Comparable<Material> {
} }
/** /**
* Pass a boolean to the material shader * Pass a boolean to the material shader.
*
* @param name the name of the boolean defined in the material definition (j3md) * @param name the name of the boolean defined in the material definition (j3md)
* @param value the boolean value * @param value the boolean value
*/ */
@ -489,7 +540,8 @@ public class Material implements Cloneable, Savable, Comparable<Material> {
} }
/** /**
* Pass a float to the material shader * Pass a float to the material shader.
*
* @param name the name of the float defined in the material definition (j3md) * @param name the name of the float defined in the material definition (j3md)
* @param value the float value * @param value the float value
*/ */
@ -498,7 +550,8 @@ public class Material implements Cloneable, Savable, Comparable<Material> {
} }
/** /**
* Pass an int to the material shader * Pass an int to the material shader.
*
* @param name the name of the int defined in the material definition (j3md) * @param name the name of the int defined in the material definition (j3md)
* @param value the int value * @param value the int value
*/ */
@ -507,7 +560,8 @@ public class Material implements Cloneable, Savable, Comparable<Material> {
} }
/** /**
* Pass a Color to the material shader * Pass a Color to the material shader.
*
* @param name the name of the color defined in the material definition (j3md) * @param name the name of the color defined in the material definition (j3md)
* @param value the ColorRGBA value * @param value the ColorRGBA value
*/ */
@ -516,7 +570,8 @@ public class Material implements Cloneable, Savable, Comparable<Material> {
} }
/** /**
* Pass a Vector2f to the material shader * Pass a Vector2f to the material shader.
*
* @param name the name of the Vector2f defined in the material definition (j3md) * @param name the name of the Vector2f defined in the material definition (j3md)
* @param value the Vector2f value * @param value the Vector2f value
*/ */
@ -525,7 +580,8 @@ public class Material implements Cloneable, Savable, Comparable<Material> {
} }
/** /**
* Pass a Vector3f to the material shader * Pass a Vector3f to the material shader.
*
* @param name the name of the Vector3f defined in the material definition (j3md) * @param name the name of the Vector3f defined in the material definition (j3md)
* @param value the Vector3f value * @param value the Vector3f value
*/ */
@ -534,7 +590,8 @@ public class Material implements Cloneable, Savable, Comparable<Material> {
} }
/** /**
* Pass a Vector4f to the material shader * Pass a Vector4f to the material shader.
*
* @param name the name of the Vector4f defined in the material definition (j3md) * @param name the name of the Vector4f defined in the material definition (j3md)
* @param value the Vector4f value * @param value the Vector4f value
*/ */
@ -567,9 +624,6 @@ public class Material implements Cloneable, Savable, Comparable<Material> {
* // or the direction of the light (for directional lights).<br/> * // or the direction of the light (for directional lights).<br/>
* // g_LightPosition.w is the inverse radius (1/r) of the light (for attenuation) <br/> * // g_LightPosition.w is the inverse radius (1/r) of the light (for attenuation) <br/>
* </p> * </p>
*
* @param shader
* @param lightList
*/ */
protected void updateLightListUniforms(Shader shader, Geometry g, int numLights) { protected void updateLightListUniforms(Shader shader, Geometry g, int numLights) {
if (numLights == 0){ // this shader does not do lighting, ignore. if (numLights == 0){ // this shader does not do lighting, ignore.
@ -719,6 +773,29 @@ public class Material implements Cloneable, Savable, Comparable<Material> {
} }
} }
/**
* Select the technique to use for rendering this material.
* <p>
* If <code>name</code> is "Default", then one of the
* {@link MaterialDef#getDefaultTechniques() default techniques}
* on the material will be selected. Otherwise, the named technique
* will be found in the material definition.
* <p>
* Any candidate technique for selection (either default or named)
* must be verified to be compatible with the system, for that, the
* <code>renderManager</code> is queried for capabilities.
*
* @param name The name of the technique to select, pass "Default" to
* select one of the default techniques.
* @param renderManager The {@link RenderManager render manager}
* to query for capabilities.
*
* @throws IllegalArgumentException If "Default" is passed and no default
* techniques are available on the material definition, or if a name
* is passed but there's no technique by that name.
* @throws UnsupportedOperationException If no candidate technique supports
* the system capabilities.
*/
public void selectTechnique(String name, RenderManager renderManager) { public void selectTechnique(String name, RenderManager renderManager) {
// check if already created // check if already created
Technique tech = techniques.get(name); Technique tech = techniques.get(name);
@ -730,7 +807,7 @@ public class Material implements Cloneable, Savable, Comparable<Material> {
if (name.equals("Default")) { if (name.equals("Default")) {
List<TechniqueDef> techDefs = def.getDefaultTechniques(); List<TechniqueDef> techDefs = def.getDefaultTechniques();
if (techDefs == null || techDefs.isEmpty()) { if (techDefs == null || techDefs.isEmpty()) {
throw new IllegalStateException("No default techniques are available on material '" + def.getName() + "'"); throw new IllegalArgumentException("No default techniques are available on material '" + def.getName() + "'");
} }
TechniqueDef lastTech = null; TechniqueDef lastTech = null;
@ -794,8 +871,13 @@ public class Material implements Cloneable, Savable, Comparable<Material> {
} }
/** /**
* "Pre-load" the material, including textures and shaders, to the * Preloads this material for the given render manager.
* renderer. * <p>
* Preloading the material can ensure that when the material is first
* used for rendering, there won't be any delay since the material has
* been already been setup for rendering.
*
* @param rm The render manager to preload for
*/ */
public void preload(RenderManager rm) { public void preload(RenderManager rm) {
autoSelectTechnique(rm); autoSelectTechnique(rm);
@ -844,9 +926,11 @@ public class Material implements Cloneable, Savable, Comparable<Material> {
} }
/** /**
* Should be called after selectTechnique() * Called by {@link RenderManager} to render the geometry by
* @param geom * using this material.
* @param r *
* @param geom The geometry to render
* @param rm The render manager requesting the rendering
*/ */
public void render(Geometry geom, RenderManager rm) { public void render(Geometry geom, RenderManager rm) {
autoSelectTechnique(rm); autoSelectTechnique(rm);

@ -176,6 +176,15 @@ public class Technique implements Savable {
updateUniformParam(paramName, type, value, false); updateUniformParam(paramName, type, value, false);
} }
/**
* Returns true if the technique must be reloaded.
* <p>
* If a technique needs to reload, then the {@link Material} should
* call {@link #makeCurrent(com.jme3.asset.AssetManager) } on this
* technique.
*
* @return true if the technique must be reloaded.
*/
public boolean isNeedReload() { public boolean isNeedReload() {
return needReload; return needReload;
} }
@ -183,8 +192,10 @@ public class Technique implements Savable {
/** /**
* Prepares the technique for use by loading the shader and setting * Prepares the technique for use by loading the shader and setting
* the proper defines based on material parameters. * the proper defines based on material parameters.
*
* @param assetManager The asset manager to use for loading shaders.
*/ */
public void makeCurrent(AssetManager manager) { public void makeCurrent(AssetManager assetManager) {
// check if reload is needed.. // check if reload is needed..
if (def.isUsingShaders()) { if (def.isUsingShaders()) {
DefineList newDefines = new DefineList(); DefineList newDefines = new DefineList();
@ -203,7 +214,7 @@ public class Technique implements Savable {
defines.clear(); defines.clear();
defines.addFrom(newDefines); defines.addFrom(newDefines);
// defines changed, recompile needed // defines changed, recompile needed
loadShader(manager); loadShader(assetManager);
} }
} }
} }
@ -214,8 +225,8 @@ public class Technique implements Savable {
allDefines.addFrom(def.getShaderPresetDefines()); allDefines.addFrom(def.getShaderPresetDefines());
allDefines.addFrom(defines); allDefines.addFrom(defines);
ShaderKey key = new ShaderKey(def.getVertName(), ShaderKey key = new ShaderKey(def.getVertexShaderName(),
def.getFragName(), def.getFragmentShaderName(),
allDefines, allDefines,
def.getShaderLanguage()); def.getShaderLanguage());
shader = manager.loadShader(key); shader = manager.loadShader(key);

@ -38,6 +38,7 @@ import com.jme3.export.InputCapsule;
import com.jme3.export.OutputCapsule; import com.jme3.export.OutputCapsule;
import com.jme3.export.Savable; import com.jme3.export.Savable;
import com.jme3.renderer.Caps; import com.jme3.renderer.Caps;
import com.jme3.renderer.Renderer;
import com.jme3.shader.DefineList; import com.jme3.shader.DefineList;
import com.jme3.shader.UniformBinding; import com.jme3.shader.UniformBinding;
import com.jme3.shader.VarType; import com.jme3.shader.VarType;
@ -47,12 +48,49 @@ import java.util.EnumSet;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
/**
* Describes a technique definition.
*
* @author Kirill Vainer
*/
public class TechniqueDef implements Savable { public class TechniqueDef implements Savable {
/**
* Describes light rendering mode.
*/
public enum LightMode { public enum LightMode {
/**
* Disable light-based rendering
*/
Disable, Disable,
/**
* Enable light rendering by using a single pass.
* <p>
* An array of light positions and light colors is passed to the shader
* containing the world light list for the geometry being rendered.
*/
SinglePass, SinglePass,
/**
* Enable light rendering by using multi-pass rendering.
* <p>
* The geometry will be rendered once for each light. Each time the
* light position and light color uniforms are updated to contain
* the values for the current light. The ambient light color uniform
* is only set to the ambient light color on the first pass, future
* passes have it set to black.
*/
MultiPass, MultiPass,
/**
* Enable light rendering by using the
* {@link Renderer#setLighting(com.jme3.light.LightList) renderer's setLighting}
* method.
* <p>
* The specific details of rendering the lighting is up to the
* renderer implementation.
*/
FixedPipeline, FixedPipeline,
} }
@ -78,97 +116,140 @@ public class TechniqueDef implements Savable {
private HashMap<String, String> defineParams; private HashMap<String, String> defineParams;
private ArrayList<UniformBinding> worldBinds; private ArrayList<UniformBinding> worldBinds;
/**
* Creates a new technique definition.
* <p>
* Used internally by the J3M/J3MD loader.
*
* @param name The name of the technique, should be set to <code>null</code>
* for default techniques.
*/
public TechniqueDef(String name){ public TechniqueDef(String name){
this.name = name == null ? "Default" : name; this.name = name == null ? "Default" : name;
} }
/** /**
* Do not use this constructor. * Serialization only. Do not use.
*/ */
public TechniqueDef(){ public TechniqueDef(){
} }
public void write(JmeExporter ex) throws IOException{ /**
OutputCapsule oc = ex.getCapsule(this); * Returns the name of this technique as specified in the J3MD file.
oc.write(name, "name", null); * Default techniques have the name "Default".
oc.write(vertName, "vertName", null); *
oc.write(fragName, "fragName", null); * @return the name of this technique
oc.write(shaderLang, "shaderLang", null); */
oc.write(presetDefines, "presetDefines", null);
oc.write(lightMode, "lightMode", LightMode.Disable);
oc.write(shadowMode, "shadowMode", ShadowMode.Disable);
oc.write(renderState, "renderState", null);
oc.write(usesShaders, "usesShaders", false);
// TODO: Finish this when Map<String, String> export is available
// oc.write(defineParams, "defineParams", null);
// TODO: Finish this when List<Enum> export is available
// oc.write(worldBinds, "worldBinds", null);
}
public void read(JmeImporter im) throws IOException{
InputCapsule ic = im.getCapsule(this);
name = ic.readString("name", null);
vertName = ic.readString("vertName", null);
fragName = ic.readString("fragName", null);
shaderLang = ic.readString("shaderLang", null);
presetDefines = (DefineList) ic.readSavable("presetDefines", null);
lightMode = ic.readEnum("lightMode", LightMode.class, LightMode.Disable);
shadowMode = ic.readEnum("shadowMode", ShadowMode.class, ShadowMode.Disable);
renderState = (RenderState) ic.readSavable("renderState", null);
usesShaders = ic.readBoolean("usesShaders", false);
}
public String getName(){ public String getName(){
return name; return name;
} }
/**
* Returns the light mode.
* @return the light mode.
* @see LightMode
*/
public LightMode getLightMode() { public LightMode getLightMode() {
return lightMode; return lightMode;
} }
/**
* Set the light mode
*
* @param lightMode the light mode
*
* @see LightMode
*/
public void setLightMode(LightMode lightMode) { public void setLightMode(LightMode lightMode) {
this.lightMode = lightMode; this.lightMode = lightMode;
} }
/**
* Returns the shadow mode.
* @return the shadow mode.
*/
public ShadowMode getShadowMode() { public ShadowMode getShadowMode() {
return shadowMode; return shadowMode;
} }
/**
* Set the shadow mode.
*
* @param shadowMode the shadow mode.
*
* @see ShadowMode
*/
public void setShadowMode(ShadowMode shadowMode) { public void setShadowMode(ShadowMode shadowMode) {
this.shadowMode = shadowMode; this.shadowMode = shadowMode;
} }
/**
* Returns the render state that this technique is using
* @return the render state that this technique is using
* @see #setRenderState(com.jme3.material.RenderState)
*/
public RenderState getRenderState() { public RenderState getRenderState() {
return renderState; return renderState;
} }
/**
* Sets the render state that this technique is using.
*
* @param renderState the render state that this technique is using.
*
* @see RenderState
*/
public void setRenderState(RenderState renderState) { public void setRenderState(RenderState renderState) {
this.renderState = renderState; this.renderState = renderState;
} }
/**
* Returns true if this technique uses shaders, false otherwise.
*
* @return true if this technique uses shaders, false otherwise.
*
* @see #setShaderFile(java.lang.String, java.lang.String, java.lang.String)
*/
public boolean isUsingShaders(){ public boolean isUsingShaders(){
return usesShaders; return usesShaders;
} }
/**
* Gets the {@link Caps renderer capabilities} that are required
* by this technique.
*
* @return the required renderer capabilities
*/
public EnumSet<Caps> getRequiredCaps() { public EnumSet<Caps> getRequiredCaps() {
return requiredCaps; return requiredCaps;
} }
public void setShaderFile(String vert, String frag, String lang){ /**
this.vertName = vert; * Sets the shaders that this technique definition will use.
this.fragName = frag; *
this.shaderLang = lang; * @param vertexShader The name of the vertex shader
* @param fragmentShader The name of the fragment shader
* @param shaderLanguage The shader language
*/
public void setShaderFile(String vertexShader, String fragmentShader, String shaderLanguage){
this.vertName = vertexShader;
this.fragName = fragmentShader;
this.shaderLang = shaderLanguage;
Caps langCap = Caps.valueOf(lang); Caps langCap = Caps.valueOf(shaderLanguage);
requiredCaps.add(langCap); requiredCaps.add(langCap);
usesShaders = true; usesShaders = true;
} }
public DefineList getShaderPresetDefines() { /**
return presetDefines; * Returns the define name which the given material parameter influences.
} *
* @param paramName The parameter name to look up
* @return The define name
*
* @see #addShaderParamDefine(java.lang.String, java.lang.String)
*/
public String getShaderParamDefine(String paramName){ public String getShaderParamDefine(String paramName){
if (defineParams == null) if (defineParams == null)
return null; return null;
@ -176,6 +257,18 @@ public class TechniqueDef implements Savable {
return defineParams.get(paramName); return defineParams.get(paramName);
} }
/**
* Adds a define linked to a material parameter.
* <p>
* Any time the material parameter on the parent material is altered,
* the appropriate define on the technique will be modified as well.
* See the method
* {@link DefineList#set(java.lang.String, com.jme3.shader.VarType, java.lang.Object) }
* on the exact details of how the material parameter changes the define.
*
* @param paramName The name of the material parameter to link to.
* @param defineName The name of the define parameter, e.g. USE_LIGHTING
*/
public void addShaderParamDefine(String paramName, String defineName){ public void addShaderParamDefine(String paramName, String defineName){
if (defineParams == null) if (defineParams == null)
defineParams = new HashMap<String, String>(); defineParams = new HashMap<String, String>();
@ -183,6 +276,30 @@ public class TechniqueDef implements Savable {
defineParams.put(paramName, defineName); defineParams.put(paramName, defineName);
} }
/**
* Returns the {@link DefineList} for the preset defines.
*
* @return the {@link DefineList} for the preset defines.
*
* @see #addShaderPresetDefine(java.lang.String, com.jme3.shader.VarType, java.lang.Object)
*/
public DefineList getShaderPresetDefines() {
return presetDefines;
}
/**
* Adds a preset define.
* <p>
* Preset defines do not depend upon any parameters to be activated,
* they are always passed to the shader as long as this technique is used.
*
* @param defineName The name of the define parameter, e.g. USE_LIGHTING
* @param type The type of the define. See
* {@link DefineList#set(java.lang.String, com.jme3.shader.VarType, java.lang.Object) }
* to see why it matters.
*
* @param value The value of the define
*/
public void addShaderPresetDefine(String defineName, VarType type, Object value){ public void addShaderPresetDefine(String defineName, VarType type, Object value){
if (presetDefines == null) if (presetDefines == null)
presetDefines = new DefineList(); presetDefines = new DefineList();
@ -190,33 +307,94 @@ public class TechniqueDef implements Savable {
presetDefines.set(defineName, type, value); presetDefines.set(defineName, type, value);
} }
public String getFragName() { /**
* Returns the name of the fragment shader used by the technique, or null
* if no fragment shader is specified.
*
* @return the name of the fragment shader to be used.
*/
public String getFragmentShaderName() {
return fragName; return fragName;
} }
public String getVertName() {
/**
* Returns the name of the vertex shader used by the technique, or null
* if no vertex shader is specified.
*
* @return the name of the vertex shader to be used.
*/
public String getVertexShaderName() {
return vertName; return vertName;
} }
/**
* Returns the shader language of the shaders used in this technique.
*
* @return the shader language of the shaders used in this technique.
*/
public String getShaderLanguage() { public String getShaderLanguage() {
return shaderLang; return shaderLang;
} }
/**
* Adds a new world parameter by the given name.
*
* @param name The world parameter to add.
* @return True if the world parameter name was found and added
* to the list of world parameters, false otherwise.
*/
public boolean addWorldParam(String name) { public boolean addWorldParam(String name) {
if (worldBinds == null){ if (worldBinds == null){
worldBinds = new ArrayList<UniformBinding>(); worldBinds = new ArrayList<UniformBinding>();
} }
for (UniformBinding binding : UniformBinding.values()) {
if (binding.name().equals(name)) { try {
worldBinds.add(binding); worldBinds.add( UniformBinding.valueOf(name) );
return true; return true;
} } catch (IllegalArgumentException ex){
return false;
} }
return false;
} }
/**
* Returns a list of world parameters that are used by this
* technique definition.
*
* @return The list of world parameters
*/
public List<UniformBinding> getWorldBindings() { public List<UniformBinding> getWorldBindings() {
return worldBinds; return worldBinds;
} }
public void write(JmeExporter ex) throws IOException{
OutputCapsule oc = ex.getCapsule(this);
oc.write(name, "name", null);
oc.write(vertName, "vertName", null);
oc.write(fragName, "fragName", null);
oc.write(shaderLang, "shaderLang", null);
oc.write(presetDefines, "presetDefines", null);
oc.write(lightMode, "lightMode", LightMode.Disable);
oc.write(shadowMode, "shadowMode", ShadowMode.Disable);
oc.write(renderState, "renderState", null);
oc.write(usesShaders, "usesShaders", false);
// TODO: Finish this when Map<String, String> export is available
// oc.write(defineParams, "defineParams", null);
// TODO: Finish this when List<Enum> export is available
// oc.write(worldBinds, "worldBinds", null);
}
public void read(JmeImporter im) throws IOException{
InputCapsule ic = im.getCapsule(this);
name = ic.readString("name", null);
vertName = ic.readString("vertName", null);
fragName = ic.readString("fragName", null);
shaderLang = ic.readString("shaderLang", null);
presetDefines = (DefineList) ic.readSavable("presetDefines", null);
lightMode = ic.readEnum("lightMode", LightMode.class, LightMode.Disable);
shadowMode = ic.readEnum("shadowMode", ShadowMode.class, ShadowMode.Disable);
renderState = (RenderState) ic.readSavable("renderState", null);
usesShaders = ic.readBoolean("usesShaders", false);
}
} }

@ -9,7 +9,7 @@
The <code>com.jme3.material</code> package contains classes for manipulating The <code>com.jme3.material</code> package contains classes for manipulating
jMonkeyEngine materials. jMonkeyEngine materials.
Materials are applied to {@link com.jme3.scene.Geoemtry geometries} in the Materials are applied to {@link com.jme3.scene.Geometry geometries} in the
scene. scene.
Each geometry has a single material which is used to render that Each geometry has a single material which is used to render that
geometry. geometry.

@ -1197,8 +1197,7 @@ public final class Matrix4f implements Savable, Cloneable {
* *
* @param vec * @param vec
* vec to multiply against. * vec to multiply against.
* @param store *
* a vector to store the result in. created if null is passed.
* @return the rotated vector. * @return the rotated vector.
*/ */
public Vector4f multAcross(Vector4f vec) { public Vector4f multAcross(Vector4f vec) {

@ -455,7 +455,8 @@ public final class Ray implements Savable, Cloneable, Collidable {
* <code>getLimit</code> returns the limit or the ray, aka the length. * <code>getLimit</code> returns the limit or the ray, aka the length.
* If the limit is not infinity, then this ray is a line with length <code> * If the limit is not infinity, then this ray is a line with length <code>
* limit</code>. * limit</code>.
* @return *
* @return the limit or the ray, aka the length.
*/ */
public float getLimit(){ public float getLimit(){
return limit; return limit;

@ -277,7 +277,6 @@ public class Spline implements Savable {
/** /**
* returns the curve tension * returns the curve tension
* @return
*/ */
public float getCurveTension() { public float getCurveTension() {
return curveTension; return curveTension;
@ -297,7 +296,6 @@ public class Spline implements Savable {
/** /**
* returns true if the spline cycle * returns true if the spline cycle
* @return
*/ */
public boolean isCycle() { public boolean isCycle() {
return cycle; return cycle;
@ -326,7 +324,6 @@ public class Spline implements Savable {
/** /**
* return the total lenght of the spline * return the total lenght of the spline
* @return
*/ */
public float getTotalLength() { public float getTotalLength() {
return totalLength; return totalLength;
@ -334,7 +331,6 @@ public class Spline implements Savable {
/** /**
* return the type of the spline * return the type of the spline
* @return
*/ */
public SplineType getType() { public SplineType getType() {
return type; return type;
@ -351,7 +347,6 @@ public class Spline implements Savable {
/** /**
* returns this spline control points * returns this spline control points
* @return
*/ */
public List<Vector3f> getControlPoints() { public List<Vector3f> getControlPoints() {
return controlPoints; return controlPoints;
@ -359,7 +354,6 @@ public class Spline implements Savable {
/** /**
* returns a list of float representing the segments lenght * returns a list of float representing the segments lenght
* @return
*/ */
public List<Float> getSegmentsLength() { public List<Float> getSegmentsLength() {
return segmentsLength; return segmentsLength;

@ -29,7 +29,6 @@
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.jme3.math; package com.jme3.math;
import com.jme3.export.JmeExporter; import com.jme3.export.JmeExporter;
@ -50,15 +49,13 @@ public class Triangle extends AbstractTriangle implements Savable {
private Vector3f pointa = new Vector3f(); private Vector3f pointa = new Vector3f();
private Vector3f pointb = new Vector3f(); private Vector3f pointb = new Vector3f();
private Vector3f pointc = new Vector3f(); private Vector3f pointc = new Vector3f();
private transient Vector3f center; private transient Vector3f center;
private transient Vector3f normal; private transient Vector3f normal;
private float projection; private float projection;
private int index; private int index;
public Triangle() {} public Triangle() {
}
/** /**
* Constructor instantiates a new <Code>Triangle</code> object with the * Constructor instantiates a new <Code>Triangle</code> object with the
@ -84,22 +81,26 @@ public class Triangle extends AbstractTriangle implements Savable {
*/ */
public Vector3f get(int i) { public Vector3f get(int i) {
switch (i) { switch (i) {
case 0: return pointa; case 0:
case 1: return pointb; return pointa;
case 2: return pointc; case 1:
default: return null; return pointb;
case 2:
return pointc;
default:
return null;
} }
} }
public Vector3f get1(){ public Vector3f get1() {
return pointa; return pointa;
} }
public Vector3f get2(){ public Vector3f get2() {
return pointb; return pointb;
} }
public Vector3f get3(){ public Vector3f get3() {
return pointc; return pointc;
} }
@ -112,9 +113,15 @@ public class Triangle extends AbstractTriangle implements Savable {
*/ */
public void set(int i, Vector3f point) { public void set(int i, Vector3f point) {
switch (i) { switch (i) {
case 0: pointa.set(point); break; case 0:
case 1: pointb.set(point); break; pointa.set(point);
case 2: pointc.set(point); break; break;
case 1:
pointb.set(point);
break;
case 2:
pointc.set(point);
break;
} }
} }
@ -123,29 +130,34 @@ public class Triangle extends AbstractTriangle implements Savable {
* <code>set</code> sets one of the triangles points to that specified as * <code>set</code> sets one of the triangles points to that specified as
* a parameter. * a parameter.
* @param i the index to place the point. * @param i the index to place the point.
* @param point the point to set.
*/ */
public void set(int i, float x, float y, float z) { public void set(int i, float x, float y, float z) {
switch (i) { switch (i) {
case 0: pointa.set(x,y,z); break; case 0:
case 1: pointb.set(x,y,z); break; pointa.set(x, y, z);
case 2: pointc.set(x,y,z); break; break;
case 1:
pointb.set(x, y, z);
break;
case 2:
pointc.set(x, y, z);
break;
} }
} }
public void set1(Vector3f v){ public void set1(Vector3f v) {
pointa.set(v); pointa.set(v);
} }
public void set2(Vector3f v){ public void set2(Vector3f v) {
pointb.set(v); pointb.set(v);
} }
public void set3(Vector3f v){ public void set3(Vector3f v) {
pointc.set(v); pointc.set(v);
} }
public void set(Vector3f v1, Vector3f v2, Vector3f v3){ public void set(Vector3f v1, Vector3f v2, Vector3f v3) {
pointa.set(v1); pointa.set(v1);
pointb.set(v2); pointb.set(v2);
pointc.set(v3); pointc.set(v3);
@ -156,9 +168,11 @@ public class Triangle extends AbstractTriangle implements Savable {
* *
*/ */
public void calculateCenter() { public void calculateCenter() {
if (center == null) if (center == null) {
center = new Vector3f(pointa); center = new Vector3f(pointa);
else center.set(pointa); } else {
center.set(pointa);
}
center.addLocal(pointb).addLocal(pointc).multLocal(FastMath.ONE_THIRD); center.addLocal(pointb).addLocal(pointc).multLocal(FastMath.ONE_THIRD);
} }
@ -167,10 +181,12 @@ public class Triangle extends AbstractTriangle implements Savable {
* *
*/ */
public void calculateNormal() { public void calculateNormal() {
if (normal == null) if (normal == null) {
normal = new Vector3f(pointb); normal = new Vector3f(pointb);
else normal.set(pointb); } else {
normal.subtractLocal(pointa).crossLocal(pointc.x-pointa.x, pointc.y-pointa.y, pointc.z-pointa.z); normal.set(pointb);
}
normal.subtractLocal(pointa).crossLocal(pointc.x - pointa.x, pointc.y - pointa.y, pointc.z - pointa.z);
normal.normalizeLocal(); normal.normalizeLocal();
} }
@ -179,9 +195,9 @@ public class Triangle extends AbstractTriangle implements Savable {
* @return the center point. * @return the center point.
*/ */
public Vector3f getCenter() { public Vector3f getCenter() {
if(center == null) { if (center == null) {
calculateCenter(); calculateCenter();
} }
return center; return center;
} }
@ -200,9 +216,9 @@ public class Triangle extends AbstractTriangle implements Savable {
* @return the normal vector * @return the normal vector
*/ */
public Vector3f getNormal() { public Vector3f getNormal() {
if(normal == null) { if (normal == null) {
calculateNormal(); calculateNormal();
} }
return normal; return normal;
} }
@ -246,13 +262,14 @@ public class Triangle extends AbstractTriangle implements Savable {
this.index = index; this.index = index;
} }
public static Vector3f computeTriangleNormal(Vector3f v1, Vector3f v2, Vector3f v3, Vector3f store){ public static Vector3f computeTriangleNormal(Vector3f v1, Vector3f v2, Vector3f v3, Vector3f store) {
if (store == null) if (store == null) {
store = new Vector3f(v2); store = new Vector3f(v2);
else } else {
store.set(v2); store.set(v2);
}
store.subtractLocal(v1).crossLocal(v3.x-v1.x, v3.y-v1.y, v3.z-v1.z); store.subtractLocal(v1).crossLocal(v3.x - v1.x, v3.y - v1.y, v3.z - v1.z);
return store.normalizeLocal(); return store.normalizeLocal();
} }
@ -263,9 +280,9 @@ public class Triangle extends AbstractTriangle implements Savable {
} }
public void read(JmeImporter e) throws IOException { public void read(JmeImporter e) throws IOException {
pointa = (Vector3f)e.getCapsule(this).readSavable("pointa", Vector3f.ZERO.clone()); pointa = (Vector3f) e.getCapsule(this).readSavable("pointa", Vector3f.ZERO.clone());
pointb = (Vector3f)e.getCapsule(this).readSavable("pointb", Vector3f.ZERO.clone()); pointb = (Vector3f) e.getCapsule(this).readSavable("pointb", Vector3f.ZERO.clone());
pointc = (Vector3f)e.getCapsule(this).readSavable("pointc", Vector3f.ZERO.clone()); pointc = (Vector3f) e.getCapsule(this).readSavable("pointc", Vector3f.ZERO.clone());
} }
@Override @Override

@ -320,7 +320,8 @@ public final class Vector2f implements Savable, Cloneable {
* <code>distanceSquared</code> calculates the distance squared between * <code>distanceSquared</code> calculates the distance squared between
* this vector and vector v. * this vector and vector v.
* *
* @param v the second vector to determine the distance squared. * @param otherX The X coordinate of the v vector
* @param otherY The Y coordinate of the v vector
* @return the distance squared between the two vectors. * @return the distance squared between the two vectors.
*/ */
public float distanceSquared(float otherX, float otherY) { public float distanceSquared(float otherX, float otherY) {

@ -677,7 +677,7 @@ public final class Vector4f implements Savable, Cloneable {
* the y value to subtract. * the y value to subtract.
* @param subtractZ * @param subtractZ
* the z value to subtract. * the z value to subtract.
* @param subtract@ * @param subtractW
* the w value to subtract. * the w value to subtract.
* @return this * @return this
*/ */

@ -189,9 +189,10 @@ public abstract class Filter implements Savable {
public abstract void cleanUpFilter(Renderer r); public abstract void cleanUpFilter(Renderer r);
/** /**
* this method should return the material used for this filter. * Returns the material used for this filter.
* this method is called every frames * this method is called every frame.
* @return *
* @return the material used for this filter.
*/ */
public abstract Material getMaterial(); public abstract Material getMaterial();
@ -232,16 +233,14 @@ public abstract class Filter implements Savable {
} }
/** /**
* Override this method if you want to load extra properties when the filter is loaded else only basic properties of the filter will be loaded * Override this method if you want to load extra properties when the filter
* This method should always begin by super.read(ex); * is loaded else only basic properties of the filter will be loaded
* @param ex * This method should always begin by super.read(im);
* @throws IOException
*/ */
public void read(JmeImporter im) throws IOException { public void read(JmeImporter im) throws IOException {
InputCapsule ic = im.getCapsule(this); InputCapsule ic = im.getCapsule(this);
name = ic.readString("name", ""); name = ic.readString("name", "");
enabled = ic.readBoolean("enabled", true); enabled = ic.readBoolean("enabled", true);
} }
public String getName() { public String getName() {
@ -269,8 +268,9 @@ public abstract class Filter implements Savable {
} }
/** /**
* Override this method and retrun true if your Filter need the depth texture * Override this method and return true if your Filter need the depth texture
* @return *
* @return true if your Filter need the depth texture
*/ */
public boolean isRequiresDepthTexture() { public boolean isRequiresDepthTexture() {
return false; return false;
@ -278,7 +278,8 @@ public abstract class Filter implements Savable {
/** /**
* Override this method and return false if your Filter does not need the scene texture * Override this method and return false if your Filter does not need the scene texture
* @return *
* @return false if your Filter does not need the scene texture
*/ */
public boolean isRequiresSceneTexture() { public boolean isRequiresSceneTexture() {
return true; return true;

@ -599,7 +599,6 @@ public class Camera implements Savable, Cloneable {
* <code>setLocation</code> sets the position of the camera. * <code>setLocation</code> sets the position of the camera.
* *
* @param location the position of the camera. * @param location the position of the camera.
* @see Camera#setLocation(com.jme.math.Vector3f)
*/ */
public void setLocation(Vector3f location) { public void setLocation(Vector3f location) {
this.location.set(location); this.location.set(location);
@ -660,7 +659,8 @@ public class Camera implements Savable, Cloneable {
* @param left the left axis of the camera. * @param left the left axis of the camera.
* @param up the up axis of the camera. * @param up the up axis of the camera.
* @param direction the direction the camera is facing. * @param direction the direction the camera is facing.
* @see Camera#setAxes(com.jme.math.Vector3f,com.jme.math.Vector3f,com.jme.math.Vector3f) *
* @see Camera#setAxes(com.jme3.math.Quaternion)
*/ */
public void setAxes(Vector3f left, Vector3f up, Vector3f direction) { public void setAxes(Vector3f left, Vector3f up, Vector3f direction) {
this.rotation.fromAxes(left, up, direction); this.rotation.fromAxes(left, up, direction);

@ -154,9 +154,8 @@ public abstract class GLObject implements Cloneable {
/** /**
* This should create a deep clone. For a shallow clone, use * This should create a deep clone. For a shallow clone, use
* createDestructableClone(). * createDestructableClone().
*
* @return
*/ */
@Override
protected GLObject clone(){ protected GLObject clone(){
try{ try{
GLObject obj = (GLObject) super.clone(); GLObject obj = (GLObject) super.clone();

@ -181,9 +181,6 @@ public class RenderManager {
/** /**
* Creates a new viewport, to display the given camera's content. * Creates a new viewport, to display the given camera's content.
* The view will be processed before the primary viewport. * The view will be processed before the primary viewport.
* @param viewName
* @param cam
* @return
*/ */
public ViewPort createPreView(String viewName, Camera cam) { public ViewPort createPreView(String viewName, Camera cam) {
ViewPort vp = new ViewPort(viewName, cam); ViewPort vp = new ViewPort(viewName, cam);
@ -491,9 +488,6 @@ public class RenderManager {
/** /**
* Render scene graph * Render scene graph
* @param s
* @param r
* @param cam
*/ */
public void renderScene(Spatial scene, ViewPort vp) { public void renderScene(Spatial scene, ViewPort vp) {
if (scene.getParent() == null) { if (scene.getParent() == null) {

@ -99,7 +99,7 @@ public interface Renderer {
public void onFrame(); public void onFrame();
/** /**
* @param transform The world transform to use. This changes * @param worldMatrix The world transform to use. This changes
* the world matrix given in the shader. * the world matrix given in the shader.
*/ */
public void setWorldMatrix(Matrix4f worldMatrix); public void setWorldMatrix(Matrix4f worldMatrix);
@ -171,7 +171,6 @@ public interface Renderer {
/** /**
* Deletes a texture from the GPU. * Deletes a texture from the GPU.
* @param tex
*/ */
public void deleteImage(Image image); public void deleteImage(Image image);

@ -70,10 +70,10 @@ public class GeometryList {
} }
/** /**
* Adds a spatial to the list. List size is doubled if there is no room. * Adds a geometry to the list. List size is doubled if there is no room.
* *
* @param s * @param g
* The spatial to add. * The geometry to add.
*/ */
public void add(Geometry g) { public void add(Geometry g) {
if (size == geometries.length) { if (size == geometries.length) {

@ -177,7 +177,7 @@ public class Geometry extends Spatial {
* this geometry. The location of the geometry is based on the location of * this geometry. The location of the geometry is based on the location of
* all this node's parents. * all this node's parents.
* *
* @see com.jme.scene.Spatial#updateWorldBound() * @see Spatial#updateWorldBound()
*/ */
@Override @Override
protected void updateWorldBound() { protected void updateWorldBound() {
@ -282,7 +282,6 @@ public class Geometry extends Spatial {
* Exception: if the mesh is marked as being a software * Exception: if the mesh is marked as being a software
* animated mesh, (bind pose is set) then the positions * animated mesh, (bind pose is set) then the positions
* and normals are deep copied. * and normals are deep copied.
* @return
*/ */
@Override @Override
public Geometry clone(boolean cloneMaterial){ public Geometry clone(boolean cloneMaterial){
@ -308,8 +307,8 @@ public class Geometry extends Spatial {
* Exception: if the mesh is marked as being a software * Exception: if the mesh is marked as being a software
* animated mesh, (bind pose is set) then the positions * animated mesh, (bind pose is set) then the positions
* and normals are deep copied. * and normals are deep copied.
* @return
*/ */
@Override
public Geometry clone(){ public Geometry clone(){
return clone(true); return clone(true);
} }
@ -318,7 +317,6 @@ public class Geometry extends Spatial {
* Creates a deep clone of the geometry, * Creates a deep clone of the geometry,
* this creates an identical copy of the mesh * this creates an identical copy of the mesh
* with the vertexbuffer data duplicated. * with the vertexbuffer data duplicated.
* @return
*/ */
@Override @Override
public Spatial deepClone(){ public Spatial deepClone(){

@ -526,7 +526,7 @@ public class Node extends Spatial implements Savable {
* @return Non-null, but possibly 0-element, list of matching Spatials (also Instances extending Spatials). * @return Non-null, but possibly 0-element, list of matching Spatials (also Instances extending Spatials).
* *
* @see java.util.regex.Pattern * @see java.util.regex.Pattern
* @see Spatial#matches(Class<? extends Spatial>, String) * @see Spatial#matches(java.lang.Class, java.lang.String)
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public <T extends Spatial>List<T> descendantMatches( public <T extends Spatial>List<T> descendantMatches(
@ -546,7 +546,7 @@ public class Node extends Spatial implements Savable {
/** /**
* Convenience wrapper. * Convenience wrapper.
* *
* @see #descendantMatches(Class<? extends Spatial>, String) * @see #descendantMatches(java.lang.Class, java.lang.String)
*/ */
public <T extends Spatial>List<T> descendantMatches( public <T extends Spatial>List<T> descendantMatches(
Class<T> spatialSubclass) { Class<T> spatialSubclass) {
@ -556,7 +556,7 @@ public class Node extends Spatial implements Savable {
/** /**
* Convenience wrapper. * Convenience wrapper.
* *
* @see #descendantMatches(Class<? extends Spatial>, String) * @see #descendantMatches(java.lang.Class, java.lang.String)
*/ */
public <T extends Spatial>List<T> descendantMatches(String nameRegex) { public <T extends Spatial>List<T> descendantMatches(String nameRegex) {
return descendantMatches(null, nameRegex); return descendantMatches(null, nameRegex);

@ -784,9 +784,6 @@ public abstract class Spatial implements Savable, Cloneable, Collidable {
/** /**
* <code>setLocalScale</code> sets the local scale of this node. * <code>setLocalScale</code> sets the local scale of this node.
*
* @param localScale
* the new local scale
*/ */
public void setLocalScale(float x, float y, float z) { public void setLocalScale(float x, float y, float z) {
localTransform.setScale(x, y, z); localTransform.setScale(x, y, z);

@ -664,11 +664,6 @@ public class VertexBuffer extends GLObject implements Savable, Cloneable {
* of the parameters. The buffer will be of the type specified by * of the parameters. The buffer will be of the type specified by
* {@link Format format} and would be able to contain the given number * {@link Format format} and would be able to contain the given number
* of elements with the given number of components in each element. * of elements with the given number of components in each element.
*
* @param format
* @param components
* @param numElements
* @return
*/ */
public static Buffer createBuffer(Format format, int components, int numElements){ public static Buffer createBuffer(Format format, int components, int numElements){
if (components < 1 || components > 4) if (components < 1 || components > 4)

@ -48,7 +48,7 @@ public interface Control extends Savable {
* Creates a clone of the Control, the given Spatial is the cloned * Creates a clone of the Control, the given Spatial is the cloned
* version of the spatial to which this control is attached to. * version of the spatial to which this control is attached to.
* @param spatial * @param spatial
* @return * @return A clone of this control for the spatial
*/ */
public Control cloneForSpatial(Spatial spatial); public Control cloneForSpatial(Spatial spatial);

@ -76,14 +76,14 @@ public class LightControl extends AbstractControl {
} }
/** /**
* @param camera The Camera to be synced. * @param light The light to be synced.
*/ */
public LightControl(Light light) { public LightControl(Light light) {
this.light = light; this.light = light;
} }
/** /**
* @param camera The Camera to be synced. * @param light The light to be synced.
*/ */
public LightControl(Light light, ControlDirection controlDir) { public LightControl(Light light, ControlDirection controlDir) {
this.light = light; this.light = light;

@ -380,7 +380,6 @@ public final class Shader extends GLObject implements Savable {
/** /**
* Returns true if this program and all it's shaders have been compiled, * Returns true if this program and all it's shaders have been compiled,
* linked and validated successfuly. * linked and validated successfuly.
* @return
*/ */
public boolean isUsable(){ public boolean isUsable(){
return usable; return usable;
@ -417,7 +416,6 @@ public final class Shader extends GLObject implements Savable {
/** /**
* Called by the object manager to reset all object IDs. This causes * Called by the object manager to reset all object IDs. This causes
* the shader to be reuploaded to the GPU incase the display was restarted. * the shader to be reuploaded to the GPU incase the display was restarted.
* @param r
*/ */
@Override @Override
public void resetObject() { public void resetObject() {

@ -37,6 +37,7 @@ import com.jme3.input.KeyInput;
import com.jme3.input.MouseInput; import com.jme3.input.MouseInput;
import com.jme3.input.TouchInput; import com.jme3.input.TouchInput;
import com.jme3.renderer.Renderer; import com.jme3.renderer.Renderer;
import com.jme3.system.JmeCanvasContext;
/** /**
* Represents a rendering context within the engine. * Represents a rendering context within the engine.

Loading…
Cancel
Save