diff --git a/engine/src/blender/com/jme3/asset/BlenderKey.java b/engine/src/blender/com/jme3/asset/BlenderKey.java
index e39b842d0..dedbbd4d6 100644
--- a/engine/src/blender/com/jme3/asset/BlenderKey.java
+++ b/engine/src/blender/com/jme3/asset/BlenderKey.java
@@ -63,709 +63,713 @@ import com.jme3.texture.Texture;
*/
public class BlenderKey extends ModelKey {
- protected static final int DEFAULT_FPS = 25;
- /**
- * FramesPerSecond parameter describe how many frames there are in each second. It allows to calculate the time
- * between the frames.
- */
- protected int fps = DEFAULT_FPS;
- /**
- * This variable is a bitwise flag of FeatureToLoad interface values; By default everything is being loaded.
- */
- protected int featuresToLoad = FeaturesToLoad.ALL;
- /** This variable determines if assets that are not linked to the objects should be loaded. */
- protected boolean loadUnlinkedAssets;
- /** The root path for all the assets. */
- protected String assetRootPath;
- /** This variable indicate if Y axis is UP axis. If not then Z is up. By default set to true. */
- protected boolean fixUpAxis = true;
- /** Generated textures resolution (PPU - Pixels Per Unit). */
- protected int generatedTexturePPU = 128;
- /**
- * The name of world settings that the importer will use. If not set or specified name does not occur in the file
- * then the first world settings in the file will be used.
- */
- protected String usedWorld;
- /**
- * User's default material that is set fo objects that have no material definition in blender. The default value is
- * null. If the value is null the importer will use its own default material (gray color - like in blender).
- */
- protected Material defaultMaterial;
- /** Face cull mode. By default it is disabled. */
- protected FaceCullMode faceCullMode = FaceCullMode.Back;
- /**
- * Variable describes which layers will be loaded. N-th bit set means N-th layer will be loaded.
- * If set to -1 then the current layer will be loaded.
- */
- protected int layersToLoad = -1;
- /** A variable that toggles the object custom properties loading. */
- protected boolean loadObjectProperties = true;
- /** Maximum texture size. Might be dependant on the graphic card.*/
- protected int maxTextureSize = -1;
- /** Allows to toggle generated textures loading. Disabled by default because it very often takes too much memory and needs to be used wisely. */
- protected boolean loadGeneratedTextures;
- /** Tells if the mipmaps will be generated by jme or not. By default generation is dependant on the blender settings. */
- protected MipmapGenerationMethod mipmapGenerationMethod = MipmapGenerationMethod.GENERATE_WHEN_NEEDED;
-
- /**
- * Constructor used by serialization mechanisms.
- */
- public BlenderKey() {}
-
- /**
- * Constructor. Creates a key for the given file name.
- * @param name
- * the name (path) of a file
- */
- public BlenderKey(String name) {
- super(name);
- }
-
- /**
- * This method returns frames per second amount. The default value is BlenderKey.DEFAULT_FPS = 25.
- * @return the frames per second amount
- */
- public int getFps() {
- return fps;
- }
-
- /**
- * This method sets frames per second amount.
- * @param fps
- * the frames per second amount
- */
- public void setFps(int fps) {
- this.fps = fps;
- }
-
- /**
- * This method returns the face cull mode.
- * @return the face cull mode
- */
- public FaceCullMode getFaceCullMode() {
- return faceCullMode;
- }
-
- /**
- * This method sets the face cull mode.
- * @param faceCullMode
- * the face cull mode
- */
- public void setFaceCullMode(FaceCullMode faceCullMode) {
- this.faceCullMode = faceCullMode;
- }
-
- /**
- * This method sets layers to be loaded.
- * @param layersToLoad
- * layers to be loaded
- */
- public void setLayersToLoad(int layersToLoad) {
- this.layersToLoad = layersToLoad;
- }
-
- /**
- * This method returns layers to be loaded.
- * @return layers to be loaded
- */
- public int getLayersToLoad() {
- return layersToLoad;
- }
-
- /**
- * This method sets the properies loading policy.
- * By default the value is true.
- * @param loadObjectProperties true to load properties and false to suspend their loading
- */
- public void setLoadObjectProperties(boolean loadObjectProperties) {
- this.loadObjectProperties = loadObjectProperties;
- }
-
- /**
- * @return the current properties loading properties
- */
- public boolean isLoadObjectProperties() {
- return loadObjectProperties;
- }
-
- /**
- * @return maximum texture size (width/height)
- */
- public int getMaxTextureSize() {
- if(maxTextureSize <= 0) {
- try {
- maxTextureSize = GL11.glGetInteger(GL11.GL_MAX_TEXTURE_SIZE);
- } catch(Exception e) {
- //this is in case this method was called before openGL initialization
- return 8192;
- }
- }
- return maxTextureSize;
- }
-
- /**
- * This method sets the maximum texture size.
- * @param maxTextureSize the maximum texture size
- */
- public void setMaxTextureSize(int maxTextureSize) {
- this.maxTextureSize = maxTextureSize;
- }
-
- /**
- * This method sets the flag that toggles the generated textures loading.
- * @param loadGeneratedTextures true if generated textures should be loaded and false otherwise
- */
- public void setLoadGeneratedTextures(boolean loadGeneratedTextures) {
- this.loadGeneratedTextures = loadGeneratedTextures;
- }
-
- /**
- * @return tells if the generated textures should be loaded (false is the default value)
- */
- public boolean isLoadGeneratedTextures() {
- return loadGeneratedTextures;
- }
-
- /**
- * This method sets the asset root path.
- * @param assetRootPath
- * the assets root path
- */
- public void setAssetRootPath(String assetRootPath) {
- this.assetRootPath = assetRootPath;
- }
-
- /**
- * This method returns the asset root path.
- * @return the asset root path
- */
- public String getAssetRootPath() {
- return assetRootPath;
- }
-
- /**
- * This method adds features to be loaded.
- * @param featuresToLoad
- * bitwise flag of FeaturesToLoad interface values
- */
- public void includeInLoading(int featuresToLoad) {
- this.featuresToLoad |= featuresToLoad;
- }
-
- /**
- * This method removes features from being loaded.
- * @param featuresNotToLoad
- * bitwise flag of FeaturesToLoad interface values
- */
- public void excludeFromLoading(int featuresNotToLoad) {
- this.featuresToLoad &= ~featuresNotToLoad;
- }
-
- /**
- * This method returns bitwise value of FeaturesToLoad interface value. It describes features that will be loaded by
- * the blender file loader.
- * @return features that will be loaded by the blender file loader
- */
- public int getFeaturesToLoad() {
- return featuresToLoad;
- }
-
- /**
- * This method determines if unlinked assets should be loaded.
- * If not then only objects on selected layers will be loaded and their assets if required.
- * If yes then all assets will be loaded even if they are on inactive layers or are not linked
- * to anything.
- * @return true if unlinked assets should be loaded and false otherwise
- */
- public boolean isLoadUnlinkedAssets() {
- return loadUnlinkedAssets;
- }
-
- /**
- * This method sets if unlinked assets should be loaded.
- * If not then only objects on selected layers will be loaded and their assets if required.
- * If yes then all assets will be loaded even if they are on inactive layers or are not linked
- * to anything.
- * @param loadUnlinkedAssets
- * true if unlinked assets should be loaded and false otherwise
- */
- public void setLoadUnlinkedAssets(boolean loadUnlinkedAssets) {
- this.loadUnlinkedAssets = loadUnlinkedAssets;
- }
-
- /**
- * This method creates an object where loading results will be stores. Only those features will be allowed to store
- * that were specified by features-to-load flag.
- * @return an object to store loading results
- */
- public LoadingResults prepareLoadingResults() {
- return new LoadingResults(featuresToLoad);
- }
-
- /**
- * This method sets the fix up axis state. If set to true then Y is up axis. Otherwise the up i Z axis. By default Y
- * is up axis.
- * @param fixUpAxis
- * the up axis state variable
- */
- public void setFixUpAxis(boolean fixUpAxis) {
- this.fixUpAxis = fixUpAxis;
- }
-
- /**
- * This method returns the fix up axis state. If set to true then Y is up axis. Otherwise the up i Z axis. By
- * default Y is up axis.
- * @return the up axis state variable
- */
- public boolean isFixUpAxis() {
- return fixUpAxis;
- }
-
- /**
- * This method sets the generated textures resolution.
- * @param generatedTexturePPU the generated textures resolution
- */
- public void setGeneratedTexturePPU(int generatedTexturePPU) {
- this.generatedTexturePPU = generatedTexturePPU;
- }
-
- /**
- * @return the generated textures resolution
- */
- public int getGeneratedTexturePPU() {
- return generatedTexturePPU;
- }
-
- /**
- * @return mipmaps generation method
- */
- public MipmapGenerationMethod getMipmapGenerationMethod() {
- return mipmapGenerationMethod;
- }
-
- /**
- * @param mipmapGenerationMethod
- * mipmaps generation method
- */
- public void setMipmapGenerationMethod(MipmapGenerationMethod mipmapGenerationMethod) {
- this.mipmapGenerationMethod = mipmapGenerationMethod;
- }
-
- /**
- * This mehtod sets the name of the WORLD data block taht should be used during file loading. By default the name is
- * not set. If no name is set or the given name does not occur in the file - the first WORLD data block will be used
- * during loading (assumin any exists in the file).
- * @param usedWorld
- * the name of the WORLD block used during loading
- */
- public void setUsedWorld(String usedWorld) {
- this.usedWorld = usedWorld;
- }
-
- /**
- * This mehtod returns the name of the WORLD data block taht should be used during file loading.
- * @return the name of the WORLD block used during loading
- */
- public String getUsedWorld() {
- return usedWorld;
- }
-
- /**
- * This method sets the default material for objects.
- * @param defaultMaterial
- * the default material
- */
- public void setDefaultMaterial(Material defaultMaterial) {
- this.defaultMaterial = defaultMaterial;
- }
-
- /**
- * This method returns the default material.
- * @return the default material
- */
- public Material getDefaultMaterial() {
- return defaultMaterial;
- }
-
- @Override
- public void write(JmeExporter e) throws IOException {
- super.write(e);
- OutputCapsule oc = e.getCapsule(this);
- oc.write(fps, "fps", DEFAULT_FPS);
- oc.write(featuresToLoad, "features-to-load", FeaturesToLoad.ALL);
- oc.write(loadUnlinkedAssets, "load-unlinked-assets", false);
- oc.write(assetRootPath, "asset-root-path", null);
- oc.write(fixUpAxis, "fix-up-axis", true);
- oc.write(generatedTexturePPU, "generated-texture-ppu", 128);
- oc.write(usedWorld, "used-world", null);
- oc.write(defaultMaterial, "default-material", null);
- oc.write(faceCullMode, "face-cull-mode", FaceCullMode.Off);
- oc.write(layersToLoad, "layers-to-load", -1);
- oc.write(mipmapGenerationMethod , "mipmap-generation-method", MipmapGenerationMethod.GENERATE_WHEN_NEEDED);
- }
-
- @Override
- public void read(JmeImporter e) throws IOException {
- super.read(e);
- InputCapsule ic = e.getCapsule(this);
- fps = ic.readInt("fps", DEFAULT_FPS);
- featuresToLoad = ic.readInt("features-to-load", FeaturesToLoad.ALL);
- loadUnlinkedAssets = ic.readBoolean("load-unlinked-assets", false);
- assetRootPath = ic.readString("asset-root-path", null);
- fixUpAxis = ic.readBoolean("fix-up-axis", true);
- generatedTexturePPU = ic.readInt("generated-texture-ppu", 128);
- usedWorld = ic.readString("used-world", null);
- defaultMaterial = (Material) ic.readSavable("default-material", null);
- faceCullMode = ic.readEnum("face-cull-mode", FaceCullMode.class, FaceCullMode.Off);
- layersToLoad = ic.readInt("layers-to=load", -1);
- mipmapGenerationMethod = ic.readEnum("mipmap-generation-method", MipmapGenerationMethod.class, MipmapGenerationMethod.GENERATE_WHEN_NEEDED);
- }
-
- @Override
- public int hashCode() {
- final int prime = 31;
- int result = super.hashCode();
- result = prime * result + (assetRootPath == null ? 0 : assetRootPath.hashCode());
- result = prime * result + (defaultMaterial == null ? 0 : defaultMaterial.hashCode());
- result = prime * result + (faceCullMode == null ? 0 : faceCullMode.hashCode());
- result = prime * result + featuresToLoad;
- result = prime * result + (fixUpAxis ? 1231 : 1237);
- result = prime * result + fps;
- result = prime * result + generatedTexturePPU;
- result = prime * result + layersToLoad;
- result = prime * result + (loadUnlinkedAssets ? 1231 : 1237);
- result = prime * result + (usedWorld == null ? 0 : usedWorld.hashCode());
- return result;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj) {
- return true;
- }
- if (!super.equals(obj)) {
- return false;
- }
- if (this.getClass() != obj.getClass()) {
- return false;
- }
- BlenderKey other = (BlenderKey) obj;
- if (assetRootPath == null) {
- if (other.assetRootPath != null) {
- return false;
- }
- } else if (!assetRootPath.equals(other.assetRootPath)) {
- return false;
- }
- if (defaultMaterial == null) {
- if (other.defaultMaterial != null) {
- return false;
- }
- } else if (!defaultMaterial.equals(other.defaultMaterial)) {
- return false;
- }
- if (faceCullMode != other.faceCullMode) {
- return false;
- }
- if (featuresToLoad != other.featuresToLoad) {
- return false;
- }
- if (fixUpAxis != other.fixUpAxis) {
- return false;
- }
- if (fps != other.fps) {
- return false;
- }
- if (generatedTexturePPU != other.generatedTexturePPU) {
- return false;
- }
- if (layersToLoad != other.layersToLoad) {
- return false;
- }
- if (loadUnlinkedAssets != other.loadUnlinkedAssets) {
- return false;
- }
- if (usedWorld == null) {
- if (other.usedWorld != null) {
- return false;
- }
- } else if (!usedWorld.equals(other.usedWorld)) {
- return false;
- }
- return true;
- }
-
- /**
- * This enum tells the importer if the mipmaps for textures will be generated by jme.
- *
NEVER_GENERATE and ALWAYS_GENERATE are quite understandable
- * GENERATE_WHEN_NEEDED is an option that checks if the texture had 'Generate mipmaps' option set
- * in blender, mipmaps are generated only when the option is set
- * @author Marcin Roguski (Kaelthas)
- */
- public static enum MipmapGenerationMethod {
- NEVER_GENERATE,
- ALWAYS_GENERATE,
- GENERATE_WHEN_NEEDED;
- }
-
- /**
- * This interface describes the features of the scene that are to be loaded.
- * @author Marcin Roguski (Kaelthas)
- */
- public static interface FeaturesToLoad {
-
- int SCENES = 0x0000FFFF;
- int OBJECTS = 0x0000000B;
- int ANIMATIONS = 0x00000004;
- int MATERIALS = 0x00000003;
- int TEXTURES = 0x00000001;
- int CAMERAS = 0x00000020;
- int LIGHTS = 0x00000010;
- int ALL = 0xFFFFFFFF;
- }
-
- /**
- * This class holds the loading results according to the given loading flag.
- * @author Marcin Roguski (Kaelthas)
- */
- public static class LoadingResults extends Spatial {
-
- /** Bitwise mask of features that are to be loaded. */
- private final int featuresToLoad;
- /** The scenes from the file. */
- private List scenes;
- /** Objects from all scenes. */
- private List objects;
- /** Materials from all objects. */
- private List materials;
- /** Textures from all objects. */
- private List textures;
- /** Animations of all objects. */
- private List animations;
- /** All cameras from the file. */
- private Listcameras;
- /** All lights from the file. */
- private List lights;
-
- /**
- * Private constructor prevents users to create an instance of this class from outside the
- * @param featuresToLoad
- * bitwise mask of features that are to be loaded
- * @see FeaturesToLoad FeaturesToLoad
- */
- private LoadingResults(int featuresToLoad) {
- this.featuresToLoad = featuresToLoad;
- if ((featuresToLoad & FeaturesToLoad.SCENES) != 0) {
- scenes = new ArrayList();
- }
- if ((featuresToLoad & FeaturesToLoad.OBJECTS) != 0) {
- objects = new ArrayList();
- if ((featuresToLoad & FeaturesToLoad.MATERIALS) != 0) {
- materials = new ArrayList();
- if ((featuresToLoad & FeaturesToLoad.TEXTURES) != 0) {
- textures = new ArrayList();
- }
- }
- if ((featuresToLoad & FeaturesToLoad.ANIMATIONS) != 0) {
- animations = new ArrayList();
- }
- }
- if ((featuresToLoad & FeaturesToLoad.CAMERAS) != 0) {
- cameras = new ArrayList();
- }
- if ((featuresToLoad & FeaturesToLoad.LIGHTS) != 0) {
- lights = new ArrayList();
- }
- }
-
- /**
- * This method returns a bitwise flag describing what features of the blend file will be included in the result.
- * @return bitwise mask of features that are to be loaded
- * @see FeaturesToLoad FeaturesToLoad
- */
- public int getLoadedFeatures() {
- return featuresToLoad;
- }
-
- /**
- * This method adds a scene to the result set.
- * @param scene
- * scene to be added to the result set
- */
- public void addScene(Node scene) {
- if (scenes != null) {
- scenes.add(scene);
- }
- }
-
- /**
- * This method adds an object to the result set.
- * @param object
- * object to be added to the result set
- */
- public void addObject(Node object) {
- if (objects != null) {
- objects.add(object);
- }
- }
-
- /**
- * This method adds a material to the result set.
- * @param material
- * material to be added to the result set
- */
- public void addMaterial(Material material) {
- if (materials != null) {
- materials.add(material);
- }
- }
-
- /**
- * This method adds a texture to the result set.
- * @param texture
- * texture to be added to the result set
- */
- public void addTexture(Texture texture) {
- if (textures != null) {
- textures.add(texture);
- }
- }
-
- /**
- * This method adds a camera to the result set.
- * @param camera
- * camera to be added to the result set
- */
- public void addCamera(CameraNode camera) {
- if (cameras != null) {
- cameras.add(camera);
- }
- }
-
- /**
- * This method adds a light to the result set.
- * @param light
- * light to be added to the result set
- */
- public void addLight(LightNode light) {
- if (lights != null) {
- lights.add(light);
- }
- }
-
- /**
- * This method returns all loaded scenes.
- * @return all loaded scenes
- */
- public List getScenes() {
- return scenes;
- }
-
- /**
- * This method returns all loaded objects.
- * @return all loaded objects
- */
- public List getObjects() {
- return objects;
- }
-
- /**
- * This method returns all loaded materials.
- * @return all loaded materials
- */
- public List getMaterials() {
- return materials;
- }
-
- /**
- * This method returns all loaded textures.
- * @return all loaded textures
- */
- public List getTextures() {
- return textures;
- }
-
- /**
- * This method returns all loaded animations.
- * @return all loaded animations
- */
- public List getAnimations() {
- return animations;
- }
-
- /**
- * This method returns all loaded cameras.
- * @return all loaded cameras
- */
- public List getCameras() {
- return cameras;
- }
-
- /**
- * This method returns all loaded lights.
- * @return all loaded lights
- */
- public List getLights() {
- return lights;
- }
-
- public int collideWith(Collidable other, CollisionResults results) throws UnsupportedCollisionException {
- return 0;
- }
-
- @Override
- public void updateModelBound() {}
-
- @Override
- public void setModelBound(BoundingVolume modelBound) {}
-
- @Override
- public int getVertexCount() {
- return 0;
- }
-
- @Override
- public int getTriangleCount() {
- return 0;
- }
-
- @Override
- public Spatial deepClone() {
- return null;
- }
-
- @Override
- public void depthFirstTraversal(SceneGraphVisitor visitor) {}
-
- @Override
- protected void breadthFirstTraversal(SceneGraphVisitor visitor, Queue queue) {}
- }
-
- /**
- * The WORLD file block contains various data that could be added to the scene. The contained data includes: ambient
- * light.
- * @author Marcin Roguski (Kaelthas)
- */
- public static class WorldData {
-
- /** The ambient light. */
- private AmbientLight ambientLight;
-
- /**
- * This method returns the world's ambient light.
- * @return the world's ambient light
- */
- public AmbientLight getAmbientLight() {
- return ambientLight;
- }
-
- /**
- * This method sets the world's ambient light.
- * @param ambientLight
- * the world's ambient light
- */
- public void setAmbientLight(AmbientLight ambientLight) {
- this.ambientLight = ambientLight;
- }
- }
+ protected static final int DEFAULT_FPS = 25;
+ /**
+ * FramesPerSecond parameter describe how many frames there are in each second. It allows to calculate the time
+ * between the frames.
+ */
+ protected int fps = DEFAULT_FPS;
+ /**
+ * This variable is a bitwise flag of FeatureToLoad interface values; By default everything is being loaded.
+ */
+ protected int featuresToLoad = FeaturesToLoad.ALL;
+ /** This variable determines if assets that are not linked to the objects should be loaded. */
+ protected boolean loadUnlinkedAssets;
+ /** The root path for all the assets. */
+ protected String assetRootPath;
+ /** This variable indicate if Y axis is UP axis. If not then Z is up. By default set to true. */
+ protected boolean fixUpAxis = true;
+ /** Generated textures resolution (PPU - Pixels Per Unit). */
+ protected int generatedTexturePPU = 128;
+ /**
+ * The name of world settings that the importer will use. If not set or specified name does not occur in the file
+ * then the first world settings in the file will be used.
+ */
+ protected String usedWorld;
+ /**
+ * User's default material that is set fo objects that have no material definition in blender. The default value is
+ * null. If the value is null the importer will use its own default material (gray color - like in blender).
+ */
+ protected Material defaultMaterial;
+ /** Face cull mode. By default it is disabled. */
+ protected FaceCullMode faceCullMode = FaceCullMode.Back;
+ /**
+ * Variable describes which layers will be loaded. N-th bit set means N-th layer will be loaded.
+ * If set to -1 then the current layer will be loaded.
+ */
+ protected int layersToLoad = -1;
+ /** A variable that toggles the object custom properties loading. */
+ protected boolean loadObjectProperties = true;
+ /** Maximum texture size. Might be dependant on the graphic card. */
+ protected int maxTextureSize = -1;
+ /** Allows to toggle generated textures loading. Disabled by default because it very often takes too much memory and needs to be used wisely. */
+ protected boolean loadGeneratedTextures;
+ /** Tells if the mipmaps will be generated by jme or not. By default generation is dependant on the blender settings. */
+ protected MipmapGenerationMethod mipmapGenerationMethod = MipmapGenerationMethod.GENERATE_WHEN_NEEDED;
+
+ /**
+ * Constructor used by serialization mechanisms.
+ */
+ public BlenderKey() {
+ }
+
+ /**
+ * Constructor. Creates a key for the given file name.
+ * @param name
+ * the name (path) of a file
+ */
+ public BlenderKey(String name) {
+ super(name);
+ }
+
+ /**
+ * This method returns frames per second amount. The default value is BlenderKey.DEFAULT_FPS = 25.
+ * @return the frames per second amount
+ */
+ public int getFps() {
+ return fps;
+ }
+
+ /**
+ * This method sets frames per second amount.
+ * @param fps
+ * the frames per second amount
+ */
+ public void setFps(int fps) {
+ this.fps = fps;
+ }
+
+ /**
+ * This method returns the face cull mode.
+ * @return the face cull mode
+ */
+ public FaceCullMode getFaceCullMode() {
+ return faceCullMode;
+ }
+
+ /**
+ * This method sets the face cull mode.
+ * @param faceCullMode
+ * the face cull mode
+ */
+ public void setFaceCullMode(FaceCullMode faceCullMode) {
+ this.faceCullMode = faceCullMode;
+ }
+
+ /**
+ * This method sets layers to be loaded.
+ * @param layersToLoad
+ * layers to be loaded
+ */
+ public void setLayersToLoad(int layersToLoad) {
+ this.layersToLoad = layersToLoad;
+ }
+
+ /**
+ * This method returns layers to be loaded.
+ * @return layers to be loaded
+ */
+ public int getLayersToLoad() {
+ return layersToLoad;
+ }
+
+ /**
+ * This method sets the properies loading policy.
+ * By default the value is true.
+ * @param loadObjectProperties
+ * true to load properties and false to suspend their loading
+ */
+ public void setLoadObjectProperties(boolean loadObjectProperties) {
+ this.loadObjectProperties = loadObjectProperties;
+ }
+
+ /**
+ * @return the current properties loading properties
+ */
+ public boolean isLoadObjectProperties() {
+ return loadObjectProperties;
+ }
+
+ /**
+ * @return maximum texture size (width/height)
+ */
+ public int getMaxTextureSize() {
+ if (maxTextureSize <= 0) {
+ try {
+ maxTextureSize = GL11.glGetInteger(GL11.GL_MAX_TEXTURE_SIZE);
+ } catch (Exception e) {
+ // this is in case this method was called before openGL initialization
+ return 8192;
+ }
+ }
+ return maxTextureSize;
+ }
+
+ /**
+ * This method sets the maximum texture size.
+ * @param maxTextureSize
+ * the maximum texture size
+ */
+ public void setMaxTextureSize(int maxTextureSize) {
+ this.maxTextureSize = maxTextureSize;
+ }
+
+ /**
+ * This method sets the flag that toggles the generated textures loading.
+ * @param loadGeneratedTextures
+ * true if generated textures should be loaded and false otherwise
+ */
+ public void setLoadGeneratedTextures(boolean loadGeneratedTextures) {
+ this.loadGeneratedTextures = loadGeneratedTextures;
+ }
+
+ /**
+ * @return tells if the generated textures should be loaded (false is the default value)
+ */
+ public boolean isLoadGeneratedTextures() {
+ return loadGeneratedTextures;
+ }
+
+ /**
+ * This method sets the asset root path.
+ * @param assetRootPath
+ * the assets root path
+ */
+ public void setAssetRootPath(String assetRootPath) {
+ this.assetRootPath = assetRootPath;
+ }
+
+ /**
+ * This method returns the asset root path.
+ * @return the asset root path
+ */
+ public String getAssetRootPath() {
+ return assetRootPath;
+ }
+
+ /**
+ * This method adds features to be loaded.
+ * @param featuresToLoad
+ * bitwise flag of FeaturesToLoad interface values
+ */
+ public void includeInLoading(int featuresToLoad) {
+ this.featuresToLoad |= featuresToLoad;
+ }
+
+ /**
+ * This method removes features from being loaded.
+ * @param featuresNotToLoad
+ * bitwise flag of FeaturesToLoad interface values
+ */
+ public void excludeFromLoading(int featuresNotToLoad) {
+ this.featuresToLoad &= ~featuresNotToLoad;
+ }
+
+ /**
+ * This method returns bitwise value of FeaturesToLoad interface value. It describes features that will be loaded by
+ * the blender file loader.
+ * @return features that will be loaded by the blender file loader
+ */
+ public int getFeaturesToLoad() {
+ return featuresToLoad;
+ }
+
+ /**
+ * This method determines if unlinked assets should be loaded.
+ * If not then only objects on selected layers will be loaded and their assets if required.
+ * If yes then all assets will be loaded even if they are on inactive layers or are not linked
+ * to anything.
+ * @return true if unlinked assets should be loaded and false otherwise
+ */
+ public boolean isLoadUnlinkedAssets() {
+ return loadUnlinkedAssets;
+ }
+
+ /**
+ * This method sets if unlinked assets should be loaded.
+ * If not then only objects on selected layers will be loaded and their assets if required.
+ * If yes then all assets will be loaded even if they are on inactive layers or are not linked
+ * to anything.
+ * @param loadUnlinkedAssets
+ * true if unlinked assets should be loaded and false otherwise
+ */
+ public void setLoadUnlinkedAssets(boolean loadUnlinkedAssets) {
+ this.loadUnlinkedAssets = loadUnlinkedAssets;
+ }
+
+ /**
+ * This method creates an object where loading results will be stores. Only those features will be allowed to store
+ * that were specified by features-to-load flag.
+ * @return an object to store loading results
+ */
+ public LoadingResults prepareLoadingResults() {
+ return new LoadingResults(featuresToLoad);
+ }
+
+ /**
+ * This method sets the fix up axis state. If set to true then Y is up axis. Otherwise the up i Z axis. By default Y
+ * is up axis.
+ * @param fixUpAxis
+ * the up axis state variable
+ */
+ public void setFixUpAxis(boolean fixUpAxis) {
+ this.fixUpAxis = fixUpAxis;
+ }
+
+ /**
+ * This method returns the fix up axis state. If set to true then Y is up axis. Otherwise the up i Z axis. By
+ * default Y is up axis.
+ * @return the up axis state variable
+ */
+ public boolean isFixUpAxis() {
+ return fixUpAxis;
+ }
+
+ /**
+ * This method sets the generated textures resolution.
+ * @param generatedTexturePPU
+ * the generated textures resolution
+ */
+ public void setGeneratedTexturePPU(int generatedTexturePPU) {
+ this.generatedTexturePPU = generatedTexturePPU;
+ }
+
+ /**
+ * @return the generated textures resolution
+ */
+ public int getGeneratedTexturePPU() {
+ return generatedTexturePPU;
+ }
+
+ /**
+ * @return mipmaps generation method
+ */
+ public MipmapGenerationMethod getMipmapGenerationMethod() {
+ return mipmapGenerationMethod;
+ }
+
+ /**
+ * @param mipmapGenerationMethod
+ * mipmaps generation method
+ */
+ public void setMipmapGenerationMethod(MipmapGenerationMethod mipmapGenerationMethod) {
+ this.mipmapGenerationMethod = mipmapGenerationMethod;
+ }
+
+ /**
+ * This mehtod sets the name of the WORLD data block taht should be used during file loading. By default the name is
+ * not set. If no name is set or the given name does not occur in the file - the first WORLD data block will be used
+ * during loading (assumin any exists in the file).
+ * @param usedWorld
+ * the name of the WORLD block used during loading
+ */
+ public void setUsedWorld(String usedWorld) {
+ this.usedWorld = usedWorld;
+ }
+
+ /**
+ * This mehtod returns the name of the WORLD data block taht should be used during file loading.
+ * @return the name of the WORLD block used during loading
+ */
+ public String getUsedWorld() {
+ return usedWorld;
+ }
+
+ /**
+ * This method sets the default material for objects.
+ * @param defaultMaterial
+ * the default material
+ */
+ public void setDefaultMaterial(Material defaultMaterial) {
+ this.defaultMaterial = defaultMaterial;
+ }
+
+ /**
+ * This method returns the default material.
+ * @return the default material
+ */
+ public Material getDefaultMaterial() {
+ return defaultMaterial;
+ }
+
+ @Override
+ public void write(JmeExporter e) throws IOException {
+ super.write(e);
+ OutputCapsule oc = e.getCapsule(this);
+ oc.write(fps, "fps", DEFAULT_FPS);
+ oc.write(featuresToLoad, "features-to-load", FeaturesToLoad.ALL);
+ oc.write(loadUnlinkedAssets, "load-unlinked-assets", false);
+ oc.write(assetRootPath, "asset-root-path", null);
+ oc.write(fixUpAxis, "fix-up-axis", true);
+ oc.write(generatedTexturePPU, "generated-texture-ppu", 128);
+ oc.write(usedWorld, "used-world", null);
+ oc.write(defaultMaterial, "default-material", null);
+ oc.write(faceCullMode, "face-cull-mode", FaceCullMode.Off);
+ oc.write(layersToLoad, "layers-to-load", -1);
+ oc.write(mipmapGenerationMethod, "mipmap-generation-method", MipmapGenerationMethod.GENERATE_WHEN_NEEDED);
+ }
+
+ @Override
+ public void read(JmeImporter e) throws IOException {
+ super.read(e);
+ InputCapsule ic = e.getCapsule(this);
+ fps = ic.readInt("fps", DEFAULT_FPS);
+ featuresToLoad = ic.readInt("features-to-load", FeaturesToLoad.ALL);
+ loadUnlinkedAssets = ic.readBoolean("load-unlinked-assets", false);
+ assetRootPath = ic.readString("asset-root-path", null);
+ fixUpAxis = ic.readBoolean("fix-up-axis", true);
+ generatedTexturePPU = ic.readInt("generated-texture-ppu", 128);
+ usedWorld = ic.readString("used-world", null);
+ defaultMaterial = (Material) ic.readSavable("default-material", null);
+ faceCullMode = ic.readEnum("face-cull-mode", FaceCullMode.class, FaceCullMode.Off);
+ layersToLoad = ic.readInt("layers-to=load", -1);
+ mipmapGenerationMethod = ic.readEnum("mipmap-generation-method", MipmapGenerationMethod.class, MipmapGenerationMethod.GENERATE_WHEN_NEEDED);
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = super.hashCode();
+ result = prime * result + (assetRootPath == null ? 0 : assetRootPath.hashCode());
+ result = prime * result + (defaultMaterial == null ? 0 : defaultMaterial.hashCode());
+ result = prime * result + (faceCullMode == null ? 0 : faceCullMode.hashCode());
+ result = prime * result + featuresToLoad;
+ result = prime * result + (fixUpAxis ? 1231 : 1237);
+ result = prime * result + fps;
+ result = prime * result + generatedTexturePPU;
+ result = prime * result + layersToLoad;
+ result = prime * result + (loadUnlinkedAssets ? 1231 : 1237);
+ result = prime * result + (usedWorld == null ? 0 : usedWorld.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!super.equals(obj)) {
+ return false;
+ }
+ if (this.getClass() != obj.getClass()) {
+ return false;
+ }
+ BlenderKey other = (BlenderKey) obj;
+ if (assetRootPath == null) {
+ if (other.assetRootPath != null) {
+ return false;
+ }
+ } else if (!assetRootPath.equals(other.assetRootPath)) {
+ return false;
+ }
+ if (defaultMaterial == null) {
+ if (other.defaultMaterial != null) {
+ return false;
+ }
+ } else if (!defaultMaterial.equals(other.defaultMaterial)) {
+ return false;
+ }
+ if (faceCullMode != other.faceCullMode) {
+ return false;
+ }
+ if (featuresToLoad != other.featuresToLoad) {
+ return false;
+ }
+ if (fixUpAxis != other.fixUpAxis) {
+ return false;
+ }
+ if (fps != other.fps) {
+ return false;
+ }
+ if (generatedTexturePPU != other.generatedTexturePPU) {
+ return false;
+ }
+ if (layersToLoad != other.layersToLoad) {
+ return false;
+ }
+ if (loadUnlinkedAssets != other.loadUnlinkedAssets) {
+ return false;
+ }
+ if (usedWorld == null) {
+ if (other.usedWorld != null) {
+ return false;
+ }
+ } else if (!usedWorld.equals(other.usedWorld)) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * This enum tells the importer if the mipmaps for textures will be generated by jme. NEVER_GENERATE and ALWAYS_GENERATE are quite understandable GENERATE_WHEN_NEEDED is an option that checks if the texture had 'Generate mipmaps' option set in blender, mipmaps are generated only when the option is set
+ * @author Marcin Roguski (Kaelthas)
+ */
+ public static enum MipmapGenerationMethod {
+ NEVER_GENERATE, ALWAYS_GENERATE, GENERATE_WHEN_NEEDED;
+ }
+
+ /**
+ * This interface describes the features of the scene that are to be loaded.
+ * @author Marcin Roguski (Kaelthas)
+ */
+ public static interface FeaturesToLoad {
+
+ int SCENES = 0x0000FFFF;
+ int OBJECTS = 0x0000000B;
+ int ANIMATIONS = 0x00000004;
+ int MATERIALS = 0x00000003;
+ int TEXTURES = 0x00000001;
+ int CAMERAS = 0x00000020;
+ int LIGHTS = 0x00000010;
+ int ALL = 0xFFFFFFFF;
+ }
+
+ /**
+ * This class holds the loading results according to the given loading flag.
+ * @author Marcin Roguski (Kaelthas)
+ */
+ public static class LoadingResults extends Spatial {
+
+ /** Bitwise mask of features that are to be loaded. */
+ private final int featuresToLoad;
+ /** The scenes from the file. */
+ private List scenes;
+ /** Objects from all scenes. */
+ private List objects;
+ /** Materials from all objects. */
+ private List materials;
+ /** Textures from all objects. */
+ private List textures;
+ /** Animations of all objects. */
+ private List animations;
+ /** All cameras from the file. */
+ private List cameras;
+ /** All lights from the file. */
+ private List lights;
+
+ /**
+ * Private constructor prevents users to create an instance of this class from outside the
+ * @param featuresToLoad
+ * bitwise mask of features that are to be loaded
+ * @see FeaturesToLoad FeaturesToLoad
+ */
+ private LoadingResults(int featuresToLoad) {
+ this.featuresToLoad = featuresToLoad;
+ if ((featuresToLoad & FeaturesToLoad.SCENES) != 0) {
+ scenes = new ArrayList();
+ }
+ if ((featuresToLoad & FeaturesToLoad.OBJECTS) != 0) {
+ objects = new ArrayList();
+ if ((featuresToLoad & FeaturesToLoad.MATERIALS) != 0) {
+ materials = new ArrayList();
+ if ((featuresToLoad & FeaturesToLoad.TEXTURES) != 0) {
+ textures = new ArrayList();
+ }
+ }
+ if ((featuresToLoad & FeaturesToLoad.ANIMATIONS) != 0) {
+ animations = new ArrayList();
+ }
+ }
+ if ((featuresToLoad & FeaturesToLoad.CAMERAS) != 0) {
+ cameras = new ArrayList();
+ }
+ if ((featuresToLoad & FeaturesToLoad.LIGHTS) != 0) {
+ lights = new ArrayList();
+ }
+ }
+
+ /**
+ * This method returns a bitwise flag describing what features of the blend file will be included in the result.
+ * @return bitwise mask of features that are to be loaded
+ * @see FeaturesToLoad FeaturesToLoad
+ */
+ public int getLoadedFeatures() {
+ return featuresToLoad;
+ }
+
+ /**
+ * This method adds a scene to the result set.
+ * @param scene
+ * scene to be added to the result set
+ */
+ public void addScene(Node scene) {
+ if (scenes != null) {
+ scenes.add(scene);
+ }
+ }
+
+ /**
+ * This method adds an object to the result set.
+ * @param object
+ * object to be added to the result set
+ */
+ public void addObject(Node object) {
+ if (objects != null) {
+ objects.add(object);
+ }
+ }
+
+ /**
+ * This method adds a material to the result set.
+ * @param material
+ * material to be added to the result set
+ */
+ public void addMaterial(Material material) {
+ if (materials != null) {
+ materials.add(material);
+ }
+ }
+
+ /**
+ * This method adds a texture to the result set.
+ * @param texture
+ * texture to be added to the result set
+ */
+ public void addTexture(Texture texture) {
+ if (textures != null) {
+ textures.add(texture);
+ }
+ }
+
+ /**
+ * This method adds a camera to the result set.
+ * @param camera
+ * camera to be added to the result set
+ */
+ public void addCamera(CameraNode camera) {
+ if (cameras != null) {
+ cameras.add(camera);
+ }
+ }
+
+ /**
+ * This method adds a light to the result set.
+ * @param light
+ * light to be added to the result set
+ */
+ public void addLight(LightNode light) {
+ if (lights != null) {
+ lights.add(light);
+ }
+ }
+
+ /**
+ * This method returns all loaded scenes.
+ * @return all loaded scenes
+ */
+ public List getScenes() {
+ return scenes;
+ }
+
+ /**
+ * This method returns all loaded objects.
+ * @return all loaded objects
+ */
+ public List getObjects() {
+ return objects;
+ }
+
+ /**
+ * This method returns all loaded materials.
+ * @return all loaded materials
+ */
+ public List getMaterials() {
+ return materials;
+ }
+
+ /**
+ * This method returns all loaded textures.
+ * @return all loaded textures
+ */
+ public List getTextures() {
+ return textures;
+ }
+
+ /**
+ * This method returns all loaded animations.
+ * @return all loaded animations
+ */
+ public List getAnimations() {
+ return animations;
+ }
+
+ /**
+ * This method returns all loaded cameras.
+ * @return all loaded cameras
+ */
+ public List getCameras() {
+ return cameras;
+ }
+
+ /**
+ * This method returns all loaded lights.
+ * @return all loaded lights
+ */
+ public List getLights() {
+ return lights;
+ }
+
+ public int collideWith(Collidable other, CollisionResults results) throws UnsupportedCollisionException {
+ return 0;
+ }
+
+ @Override
+ public void updateModelBound() {
+ }
+
+ @Override
+ public void setModelBound(BoundingVolume modelBound) {
+ }
+
+ @Override
+ public int getVertexCount() {
+ return 0;
+ }
+
+ @Override
+ public int getTriangleCount() {
+ return 0;
+ }
+
+ @Override
+ public Spatial deepClone() {
+ return null;
+ }
+
+ @Override
+ public void depthFirstTraversal(SceneGraphVisitor visitor) {
+ }
+
+ @Override
+ protected void breadthFirstTraversal(SceneGraphVisitor visitor, Queue queue) {
+ }
+ }
+
+ /**
+ * The WORLD file block contains various data that could be added to the scene. The contained data includes: ambient
+ * light.
+ * @author Marcin Roguski (Kaelthas)
+ */
+ public static class WorldData {
+
+ /** The ambient light. */
+ private AmbientLight ambientLight;
+
+ /**
+ * This method returns the world's ambient light.
+ * @return the world's ambient light
+ */
+ public AmbientLight getAmbientLight() {
+ return ambientLight;
+ }
+
+ /**
+ * This method sets the world's ambient light.
+ * @param ambientLight
+ * the world's ambient light
+ */
+ public void setAmbientLight(AmbientLight ambientLight) {
+ this.ambientLight = ambientLight;
+ }
+ }
}
diff --git a/engine/src/blender/com/jme3/asset/GeneratedTextureKey.java b/engine/src/blender/com/jme3/asset/GeneratedTextureKey.java
index 7a6d9a9ed..6bd9017ae 100644
--- a/engine/src/blender/com/jme3/asset/GeneratedTextureKey.java
+++ b/engine/src/blender/com/jme3/asset/GeneratedTextureKey.java
@@ -36,7 +36,7 @@ package com.jme3.asset;
* This key is mostly used to distinguish between textures that are loaded from
* the given assets and those being generated automatically. Every generated
* texture will have this kind of key attached.
- *
+ *
* @author Marcin Roguski (Kaelthas)
*/
public class GeneratedTextureKey extends TextureKey {
@@ -44,8 +44,9 @@ public class GeneratedTextureKey extends TextureKey {
/**
* Constructor. Stores the name. Extension and folder name are empty
* strings.
- *
- * @param name the name of the texture
+ *
+ * @param name
+ * the name of the texture
*/
public GeneratedTextureKey(String name) {
super(name);
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/AbstractBlenderHelper.java b/engine/src/blender/com/jme3/scene/plugins/blender/AbstractBlenderHelper.java
index d9ccf91c5..9e52e7ba3 100644
--- a/engine/src/blender/com/jme3/scene/plugins/blender/AbstractBlenderHelper.java
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/AbstractBlenderHelper.java
@@ -50,116 +50,116 @@ import com.jme3.scene.plugins.blender.objects.Properties;
*/
public abstract class AbstractBlenderHelper {
- /** The version of the blend file. */
- protected final int blenderVersion;
- /** This variable indicates if the Y asxis is the UP axis or not. */
- protected boolean fixUpAxis;
- /** Quaternion used to rotate data when Y is up axis. */
- protected Quaternion upAxisRotationQuaternion;
-
- /**
- * 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
- * @param fixUpAxis
- * a variable that indicates if the Y asxis is the UP axis or not
- */
- public AbstractBlenderHelper(String blenderVersion, boolean fixUpAxis) {
- this.blenderVersion = Integer.parseInt(blenderVersion);
- this.fixUpAxis = fixUpAxis;
- if(fixUpAxis) {
- upAxisRotationQuaternion = new Quaternion().fromAngles(-FastMath.HALF_PI, 0, 0);
- }
- }
-
- /**
- * This method clears the state of the helper so that it can be used for different calculations of another feature.
- */
- public void clearState() {}
+ /** The version of the blend file. */
+ protected final int blenderVersion;
+ /** This variable indicates if the Y asxis is the UP axis or not. */
+ protected boolean fixUpAxis;
+ /** Quaternion used to rotate data when Y is up axis. */
+ protected Quaternion upAxisRotationQuaternion;
- /**
- * This method should be used to check if the text is blank. Avoid using text.trim().length()==0. This causes that more strings are
- * being created and stored in the memory. It can be unwise especially inside loops.
- * @param text
- * the text to be checked
- * @return true if the text is blank and false 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;
- }
+ /**
+ * 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
+ * @param fixUpAxis
+ * a variable that indicates if the Y asxis is the UP axis or not
+ */
+ public AbstractBlenderHelper(String blenderVersion, boolean fixUpAxis) {
+ this.blenderVersion = Integer.parseInt(blenderVersion);
+ this.fixUpAxis = fixUpAxis;
+ if (fixUpAxis) {
+ upAxisRotationQuaternion = new Quaternion().fromAngles(-FastMath.HALF_PI, 0, 0);
+ }
+ }
- /**
- * This method loads the properties if they are available and defined for the structure.
- * @param structure
- * the structure we read the properties from
- * @param blenderContext
- * the blender context
- * @return loaded properties or null if they are not available
- * @throws BlenderFileException
- * an exception is thrown when the blend file is somehow corrupted
- */
- protected Properties loadProperties(Structure structure, BlenderContext blenderContext) throws BlenderFileException {
- Properties properties = null;
- Structure id = (Structure) structure.getFieldValue("ID");
- if (id != null) {
- Pointer pProperties = (Pointer) id.getFieldValue("properties");
- if (pProperties.isNotNull()) {
- Structure propertiesStructure = pProperties.fetchData(blenderContext.getInputStream()).get(0);
- properties = new Properties();
- properties.load(propertiesStructure, blenderContext);
- }
- }
- return properties;
- }
-
- /**
- * The method applies properties to the given spatial. The Properties
- * instance cannot be directly applied because the end-user might not have
- * the blender plugin jar file and thus receive ClassNotFoundException. The
- * values are set by name instead.
- *
- * @param spatial
- * the spatial that is to have properties applied
- * @param properties
- * the properties to be applied
- */
- protected void applyProperties(Spatial spatial, Properties properties) {
- List propertyNames = properties.getSubPropertiesNames();
- if(propertyNames != null && propertyNames.size() > 0) {
- for(String propertyName : propertyNames) {
- Object value = properties.findValue(propertyName);
- if(value instanceof Savable || value instanceof Boolean || value instanceof String ||
- value instanceof Float || value instanceof Integer || value instanceof Long) {
- spatial.setUserData(propertyName, value);
- } else if(value instanceof Double) {
- spatial.setUserData(propertyName, ((Double) value).floatValue());
- } else if(value instanceof int[]) {
- spatial.setUserData(propertyName, Arrays.toString((int[])value));
- } else if(value instanceof float[]) {
- spatial.setUserData(propertyName, Arrays.toString((float[])value));
- } else if(value instanceof double[]) {
- spatial.setUserData(propertyName, Arrays.toString((double[])value));
- }
- }
- }
- }
-
- /**
- * This method analyzes the given structure and the data contained within
- * blender context and decides if the feature should be loaded.
- * @param structure
- * structure to be analyzed
- * @param blenderContext
- * the blender context
- * @return true if the feature should be loaded and false otherwise
- */
- public abstract boolean shouldBeLoaded(Structure structure, BlenderContext blenderContext);
+ /**
+ * This method clears the state of the helper so that it can be used for different calculations of another feature.
+ */
+ public void clearState() {
+ }
+
+ /**
+ * This method should be used to check if the text is blank. Avoid using text.trim().length()==0. This causes that more strings are
+ * being created and stored in the memory. It can be unwise especially inside loops.
+ * @param text
+ * the text to be checked
+ * @return true if the text is blank and false 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;
+ }
+
+ /**
+ * This method loads the properties if they are available and defined for the structure.
+ * @param structure
+ * the structure we read the properties from
+ * @param blenderContext
+ * the blender context
+ * @return loaded properties or null if they are not available
+ * @throws BlenderFileException
+ * an exception is thrown when the blend file is somehow corrupted
+ */
+ protected Properties loadProperties(Structure structure, BlenderContext blenderContext) throws BlenderFileException {
+ Properties properties = null;
+ Structure id = (Structure) structure.getFieldValue("ID");
+ if (id != null) {
+ Pointer pProperties = (Pointer) id.getFieldValue("properties");
+ if (pProperties.isNotNull()) {
+ Structure propertiesStructure = pProperties.fetchData(blenderContext.getInputStream()).get(0);
+ properties = new Properties();
+ properties.load(propertiesStructure, blenderContext);
+ }
+ }
+ return properties;
+ }
+
+ /**
+ * The method applies properties to the given spatial. The Properties
+ * instance cannot be directly applied because the end-user might not have
+ * the blender plugin jar file and thus receive ClassNotFoundException. The
+ * values are set by name instead.
+ *
+ * @param spatial
+ * the spatial that is to have properties applied
+ * @param properties
+ * the properties to be applied
+ */
+ protected void applyProperties(Spatial spatial, Properties properties) {
+ List propertyNames = properties.getSubPropertiesNames();
+ if (propertyNames != null && propertyNames.size() > 0) {
+ for (String propertyName : propertyNames) {
+ Object value = properties.findValue(propertyName);
+ if (value instanceof Savable || value instanceof Boolean || value instanceof String || value instanceof Float || value instanceof Integer || value instanceof Long) {
+ spatial.setUserData(propertyName, value);
+ } else if (value instanceof Double) {
+ spatial.setUserData(propertyName, ((Double) value).floatValue());
+ } else if (value instanceof int[]) {
+ spatial.setUserData(propertyName, Arrays.toString((int[]) value));
+ } else if (value instanceof float[]) {
+ spatial.setUserData(propertyName, Arrays.toString((float[]) value));
+ } else if (value instanceof double[]) {
+ spatial.setUserData(propertyName, Arrays.toString((double[]) value));
+ }
+ }
+ }
+ }
+
+ /**
+ * This method analyzes the given structure and the data contained within
+ * blender context and decides if the feature should be loaded.
+ * @param structure
+ * structure to be analyzed
+ * @param blenderContext
+ * the blender context
+ * @return true if the feature should be loaded and false otherwise
+ */
+ public abstract boolean shouldBeLoaded(Structure structure, BlenderContext blenderContext);
}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/AbstractBlenderLoader.java b/engine/src/blender/com/jme3/scene/plugins/blender/AbstractBlenderLoader.java
index b1953eaaa..8b6323f01 100644
--- a/engine/src/blender/com/jme3/scene/plugins/blender/AbstractBlenderLoader.java
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/AbstractBlenderLoader.java
@@ -57,134 +57,134 @@ import com.jme3.scene.plugins.blender.objects.ObjectHelper;
* This class converts blender file blocks into jMonkeyEngine data structures.
* @author Marcin Roguski (Kaelthas)
*/
-/* package */ abstract class AbstractBlenderLoader implements AssetLoader {
- private static final Logger LOGGER = Logger.getLogger(AbstractBlenderLoader.class.getName());
-
- protected BlenderContext blenderContext;
+/* package */abstract class AbstractBlenderLoader implements AssetLoader {
+ private static final Logger LOGGER = Logger.getLogger(AbstractBlenderLoader.class.getName());
- /**
- * This method converts the given structure to a scene node.
- * @param structure
- * structure of a scene
- * @return scene's node
- */
- public Node toScene(Structure structure) {
- if ((blenderContext.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.SCENES) == 0) {
- return null;
- }
- Node result = new Node(structure.getName());
- try {
- List base = ((Structure)structure.getFieldValue("base")).evaluateListBase(blenderContext);
- for(Structure b : base) {
- Pointer pObject = (Pointer) b.getFieldValue("object");
- if(pObject.isNotNull()) {
- Structure objectStructure = pObject.fetchData(blenderContext.getInputStream()).get(0);
- Object object = this.toObject(objectStructure);
- if(object instanceof LightNode && (blenderContext.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.LIGHTS) != 0) {
- result.addLight(((LightNode)object).getLight());
- result.attachChild((LightNode) object);
- } else if (object instanceof Node && (blenderContext.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.OBJECTS) != 0) {
- LOGGER.log(Level.FINE, "{0}: {1}--> {2}", new Object[] { ((Node) object).getName(), ((Node) object).getLocalTranslation().toString(), ((Node) object).getParent() == null ? "null" : ((Node) object).getParent().getName() });
- if (((Node) object).getParent() == null) {
- result.attachChild((Spatial) object);
+ protected BlenderContext blenderContext;
+
+ /**
+ * This method converts the given structure to a scene node.
+ * @param structure
+ * structure of a scene
+ * @return scene's node
+ */
+ public Node toScene(Structure structure) {
+ if ((blenderContext.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.SCENES) == 0) {
+ return null;
+ }
+ Node result = new Node(structure.getName());
+ try {
+ List base = ((Structure) structure.getFieldValue("base")).evaluateListBase(blenderContext);
+ for (Structure b : base) {
+ Pointer pObject = (Pointer) b.getFieldValue("object");
+ if (pObject.isNotNull()) {
+ Structure objectStructure = pObject.fetchData(blenderContext.getInputStream()).get(0);
+ Object object = this.toObject(objectStructure);
+ if (object instanceof LightNode && (blenderContext.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.LIGHTS) != 0) {
+ result.addLight(((LightNode) object).getLight());
+ result.attachChild((LightNode) object);
+ } else if (object instanceof Node && (blenderContext.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.OBJECTS) != 0) {
+ LOGGER.log(Level.FINE, "{0}: {1}--> {2}", new Object[] { ((Node) object).getName(), ((Node) object).getLocalTranslation().toString(), ((Node) object).getParent() == null ? "null" : ((Node) object).getParent().getName() });
+ if (((Node) object).getParent() == null) {
+ result.attachChild((Spatial) object);
}
- }
- }
- }
- } catch (BlenderFileException e) {
- LOGGER.log(Level.SEVERE, e.getLocalizedMessage(), e);
- }
- return result;
- }
+ }
+ }
+ }
+ } catch (BlenderFileException e) {
+ LOGGER.log(Level.SEVERE, e.getLocalizedMessage(), e);
+ }
+ return result;
+ }
- /**
- * This method converts the given structure to a camera.
- * @param structure
- * structure of a camera
- * @return camera's node
- */
- public CameraNode toCamera(Structure structure) throws BlenderFileException {
- CameraHelper cameraHelper = blenderContext.getHelper(CameraHelper.class);
- if (cameraHelper.shouldBeLoaded(structure, blenderContext)) {
- return cameraHelper.toCamera(structure, blenderContext);
- }
- return null;
- }
+ /**
+ * This method converts the given structure to a camera.
+ * @param structure
+ * structure of a camera
+ * @return camera's node
+ */
+ public CameraNode toCamera(Structure structure) throws BlenderFileException {
+ CameraHelper cameraHelper = blenderContext.getHelper(CameraHelper.class);
+ if (cameraHelper.shouldBeLoaded(structure, blenderContext)) {
+ return cameraHelper.toCamera(structure, blenderContext);
+ }
+ return null;
+ }
- /**
- * This method converts the given structure to a light.
- * @param structure
- * structure of a light
- * @return light's node
- */
- public LightNode toLight(Structure structure) throws BlenderFileException {
- LightHelper lightHelper = blenderContext.getHelper(LightHelper.class);
- if (lightHelper.shouldBeLoaded(structure, blenderContext)) {
- return lightHelper.toLight(structure, blenderContext);
- }
- return null;
- }
+ /**
+ * This method converts the given structure to a light.
+ * @param structure
+ * structure of a light
+ * @return light's node
+ */
+ public LightNode toLight(Structure structure) throws BlenderFileException {
+ LightHelper lightHelper = blenderContext.getHelper(LightHelper.class);
+ if (lightHelper.shouldBeLoaded(structure, blenderContext)) {
+ return lightHelper.toLight(structure, blenderContext);
+ }
+ return null;
+ }
- /**
- * This method converts the given structure to a node.
- * @param structure
- * structure of an object
- * @return object's node
- */
- public Object toObject(Structure structure) throws BlenderFileException {
- ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class);
- if (objectHelper.shouldBeLoaded(structure, blenderContext)) {
- return objectHelper.toObject(structure, blenderContext);
- }
- return null;
- }
+ /**
+ * This method converts the given structure to a node.
+ * @param structure
+ * structure of an object
+ * @return object's node
+ */
+ public Object toObject(Structure structure) throws BlenderFileException {
+ ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class);
+ if (objectHelper.shouldBeLoaded(structure, blenderContext)) {
+ return objectHelper.toObject(structure, blenderContext);
+ }
+ return null;
+ }
- /**
- * This method converts the given structure to a list of geometries.
- * @param structure
- * structure of a mesh
- * @return list of geometries
- */
- public List toMesh(Structure structure) throws BlenderFileException {
- MeshHelper meshHelper = blenderContext.getHelper(MeshHelper.class);
- if (meshHelper.shouldBeLoaded(structure, blenderContext)) {
- return meshHelper.toMesh(structure, blenderContext);
- }
- return null;
- }
+ /**
+ * This method converts the given structure to a list of geometries.
+ * @param structure
+ * structure of a mesh
+ * @return list of geometries
+ */
+ public List toMesh(Structure structure) throws BlenderFileException {
+ MeshHelper meshHelper = blenderContext.getHelper(MeshHelper.class);
+ if (meshHelper.shouldBeLoaded(structure, blenderContext)) {
+ return meshHelper.toMesh(structure, blenderContext);
+ }
+ return null;
+ }
-// /**
-// * This method converts the given structure to a material.
-// * @param structure
-// * structure of a material
-// * @return material's node
-// */
-// public Material toMaterial(Structure structure) throws BlenderFileException {
-// MaterialHelper materialHelper = blenderContext.getHelper(MaterialHelper.class);
-// if (materialHelper.shouldBeLoaded(structure, blenderContext)) {
-// return materialHelper.toMaterial(structure, blenderContext);
-// }
-// return null;
-// }
+ // /**
+ // * This method converts the given structure to a material.
+ // * @param structure
+ // * structure of a material
+ // * @return material's node
+ // */
+ // public Material toMaterial(Structure structure) throws BlenderFileException {
+ // MaterialHelper materialHelper = blenderContext.getHelper(MaterialHelper.class);
+ // if (materialHelper.shouldBeLoaded(structure, blenderContext)) {
+ // return materialHelper.toMaterial(structure, blenderContext);
+ // }
+ // return null;
+ // }
- /**
- * 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.
- * @param structure
- * the structure with WORLD block data
- * @return data read from the WORLD block that can be added to the scene
- */
- public WorldData toWorldData(Structure structure) {
- WorldData result = new WorldData();
+ /**
+ * 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.
+ * @param structure
+ * the structure with WORLD block data
+ * @return data read from the WORLD block that can be added to the scene
+ */
+ public WorldData toWorldData(Structure structure) {
+ WorldData result = new WorldData();
- // reading ambient light
- AmbientLight ambientLight = new AmbientLight();
- float ambr = ((Number) structure.getFieldValue("ambr")).floatValue();
- float ambg = ((Number) structure.getFieldValue("ambg")).floatValue();
- float ambb = ((Number) structure.getFieldValue("ambb")).floatValue();
- ambientLight.setColor(new ColorRGBA(ambr, ambg, ambb, 0.0f));
- result.setAmbientLight(ambientLight);
+ // reading ambient light
+ AmbientLight ambientLight = new AmbientLight();
+ float ambr = ((Number) structure.getFieldValue("ambr")).floatValue();
+ float ambg = ((Number) structure.getFieldValue("ambg")).floatValue();
+ float ambb = ((Number) structure.getFieldValue("ambb")).floatValue();
+ ambientLight.setColor(new ColorRGBA(ambr, ambg, ambb, 0.0f));
+ result.setAmbientLight(ambientLight);
- return result;
- }
+ return result;
+ }
}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/BlenderContext.java b/engine/src/blender/com/jme3/scene/plugins/blender/BlenderContext.java
index fcdb423c7..a786e8ad4 100644
--- a/engine/src/blender/com/jme3/scene/plugins/blender/BlenderContext.java
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/BlenderContext.java
@@ -65,625 +65,626 @@ import com.jme3.scene.plugins.ogre.AnimData;
* @author Marcin Roguski (Kaelthas)
*/
public class BlenderContext {
- private static final Logger LOGGER = Logger.getLogger(BlenderContext.class.getName());
-
- /** The blender file version. */
- private int blenderVersion;
- /** The blender key. */
- private BlenderKey blenderKey;
- /** The header of the file block. */
- private DnaBlockData dnaBlockData;
- /** The scene structure. */
- private Structure sceneStructure;
- /** The input stream of the blend file. */
- private BlenderInputStream inputStream;
- /** The asset manager. */
- private AssetManager assetManager;
- /**
- * A map containing the file block headers. The key is the old pointer
- * address.
- */
- private Map fileBlockHeadersByOma = new HashMap();
- /** A map containing the file block headers. The key is the block code. */
- private Map> fileBlockHeadersByCode = new HashMap>();
- /**
- * 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 loadedFeatures = new HashMap();
- /**
- * 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
- * loaded structure and the second - the structure already converted into
- * proper data.
- */
- private Map loadedFeaturesByName = new HashMap();
- /** A stack that hold the parent structure of currently loaded feature. */
- private Stack parentStack = new Stack();
- /**
- * A map storing loaded ipos. The key is the ipo's owner old memory address
- * and the value is the ipo.
- */
- private Map loadedIpos = new HashMap();
- /** A list of modifiers for the specified object. */
- protected Map> modifiers = new HashMap>();
- /** A list of constraints for the specified object. */
- protected Map> constraints = new HashMap>();
- /** Anim data loaded for features. */
- private Map animData = new HashMap();
- /** Loaded skeletons. */
- private Map skeletons = new HashMap();
- /** A map of mesh contexts. */
- protected Map meshContexts = new HashMap();
- /** A map of bone contexts. */
- protected Map boneContexts = new HashMap();
- /** A map og helpers that perform loading. */
- private Map helpers = new HashMap();
-
- /**
- * This method sets the blender file version.
- *
- * @param blenderVersion
- * the blender file version
- */
- public void setBlenderVersion(String blenderVersion) {
- this.blenderVersion = Integer.parseInt(blenderVersion);
- }
-
- /**
- * @return the blender file version
- */
- public int getBlenderVersion() {
- return blenderVersion;
- }
-
- /**
- * This method sets the blender key.
- *
- * @param blenderKey
- * the blender key
- */
- public void setBlenderKey(BlenderKey blenderKey) {
- this.blenderKey = blenderKey;
- }
-
- /**
- * This method returns the blender key.
- *
- * @return the blender key
- */
- public BlenderKey getBlenderKey() {
- return blenderKey;
- }
-
- /**
- * This method sets the dna block data.
- *
- * @param dnaBlockData
- * the dna block data
- */
- public void setBlockData(DnaBlockData dnaBlockData) {
- this.dnaBlockData = dnaBlockData;
- }
-
- /**
- * This method returns the dna block data.
- *
- * @return the dna block data
- */
- public DnaBlockData getDnaBlockData() {
- return dnaBlockData;
- }
- /**
- * This method sets the scene structure data.
- *
- * @param sceneStructure
- * the scene structure data
- */
- public void setSceneStructure(Structure sceneStructure) {
- this.sceneStructure = sceneStructure;
- }
-
- /**
- * This method returns the scene structure data.
- *
- * @return the scene structure data
- */
- public Structure getSceneStructure() {
- return sceneStructure;
- }
-
- /**
- * This method returns the asset manager.
- *
- * @return the asset manager
- */
- public AssetManager getAssetManager() {
- return assetManager;
- }
-
- /**
- * This method sets the asset manager.
- *
- * @param assetManager
- * the asset manager
- */
- 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
- */
- public BlenderInputStream getInputStream() {
- return inputStream;
- }
-
- /**
- * This method sets the input stream of the blend file.
- *
- * @param inputStream
- * the input stream of the blend file
- */
- 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
- * the address of the block header
- * @param fileBlockHeader
- * the block header to store
- */
- public void addFileBlockHeader(Long oldMemoryAddress, FileBlockHeader fileBlockHeader) {
- fileBlockHeadersByOma.put(oldMemoryAddress, fileBlockHeader);
- List headers = fileBlockHeadersByCode.get(Integer.valueOf(fileBlockHeader.getCode()));
- if (headers == null) {
- headers = new ArrayList();
- fileBlockHeadersByCode.put(Integer.valueOf(fileBlockHeader.getCode()), headers);
- }
- headers.add(fileBlockHeader);
- }
-
- /**
- * This method returns the block header of a given memory address. If the
- * header is not present then null is returned.
- *
- * @param oldMemoryAddress
- * 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);
- }
-
- /**
- * This method returns a list of file blocks' headers of a specified code.
- *
- * @param code
- * the code of file blocks
- * @return a list of file blocks' headers of a specified code
- */
- public List getFileBlocks(Integer code) {
- return fileBlockHeadersByCode.get(code);
- }
-
- /**
- * This method clears the saved block headers stored in the features map.
- */
- public void clearFileBlocks() {
- fileBlockHeadersByOma.clear();
- fileBlockHeadersByCode.clear();
- }
-
- /**
- * This method adds a helper instance to the helpers' map.
- *
- * @param
- * the type of the helper
- * @param clazz
- * helper's class definition
- * @param helper
- * the helper instance
- */
- public void putHelper(Class clazz, AbstractBlenderHelper helper) {
- helpers.put(clazz.getSimpleName(), helper);
- }
-
- @SuppressWarnings("unchecked")
- public 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.
- *
- * @param oldMemoryAddress
- * the address of the feature
- * @param featureName
- * the name of the feature
- * @param structure
- * the filled structure of the feature
- * @param feature
- * the feature we want to store
- */
- public void addLoadedFeatures(Long oldMemoryAddress, String featureName, Structure structure, Object feature) {
- if (oldMemoryAddress == null || structure == null || feature == null) {
- throw new IllegalArgumentException("One of the given arguments is null!");
- }
- Object[] storedData = new Object[] { structure, feature };
- loadedFeatures.put(oldMemoryAddress, storedData);
- if (featureName != null) {
- loadedFeaturesByName.put(featureName, storedData);
- }
- }
-
- /**
- * This method returns the feature of a given memory address. If the feature
- * is not yet loaded then null is returned.
- *
- * @param oldMemoryAddress
- * the address of the feature
- * @param loadedFeatureDataType
- * 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
- */
- public Object getLoadedFeature(Long oldMemoryAddress, LoadedFeatureDataType loadedFeatureDataType) {
- Object[] result = loadedFeatures.get(oldMemoryAddress);
- if (result != null) {
- return result[loadedFeatureDataType.getIndex()];
- }
- return null;
- }
-
- /**
- * This method returns the feature of a given name. If the feature is not
- * yet loaded then null is returned.
- *
- * @param featureName
- * the name of the feature
- * @param loadedFeatureDataType
- * 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
- */
- public Object getLoadedFeature(String featureName, LoadedFeatureDataType loadedFeatureDataType) {
- Object[] result = loadedFeaturesByName.get(featureName);
- if (result != null) {
- return result[loadedFeatureDataType.getIndex()];
- }
- return null;
- }
-
- /**
- * This method clears the saved features stored in the features map.
- */
- public void clearLoadedFeatures() {
- loadedFeatures.clear();
- }
-
- /**
- * This method adds the structure to the parent stack.
- *
- * @param parent
- * the structure to be added to the stack
- */
- public void pushParent(Structure parent) {
- parentStack.push(parent);
- }
-
- /**
- * This method removes the structure from the top of the parent's stack.
- *
- * @return the structure that was removed from the stack
- */
- public Structure popParent() {
- try {
- return parentStack.pop();
- } catch (EmptyStackException e) {
- return null;
- }
- }
-
- /**
- * 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
- */
- public Structure peekParent() {
- try {
- return parentStack.peek();
- } catch (EmptyStackException e) {
- return null;
- }
- }
-
- /**
- * This method adds new ipo curve for the feature.
- *
- * @param ownerOMA
- * the OMA of blender feature that owns the ipo
- * @param ipo
- * the ipo to be added
- */
- public void addIpo(Long ownerOMA, Ipo ipo) {
- loadedIpos.put(ownerOMA, ipo);
- }
-
- /**
- * This method removes the ipo curve from the feature.
- *
- * @param ownerOma
- * the OMA of blender feature that owns the ipo
- */
- public Ipo removeIpo(Long ownerOma) {
- return loadedIpos.remove(ownerOma);
- }
-
- /**
- * This method returns the ipo curve of the feature.
- *
- * @param ownerOMA
- * the OMA of blender feature that owns the ipo
- */
- public Ipo getIpo(Long ownerOMA) {
- return loadedIpos.get(ownerOMA);
- }
-
- /**
- * This method adds a new modifier to the list.
- *
- * @param ownerOMA
- * the owner's old memory address
- * @param modifier
- * the object's modifier
- */
- public void addModifier(Long ownerOMA, Modifier modifier) {
- List objectModifiers = this.modifiers.get(ownerOMA);
- if (objectModifiers == null) {
- objectModifiers = new ArrayList();
- this.modifiers.put(ownerOMA, objectModifiers);
- }
- objectModifiers.add(modifier);
- }
-
- /**
- * 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.
- *
- * @param objectOMA
- * object's old memory address
- * @param type
- * the type of the modifier
- * @return the list of object's modifiers
- */
- public List getModifiers(Long objectOMA, String type) {
- List result = new ArrayList();
- List readModifiers = modifiers.get(objectOMA);
- if (readModifiers != null && readModifiers.size() > 0) {
- for (Modifier modifier : readModifiers) {
- if (type == null || type.isEmpty() || modifier.getType().equals(type)) {
- result.add(modifier);
- }
- }
- }
- return result;
- }
-
- /**
- * This method adds a new modifier to the list.
- *
- * @param ownerOMA
- * the owner's old memory address
- * @param constraints
- * the object's constraints
- */
- public void addConstraints(Long ownerOMA, List constraints) {
- List objectConstraints = this.constraints.get(ownerOMA);
- if (objectConstraints == null) {
- objectConstraints = new ArrayList();
- this.constraints.put(ownerOMA, objectConstraints);
- }
- objectConstraints.addAll(constraints);
- }
-
- /**
- * This method returns constraints for the object specified by its old
- * memory address. If no modifiers are found - null is returned.
- *
- * @param objectOMA
- * object's old memory address
- * @return the list of object's modifiers or null
- */
- public List getConstraints(Long objectOMA) {
- return objectOMA == null ? null : constraints.get(objectOMA);
- }
-
- /**
- * @return all available constraints
- */
- public List getAllConstraints() {
- List result = new ArrayList();
- for(Entry> entry : constraints.entrySet()) {
- result.addAll(entry.getValue());
- }
- return result;
- }
-
- /**
- * This method sets the anim data for the specified OMA of its owner.
- *
- * @param ownerOMA
- * the owner's old memory address
- * @param animData
- * the animation data for the feature specified by ownerOMA
- */
- public void setAnimData(Long ownerOMA, AnimData animData) {
- this.animData.put(ownerOMA, animData);
- }
-
- /**
- * This method returns the animation data for the specified owner.
- *
- * @param ownerOMA
- * the old memory address of the animation data owner
- * @return the animation data or null if none exists
- */
- public AnimData getAnimData(Long ownerOMA) {
- return this.animData.get(ownerOMA);
- }
-
- /**
- * This method sets the skeleton for the specified OMA of its owner.
- *
- * @param skeletonOMA
- * the skeleton's old memory address
- * @param skeleton
- * the skeleton specified by the given OMA
- */
- public void setSkeleton(Long skeletonOMA, Skeleton skeleton) {
- this.skeletons.put(skeletonOMA, skeleton);
- }
-
- /**
- * This method returns the skeleton for the specified OMA of its owner.
- *
- * @param skeletonOMA
- * the skeleton's old memory address
- * @return the skeleton specified by the given OMA
- */
- public Skeleton getSkeleton(Long skeletonOMA) {
- return this.skeletons.get(skeletonOMA);
- }
-
- /**
- * This method sets the mesh context for the given mesh old memory address.
- * If the context is already set it will be replaced.
- *
- * @param meshOMA
- * the mesh's old memory address
- * @param meshContext
- * the mesh's context
- */
- public void setMeshContext(Long meshOMA, MeshContext meshContext) {
- this.meshContexts.put(meshOMA, meshContext);
- }
-
- /**
- * This method returns the mesh context for the given mesh old memory
- * address. If no context exists then null is returned.
- *
- * @param meshOMA
- * the mesh's old memory address
- * @return mesh's context
- */
- public MeshContext getMeshContext(Long meshOMA) {
- return this.meshContexts.get(meshOMA);
- }
-
- /**
- * This method sets the bone context for the given bone old memory address.
- * If the context is already set it will be replaced.
- *
- * @param boneOMA
- * the bone's old memory address
- * @param boneContext
- * the bones's context
- */
- public void setBoneContext(Long boneOMA, BoneContext boneContext) {
- this.boneContexts.put(boneOMA, boneContext);
- }
-
- /**
- * This method returns the bone context for the given bone old memory
- * address. If no context exists then null is returned.
- *
- * @param boneOMA
- * the bone's old memory address
- * @return bone's context
- */
- public BoneContext getBoneContext(Long boneOMA) {
- return boneContexts.get(boneOMA);
- }
-
- /**
- * Returns bone by given name.
- *
- * @param name
- * the name of the bone
- * @return found bone or null if none bone of a given name exists
- */
- public BoneContext getBoneByName(String name) {
- for(Entry entry : boneContexts.entrySet()) {
- Bone bone = entry.getValue().getBone();
- if(bone != null && name.equals(bone.getName())) {
- return entry.getValue();
- }
- }
- return null;
- }
-
- /**
- * This metod returns the default material.
- *
- * @return the default material
- */
- public synchronized Material getDefaultMaterial() {
- if (blenderKey.getDefaultMaterial() == null) {
- Material defaultMaterial = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
- defaultMaterial.setColor("Color", ColorRGBA.DarkGray);
- blenderKey.setDefaultMaterial(defaultMaterial);
- }
- return blenderKey.getDefaultMaterial();
- }
-
- /**
- * Clears all sotred resources and closes the blender input stream.
- */
- public void dispose() {
- LOGGER.fine("Disposing blender context resources.");
- inputStream.forceClose();
- loadedFeatures.clear();
- loadedFeaturesByName.clear();
- parentStack.clear();
- loadedIpos.clear();
- modifiers.clear();
- constraints.clear();
- animData.clear();
- skeletons.clear();
- meshContexts.clear();
- boneContexts.clear();
- helpers.clear();
- }
-
- /**
- * This enum defines what loaded data type user wants to retreive. It can be
- * either filled structure or already converted data.
- *
- * @author Marcin Roguski
- */
- public static enum LoadedFeatureDataType {
-
- LOADED_STRUCTURE(0), LOADED_FEATURE(1);
- private int index;
-
- private LoadedFeatureDataType(int index) {
- this.index = index;
- }
-
- public int getIndex() {
- return index;
- }
- }
+ private static final Logger LOGGER = Logger.getLogger(BlenderContext.class.getName());
+
+ /** The blender file version. */
+ private int blenderVersion;
+ /** The blender key. */
+ private BlenderKey blenderKey;
+ /** The header of the file block. */
+ private DnaBlockData dnaBlockData;
+ /** The scene structure. */
+ private Structure sceneStructure;
+ /** The input stream of the blend file. */
+ private BlenderInputStream inputStream;
+ /** The asset manager. */
+ private AssetManager assetManager;
+ /**
+ * A map containing the file block headers. The key is the old pointer
+ * address.
+ */
+ private Map fileBlockHeadersByOma = new HashMap();
+ /** A map containing the file block headers. The key is the block code. */
+ private Map> fileBlockHeadersByCode = new HashMap>();
+ /**
+ * 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 loadedFeatures = new HashMap();
+ /**
+ * 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
+ * loaded structure and the second - the structure already converted into
+ * proper data.
+ */
+ private Map loadedFeaturesByName = new HashMap();
+ /** A stack that hold the parent structure of currently loaded feature. */
+ private Stack parentStack = new Stack();
+ /**
+ * A map storing loaded ipos. The key is the ipo's owner old memory address
+ * and the value is the ipo.
+ */
+ private Map loadedIpos = new HashMap();
+ /** A list of modifiers for the specified object. */
+ protected Map> modifiers = new HashMap>();
+ /** A list of constraints for the specified object. */
+ protected Map> constraints = new HashMap>();
+ /** Anim data loaded for features. */
+ private Map animData = new HashMap();
+ /** Loaded skeletons. */
+ private Map skeletons = new HashMap();
+ /** A map of mesh contexts. */
+ protected Map meshContexts = new HashMap();
+ /** A map of bone contexts. */
+ protected Map boneContexts = new HashMap();
+ /** A map og helpers that perform loading. */
+ private Map helpers = new HashMap();
+
+ /**
+ * This method sets the blender file version.
+ *
+ * @param blenderVersion
+ * the blender file version
+ */
+ public void setBlenderVersion(String blenderVersion) {
+ this.blenderVersion = Integer.parseInt(blenderVersion);
+ }
+
+ /**
+ * @return the blender file version
+ */
+ public int getBlenderVersion() {
+ return blenderVersion;
+ }
+
+ /**
+ * This method sets the blender key.
+ *
+ * @param blenderKey
+ * the blender key
+ */
+ public void setBlenderKey(BlenderKey blenderKey) {
+ this.blenderKey = blenderKey;
+ }
+
+ /**
+ * This method returns the blender key.
+ *
+ * @return the blender key
+ */
+ public BlenderKey getBlenderKey() {
+ return blenderKey;
+ }
+
+ /**
+ * This method sets the dna block data.
+ *
+ * @param dnaBlockData
+ * the dna block data
+ */
+ public void setBlockData(DnaBlockData dnaBlockData) {
+ this.dnaBlockData = dnaBlockData;
+ }
+
+ /**
+ * This method returns the dna block data.
+ *
+ * @return the dna block data
+ */
+ public DnaBlockData getDnaBlockData() {
+ return dnaBlockData;
+ }
+
+ /**
+ * This method sets the scene structure data.
+ *
+ * @param sceneStructure
+ * the scene structure data
+ */
+ public void setSceneStructure(Structure sceneStructure) {
+ this.sceneStructure = sceneStructure;
+ }
+
+ /**
+ * This method returns the scene structure data.
+ *
+ * @return the scene structure data
+ */
+ public Structure getSceneStructure() {
+ return sceneStructure;
+ }
+
+ /**
+ * This method returns the asset manager.
+ *
+ * @return the asset manager
+ */
+ public AssetManager getAssetManager() {
+ return assetManager;
+ }
+
+ /**
+ * This method sets the asset manager.
+ *
+ * @param assetManager
+ * the asset manager
+ */
+ 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
+ */
+ public BlenderInputStream getInputStream() {
+ return inputStream;
+ }
+
+ /**
+ * This method sets the input stream of the blend file.
+ *
+ * @param inputStream
+ * the input stream of the blend file
+ */
+ 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
+ * the address of the block header
+ * @param fileBlockHeader
+ * the block header to store
+ */
+ public void addFileBlockHeader(Long oldMemoryAddress, FileBlockHeader fileBlockHeader) {
+ fileBlockHeadersByOma.put(oldMemoryAddress, fileBlockHeader);
+ List headers = fileBlockHeadersByCode.get(Integer.valueOf(fileBlockHeader.getCode()));
+ if (headers == null) {
+ headers = new ArrayList();
+ fileBlockHeadersByCode.put(Integer.valueOf(fileBlockHeader.getCode()), headers);
+ }
+ headers.add(fileBlockHeader);
+ }
+
+ /**
+ * This method returns the block header of a given memory address. If the
+ * header is not present then null is returned.
+ *
+ * @param oldMemoryAddress
+ * 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);
+ }
+
+ /**
+ * This method returns a list of file blocks' headers of a specified code.
+ *
+ * @param code
+ * the code of file blocks
+ * @return a list of file blocks' headers of a specified code
+ */
+ public List getFileBlocks(Integer code) {
+ return fileBlockHeadersByCode.get(code);
+ }
+
+ /**
+ * This method clears the saved block headers stored in the features map.
+ */
+ public void clearFileBlocks() {
+ fileBlockHeadersByOma.clear();
+ fileBlockHeadersByCode.clear();
+ }
+
+ /**
+ * This method adds a helper instance to the helpers' map.
+ *
+ * @param
+ * the type of the helper
+ * @param clazz
+ * helper's class definition
+ * @param helper
+ * the helper instance
+ */
+ public void putHelper(Class clazz, AbstractBlenderHelper helper) {
+ helpers.put(clazz.getSimpleName(), helper);
+ }
+
+ @SuppressWarnings("unchecked")
+ public 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.
+ *
+ * @param oldMemoryAddress
+ * the address of the feature
+ * @param featureName
+ * the name of the feature
+ * @param structure
+ * the filled structure of the feature
+ * @param feature
+ * the feature we want to store
+ */
+ public void addLoadedFeatures(Long oldMemoryAddress, String featureName, Structure structure, Object feature) {
+ if (oldMemoryAddress == null || structure == null || feature == null) {
+ throw new IllegalArgumentException("One of the given arguments is null!");
+ }
+ Object[] storedData = new Object[] { structure, feature };
+ loadedFeatures.put(oldMemoryAddress, storedData);
+ if (featureName != null) {
+ loadedFeaturesByName.put(featureName, storedData);
+ }
+ }
+
+ /**
+ * This method returns the feature of a given memory address. If the feature
+ * is not yet loaded then null is returned.
+ *
+ * @param oldMemoryAddress
+ * the address of the feature
+ * @param loadedFeatureDataType
+ * 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
+ */
+ public Object getLoadedFeature(Long oldMemoryAddress, LoadedFeatureDataType loadedFeatureDataType) {
+ Object[] result = loadedFeatures.get(oldMemoryAddress);
+ if (result != null) {
+ return result[loadedFeatureDataType.getIndex()];
+ }
+ return null;
+ }
+
+ /**
+ * This method returns the feature of a given name. If the feature is not
+ * yet loaded then null is returned.
+ *
+ * @param featureName
+ * the name of the feature
+ * @param loadedFeatureDataType
+ * 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
+ */
+ public Object getLoadedFeature(String featureName, LoadedFeatureDataType loadedFeatureDataType) {
+ Object[] result = loadedFeaturesByName.get(featureName);
+ if (result != null) {
+ return result[loadedFeatureDataType.getIndex()];
+ }
+ return null;
+ }
+
+ /**
+ * This method clears the saved features stored in the features map.
+ */
+ public void clearLoadedFeatures() {
+ loadedFeatures.clear();
+ }
+
+ /**
+ * This method adds the structure to the parent stack.
+ *
+ * @param parent
+ * the structure to be added to the stack
+ */
+ public void pushParent(Structure parent) {
+ parentStack.push(parent);
+ }
+
+ /**
+ * This method removes the structure from the top of the parent's stack.
+ *
+ * @return the structure that was removed from the stack
+ */
+ public Structure popParent() {
+ try {
+ return parentStack.pop();
+ } catch (EmptyStackException e) {
+ return null;
+ }
+ }
+
+ /**
+ * 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
+ */
+ public Structure peekParent() {
+ try {
+ return parentStack.peek();
+ } catch (EmptyStackException e) {
+ return null;
+ }
+ }
+
+ /**
+ * This method adds new ipo curve for the feature.
+ *
+ * @param ownerOMA
+ * the OMA of blender feature that owns the ipo
+ * @param ipo
+ * the ipo to be added
+ */
+ public void addIpo(Long ownerOMA, Ipo ipo) {
+ loadedIpos.put(ownerOMA, ipo);
+ }
+
+ /**
+ * This method removes the ipo curve from the feature.
+ *
+ * @param ownerOma
+ * the OMA of blender feature that owns the ipo
+ */
+ public Ipo removeIpo(Long ownerOma) {
+ return loadedIpos.remove(ownerOma);
+ }
+
+ /**
+ * This method returns the ipo curve of the feature.
+ *
+ * @param ownerOMA
+ * the OMA of blender feature that owns the ipo
+ */
+ public Ipo getIpo(Long ownerOMA) {
+ return loadedIpos.get(ownerOMA);
+ }
+
+ /**
+ * This method adds a new modifier to the list.
+ *
+ * @param ownerOMA
+ * the owner's old memory address
+ * @param modifier
+ * the object's modifier
+ */
+ public void addModifier(Long ownerOMA, Modifier modifier) {
+ List objectModifiers = this.modifiers.get(ownerOMA);
+ if (objectModifiers == null) {
+ objectModifiers = new ArrayList();
+ this.modifiers.put(ownerOMA, objectModifiers);
+ }
+ objectModifiers.add(modifier);
+ }
+
+ /**
+ * 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.
+ *
+ * @param objectOMA
+ * object's old memory address
+ * @param type
+ * the type of the modifier
+ * @return the list of object's modifiers
+ */
+ public List getModifiers(Long objectOMA, String type) {
+ List result = new ArrayList();
+ List readModifiers = modifiers.get(objectOMA);
+ if (readModifiers != null && readModifiers.size() > 0) {
+ for (Modifier modifier : readModifiers) {
+ if (type == null || type.isEmpty() || modifier.getType().equals(type)) {
+ result.add(modifier);
+ }
+ }
+ }
+ return result;
+ }
+
+ /**
+ * This method adds a new modifier to the list.
+ *
+ * @param ownerOMA
+ * the owner's old memory address
+ * @param constraints
+ * the object's constraints
+ */
+ public void addConstraints(Long ownerOMA, List constraints) {
+ List objectConstraints = this.constraints.get(ownerOMA);
+ if (objectConstraints == null) {
+ objectConstraints = new ArrayList();
+ this.constraints.put(ownerOMA, objectConstraints);
+ }
+ objectConstraints.addAll(constraints);
+ }
+
+ /**
+ * This method returns constraints for the object specified by its old
+ * memory address. If no modifiers are found - null is returned.
+ *
+ * @param objectOMA
+ * object's old memory address
+ * @return the list of object's modifiers or null
+ */
+ public List getConstraints(Long objectOMA) {
+ return objectOMA == null ? null : constraints.get(objectOMA);
+ }
+
+ /**
+ * @return all available constraints
+ */
+ public List getAllConstraints() {
+ List result = new ArrayList();
+ for (Entry> entry : constraints.entrySet()) {
+ result.addAll(entry.getValue());
+ }
+ return result;
+ }
+
+ /**
+ * This method sets the anim data for the specified OMA of its owner.
+ *
+ * @param ownerOMA
+ * the owner's old memory address
+ * @param animData
+ * the animation data for the feature specified by ownerOMA
+ */
+ public void setAnimData(Long ownerOMA, AnimData animData) {
+ this.animData.put(ownerOMA, animData);
+ }
+
+ /**
+ * This method returns the animation data for the specified owner.
+ *
+ * @param ownerOMA
+ * the old memory address of the animation data owner
+ * @return the animation data or null if none exists
+ */
+ public AnimData getAnimData(Long ownerOMA) {
+ return this.animData.get(ownerOMA);
+ }
+
+ /**
+ * This method sets the skeleton for the specified OMA of its owner.
+ *
+ * @param skeletonOMA
+ * the skeleton's old memory address
+ * @param skeleton
+ * the skeleton specified by the given OMA
+ */
+ public void setSkeleton(Long skeletonOMA, Skeleton skeleton) {
+ this.skeletons.put(skeletonOMA, skeleton);
+ }
+
+ /**
+ * This method returns the skeleton for the specified OMA of its owner.
+ *
+ * @param skeletonOMA
+ * the skeleton's old memory address
+ * @return the skeleton specified by the given OMA
+ */
+ public Skeleton getSkeleton(Long skeletonOMA) {
+ return this.skeletons.get(skeletonOMA);
+ }
+
+ /**
+ * This method sets the mesh context for the given mesh old memory address.
+ * If the context is already set it will be replaced.
+ *
+ * @param meshOMA
+ * the mesh's old memory address
+ * @param meshContext
+ * the mesh's context
+ */
+ public void setMeshContext(Long meshOMA, MeshContext meshContext) {
+ this.meshContexts.put(meshOMA, meshContext);
+ }
+
+ /**
+ * This method returns the mesh context for the given mesh old memory
+ * address. If no context exists then null is returned.
+ *
+ * @param meshOMA
+ * the mesh's old memory address
+ * @return mesh's context
+ */
+ public MeshContext getMeshContext(Long meshOMA) {
+ return this.meshContexts.get(meshOMA);
+ }
+
+ /**
+ * This method sets the bone context for the given bone old memory address.
+ * If the context is already set it will be replaced.
+ *
+ * @param boneOMA
+ * the bone's old memory address
+ * @param boneContext
+ * the bones's context
+ */
+ public void setBoneContext(Long boneOMA, BoneContext boneContext) {
+ this.boneContexts.put(boneOMA, boneContext);
+ }
+
+ /**
+ * This method returns the bone context for the given bone old memory
+ * address. If no context exists then null is returned.
+ *
+ * @param boneOMA
+ * the bone's old memory address
+ * @return bone's context
+ */
+ public BoneContext getBoneContext(Long boneOMA) {
+ return boneContexts.get(boneOMA);
+ }
+
+ /**
+ * Returns bone by given name.
+ *
+ * @param name
+ * the name of the bone
+ * @return found bone or null if none bone of a given name exists
+ */
+ public BoneContext getBoneByName(String name) {
+ for (Entry entry : boneContexts.entrySet()) {
+ Bone bone = entry.getValue().getBone();
+ if (bone != null && name.equals(bone.getName())) {
+ return entry.getValue();
+ }
+ }
+ return null;
+ }
+
+ /**
+ * This metod returns the default material.
+ *
+ * @return the default material
+ */
+ public synchronized Material getDefaultMaterial() {
+ if (blenderKey.getDefaultMaterial() == null) {
+ Material defaultMaterial = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ defaultMaterial.setColor("Color", ColorRGBA.DarkGray);
+ blenderKey.setDefaultMaterial(defaultMaterial);
+ }
+ return blenderKey.getDefaultMaterial();
+ }
+
+ /**
+ * Clears all sotred resources and closes the blender input stream.
+ */
+ public void dispose() {
+ LOGGER.fine("Disposing blender context resources.");
+ inputStream.forceClose();
+ loadedFeatures.clear();
+ loadedFeaturesByName.clear();
+ parentStack.clear();
+ loadedIpos.clear();
+ modifiers.clear();
+ constraints.clear();
+ animData.clear();
+ skeletons.clear();
+ meshContexts.clear();
+ boneContexts.clear();
+ helpers.clear();
+ }
+
+ /**
+ * This enum defines what loaded data type user wants to retreive. It can be
+ * either filled structure or already converted data.
+ *
+ * @author Marcin Roguski
+ */
+ public static enum LoadedFeatureDataType {
+
+ LOADED_STRUCTURE(0), LOADED_FEATURE(1);
+ private int index;
+
+ private LoadedFeatureDataType(int index) {
+ this.index = index;
+ }
+
+ public int getIndex() {
+ return index;
+ }
+ }
}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/BlenderLoader.java b/engine/src/blender/com/jme3/scene/plugins/blender/BlenderLoader.java
index 146ecfcf3..c7f5d19be 100644
--- a/engine/src/blender/com/jme3/scene/plugins/blender/BlenderLoader.java
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/BlenderLoader.java
@@ -70,158 +70,158 @@ import com.jme3.scene.plugins.blender.textures.TextureHelper;
*/
public class BlenderLoader extends AbstractBlenderLoader {
- private static final Logger LOGGER = Logger.getLogger(BlenderLoader.class.getName());
-
- /** The blocks read from the file. */
- protected List blocks;
-
- public Spatial load(AssetInfo assetInfo) throws IOException {
- try {
- this.setup(assetInfo);
-
- List sceneBlocks = new ArrayList();
- BlenderKey blenderKey = blenderContext.getBlenderKey();
- LoadingResults loadingResults = blenderKey.prepareLoadingResults();
- WorldData worldData = null;// a set of data used in different scene aspects
- for (FileBlockHeader block : blocks) {
- switch (block.getCode()) {
- case FileBlockHeader.BLOCK_OB00:// Object
- Object object = this.toObject(block.getStructure(blenderContext));
- if(object instanceof LightNode && (blenderKey.getFeaturesToLoad() & FeaturesToLoad.LIGHTS) != 0) {
- loadingResults.addLight((LightNode) object);
- } else if (object instanceof CameraNode && (blenderKey.getFeaturesToLoad() & FeaturesToLoad.CAMERAS) != 0) {
- loadingResults.addCamera((CameraNode) object);
- } else if (object instanceof Node && (blenderKey.getFeaturesToLoad() & FeaturesToLoad.OBJECTS) != 0) {
- LOGGER.log(Level.FINE, "{0}: {1}--> {2}", new Object[] { ((Node) object).getName(), ((Node) object).getLocalTranslation().toString(), ((Node) object).getParent() == null ? "null" : ((Node) object).getParent().getName() });
- if (this.isRootObject(loadingResults, (Node)object)) {
- loadingResults.addObject((Node) object);
- }
- }
- break;
-// case FileBlockHeader.BLOCK_MA00:// Material
-// if (blenderKey.isLoadUnlinkedAssets() && (blenderKey.getFeaturesToLoad() & FeaturesToLoad.MATERIALS) != 0) {
-// loadingResults.addMaterial(this.toMaterial(block.getStructure(blenderContext)));
-// }
-// break;
- case FileBlockHeader.BLOCK_SC00:// Scene
- if ((blenderKey.getFeaturesToLoad() & FeaturesToLoad.SCENES) != 0) {
- sceneBlocks.add(block);
- }
- break;
- case FileBlockHeader.BLOCK_WO00:// World
- if (blenderKey.isLoadUnlinkedAssets() && worldData == null) {// onlu one world data is used
- Structure worldStructure = block.getStructure(blenderContext);
- String worldName = worldStructure.getName();
- if (blenderKey.getUsedWorld() == null || blenderKey.getUsedWorld().equals(worldName)) {
- worldData = this.toWorldData(worldStructure);
- if ((blenderKey.getFeaturesToLoad() & FeaturesToLoad.LIGHTS) != 0) {
- loadingResults.addLight(worldData.getAmbientLight());
- }
- }
- }
- break;
- }
- }
-
- //bake constraints after everything is loaded
- ConstraintHelper constraintHelper = blenderContext.getHelper(ConstraintHelper.class);
- constraintHelper.bakeConstraints(blenderContext);
-
- //load the scene at the very end so that the root nodes have no parent during loading or constraints applying
- for(FileBlockHeader sceneBlock : sceneBlocks) {
- loadingResults.addScene(this.toScene(sceneBlock.getStructure(blenderContext)));
- }
-
- blenderContext.dispose();
- return loadingResults;
- } catch (BlenderFileException e) {
- LOGGER.log(Level.SEVERE, e.getMessage(), e);
- }
- return null;
- }
-
- /**
- * This method indicates if the given spatial is a root object. It means it
- * has no parent or is directly attached to one of the already loaded scene
- * nodes.
- *
- * @param loadingResults
- * loading results containing the scene nodes
- * @param spatial
- * spatial object
- * @return true if the given spatial is a root object and
- * false otherwise
- */
- protected boolean isRootObject(LoadingResults loadingResults, Spatial spatial) {
- if(spatial.getParent() == null) {
- return true;
- }
- for(Node scene : loadingResults.getScenes()) {
- if(spatial.getParent().equals(scene)) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * This method sets up the loader.
- * @param assetInfo
- * the asset info
- * @throws BlenderFileException
- * an exception is throw when something wrong happens with blender file
- */
- protected void setup(AssetInfo assetInfo) throws BlenderFileException {
- // 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
- BlenderInputStream inputStream = new BlenderInputStream(assetInfo.openStream());
-
- // reading blocks
- blocks = new ArrayList();
- FileBlockHeader fileBlock;
- blenderContext = new BlenderContext();
- blenderContext.setBlenderVersion(inputStream.getVersionNumber());
- blenderContext.setAssetManager(assetInfo.getManager());
- blenderContext.setInputStream(inputStream);
- blenderContext.setBlenderKey(blenderKey);
-
- // creating helpers
- blenderContext.putHelper(ArmatureHelper.class, new ArmatureHelper(inputStream.getVersionNumber(), blenderKey.isFixUpAxis()));
- blenderContext.putHelper(TextureHelper.class, new TextureHelper(inputStream.getVersionNumber(), blenderKey.isFixUpAxis()));
- blenderContext.putHelper(MeshHelper.class, new MeshHelper(inputStream.getVersionNumber(), blenderKey.isFixUpAxis()));
- blenderContext.putHelper(ObjectHelper.class, new ObjectHelper(inputStream.getVersionNumber(), blenderKey.isFixUpAxis()));
- blenderContext.putHelper(CurvesHelper.class, new CurvesHelper(inputStream.getVersionNumber(), blenderKey.isFixUpAxis()));
- blenderContext.putHelper(LightHelper.class, new LightHelper(inputStream.getVersionNumber(), blenderKey.isFixUpAxis()));
- blenderContext.putHelper(CameraHelper.class, new CameraHelper(inputStream.getVersionNumber(), blenderKey.isFixUpAxis()));
- blenderContext.putHelper(ModifierHelper.class, new ModifierHelper(inputStream.getVersionNumber(), blenderKey.isFixUpAxis()));
- blenderContext.putHelper(MaterialHelper.class, new MaterialHelper(inputStream.getVersionNumber(), blenderKey.isFixUpAxis()));
- blenderContext.putHelper(ConstraintHelper.class, new ConstraintHelper(inputStream.getVersionNumber(), blenderContext, blenderKey.isFixUpAxis()));
- blenderContext.putHelper(IpoHelper.class, new IpoHelper(inputStream.getVersionNumber(), blenderKey.isFixUpAxis()));
- blenderContext.putHelper(ParticlesHelper.class, new ParticlesHelper(inputStream.getVersionNumber(), blenderKey.isFixUpAxis()));
-
- // reading the blocks (dna block is automatically saved in the blender context when found)
- FileBlockHeader sceneFileBlock = null;
- do {
- fileBlock = new FileBlockHeader(inputStream, blenderContext);
- if (!fileBlock.isDnaBlock()) {
- blocks.add(fileBlock);
- // save the scene's file block
- if (fileBlock.getCode() == FileBlockHeader.BLOCK_SC00) {
- sceneFileBlock = fileBlock;
- }
- }
- } while (!fileBlock.isLastBlock());
- if (sceneFileBlock != null) {
- blenderContext.setSceneStructure(sceneFileBlock.getStructure(blenderContext));
+ private static final Logger LOGGER = Logger.getLogger(BlenderLoader.class.getName());
+
+ /** The blocks read from the file. */
+ protected List blocks;
+
+ public Spatial load(AssetInfo assetInfo) throws IOException {
+ try {
+ this.setup(assetInfo);
+
+ List sceneBlocks = new ArrayList();
+ BlenderKey blenderKey = blenderContext.getBlenderKey();
+ LoadingResults loadingResults = blenderKey.prepareLoadingResults();
+ WorldData worldData = null;// a set of data used in different scene aspects
+ for (FileBlockHeader block : blocks) {
+ switch (block.getCode()) {
+ case FileBlockHeader.BLOCK_OB00:// Object
+ Object object = this.toObject(block.getStructure(blenderContext));
+ if (object instanceof LightNode && (blenderKey.getFeaturesToLoad() & FeaturesToLoad.LIGHTS) != 0) {
+ loadingResults.addLight((LightNode) object);
+ } else if (object instanceof CameraNode && (blenderKey.getFeaturesToLoad() & FeaturesToLoad.CAMERAS) != 0) {
+ loadingResults.addCamera((CameraNode) object);
+ } else if (object instanceof Node && (blenderKey.getFeaturesToLoad() & FeaturesToLoad.OBJECTS) != 0) {
+ LOGGER.log(Level.FINE, "{0}: {1}--> {2}", new Object[] { ((Node) object).getName(), ((Node) object).getLocalTranslation().toString(), ((Node) object).getParent() == null ? "null" : ((Node) object).getParent().getName() });
+ if (this.isRootObject(loadingResults, (Node) object)) {
+ loadingResults.addObject((Node) object);
+ }
+ }
+ break;
+ // case FileBlockHeader.BLOCK_MA00:// Material
+ // if (blenderKey.isLoadUnlinkedAssets() && (blenderKey.getFeaturesToLoad() & FeaturesToLoad.MATERIALS) != 0) {
+ // loadingResults.addMaterial(this.toMaterial(block.getStructure(blenderContext)));
+ // }
+ // break;
+ case FileBlockHeader.BLOCK_SC00:// Scene
+ if ((blenderKey.getFeaturesToLoad() & FeaturesToLoad.SCENES) != 0) {
+ sceneBlocks.add(block);
+ }
+ break;
+ case FileBlockHeader.BLOCK_WO00:// World
+ if (blenderKey.isLoadUnlinkedAssets() && worldData == null) {// onlu one world data is used
+ Structure worldStructure = block.getStructure(blenderContext);
+ String worldName = worldStructure.getName();
+ if (blenderKey.getUsedWorld() == null || blenderKey.getUsedWorld().equals(worldName)) {
+ worldData = this.toWorldData(worldStructure);
+ if ((blenderKey.getFeaturesToLoad() & FeaturesToLoad.LIGHTS) != 0) {
+ loadingResults.addLight(worldData.getAmbientLight());
+ }
+ }
+ }
+ break;
+ }
+ }
+
+ // bake constraints after everything is loaded
+ ConstraintHelper constraintHelper = blenderContext.getHelper(ConstraintHelper.class);
+ constraintHelper.bakeConstraints(blenderContext);
+
+ // load the scene at the very end so that the root nodes have no parent during loading or constraints applying
+ for (FileBlockHeader sceneBlock : sceneBlocks) {
+ loadingResults.addScene(this.toScene(sceneBlock.getStructure(blenderContext)));
+ }
+
+ blenderContext.dispose();
+ return loadingResults;
+ } catch (BlenderFileException e) {
+ LOGGER.log(Level.SEVERE, e.getMessage(), e);
+ }
+ return null;
+ }
+
+ /**
+ * This method indicates if the given spatial is a root object. It means it
+ * has no parent or is directly attached to one of the already loaded scene
+ * nodes.
+ *
+ * @param loadingResults
+ * loading results containing the scene nodes
+ * @param spatial
+ * spatial object
+ * @return true if the given spatial is a root object and
+ * false otherwise
+ */
+ protected boolean isRootObject(LoadingResults loadingResults, Spatial spatial) {
+ if (spatial.getParent() == null) {
+ return true;
+ }
+ for (Node scene : loadingResults.getScenes()) {
+ if (spatial.getParent().equals(scene)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * This method sets up the loader.
+ * @param assetInfo
+ * the asset info
+ * @throws BlenderFileException
+ * an exception is throw when something wrong happens with blender file
+ */
+ protected void setup(AssetInfo assetInfo) throws BlenderFileException {
+ // 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
+ BlenderInputStream inputStream = new BlenderInputStream(assetInfo.openStream());
+
+ // reading blocks
+ blocks = new ArrayList();
+ FileBlockHeader fileBlock;
+ blenderContext = new BlenderContext();
+ blenderContext.setBlenderVersion(inputStream.getVersionNumber());
+ blenderContext.setAssetManager(assetInfo.getManager());
+ blenderContext.setInputStream(inputStream);
+ blenderContext.setBlenderKey(blenderKey);
+
+ // creating helpers
+ blenderContext.putHelper(ArmatureHelper.class, new ArmatureHelper(inputStream.getVersionNumber(), blenderKey.isFixUpAxis()));
+ blenderContext.putHelper(TextureHelper.class, new TextureHelper(inputStream.getVersionNumber(), blenderKey.isFixUpAxis()));
+ blenderContext.putHelper(MeshHelper.class, new MeshHelper(inputStream.getVersionNumber(), blenderKey.isFixUpAxis()));
+ blenderContext.putHelper(ObjectHelper.class, new ObjectHelper(inputStream.getVersionNumber(), blenderKey.isFixUpAxis()));
+ blenderContext.putHelper(CurvesHelper.class, new CurvesHelper(inputStream.getVersionNumber(), blenderKey.isFixUpAxis()));
+ blenderContext.putHelper(LightHelper.class, new LightHelper(inputStream.getVersionNumber(), blenderKey.isFixUpAxis()));
+ blenderContext.putHelper(CameraHelper.class, new CameraHelper(inputStream.getVersionNumber(), blenderKey.isFixUpAxis()));
+ blenderContext.putHelper(ModifierHelper.class, new ModifierHelper(inputStream.getVersionNumber(), blenderKey.isFixUpAxis()));
+ blenderContext.putHelper(MaterialHelper.class, new MaterialHelper(inputStream.getVersionNumber(), blenderKey.isFixUpAxis()));
+ blenderContext.putHelper(ConstraintHelper.class, new ConstraintHelper(inputStream.getVersionNumber(), blenderContext, blenderKey.isFixUpAxis()));
+ blenderContext.putHelper(IpoHelper.class, new IpoHelper(inputStream.getVersionNumber(), blenderKey.isFixUpAxis()));
+ blenderContext.putHelper(ParticlesHelper.class, new ParticlesHelper(inputStream.getVersionNumber(), blenderKey.isFixUpAxis()));
+
+ // reading the blocks (dna block is automatically saved in the blender context when found)
+ FileBlockHeader sceneFileBlock = null;
+ do {
+ fileBlock = new FileBlockHeader(inputStream, blenderContext);
+ if (!fileBlock.isDnaBlock()) {
+ blocks.add(fileBlock);
+ // save the scene's file block
+ if (fileBlock.getCode() == FileBlockHeader.BLOCK_SC00) {
+ sceneFileBlock = fileBlock;
+ }
+ }
+ } while (!fileBlock.isLastBlock());
+ if (sceneFileBlock != null) {
+ blenderContext.setSceneStructure(sceneFileBlock.getStructure(blenderContext));
}
- }
+ }
}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/BlenderModelLoader.java b/engine/src/blender/com/jme3/scene/plugins/blender/BlenderModelLoader.java
index e2f7a21b0..4deaf4e29 100644
--- a/engine/src/blender/com/jme3/scene/plugins/blender/BlenderModelLoader.java
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/BlenderModelLoader.java
@@ -57,30 +57,30 @@ public class BlenderModelLoader extends BlenderLoader {
public Spatial load(AssetInfo assetInfo) throws IOException {
try {
this.setup(assetInfo);
-
+
BlenderKey blenderKey = blenderContext.getBlenderKey();
Node modelRoot = new Node(blenderKey.getName());
-
+
for (FileBlockHeader block : blocks) {
if (block.getCode() == FileBlockHeader.BLOCK_OB00) {
Object object = this.toObject(block.getStructure(blenderContext));
-
- if(object instanceof LightNode && (blenderKey.getFeaturesToLoad() & FeaturesToLoad.LIGHTS) != 0) {
- modelRoot.addLight(((LightNode)object).getLight());
- modelRoot.attachChild((LightNode)object);
- } else if (object instanceof Node && (blenderKey.getFeaturesToLoad() & FeaturesToLoad.OBJECTS) != 0) {
- LOGGER.log(Level.FINE, "{0}: {1}--> {2}", new Object[] { ((Node) object).getName(), ((Node) object).getLocalTranslation().toString(), ((Node) object).getParent() == null ? "null" : ((Node) object).getParent().getName() });
- if (((Node) object).getParent() == null) {
- modelRoot.attachChild((Node)object);
+
+ if (object instanceof LightNode && (blenderKey.getFeaturesToLoad() & FeaturesToLoad.LIGHTS) != 0) {
+ modelRoot.addLight(((LightNode) object).getLight());
+ modelRoot.attachChild((LightNode) object);
+ } else if (object instanceof Node && (blenderKey.getFeaturesToLoad() & FeaturesToLoad.OBJECTS) != 0) {
+ LOGGER.log(Level.FINE, "{0}: {1}--> {2}", new Object[] { ((Node) object).getName(), ((Node) object).getLocalTranslation().toString(), ((Node) object).getParent() == null ? "null" : ((Node) object).getParent().getName() });
+ if (((Node) object).getParent() == null) {
+ modelRoot.attachChild((Node) object);
}
- }
+ }
}
}
-
- //bake constraints after everything is loaded
- ConstraintHelper constraintHelper = blenderContext.getHelper(ConstraintHelper.class);
- constraintHelper.bakeConstraints(blenderContext);
-
+
+ // bake constraints after everything is loaded
+ ConstraintHelper constraintHelper = blenderContext.getHelper(ConstraintHelper.class);
+ constraintHelper.bakeConstraints(blenderContext);
+
blenderContext.dispose();
return modelRoot;
} catch (BlenderFileException e) {
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/animations/ArmatureHelper.java b/engine/src/blender/com/jme3/scene/plugins/blender/animations/ArmatureHelper.java
index 79db7fa49..a0112aba6 100644
--- a/engine/src/blender/com/jme3/scene/plugins/blender/animations/ArmatureHelper.java
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/animations/ArmatureHelper.java
@@ -56,215 +56,215 @@ import java.util.logging.Logger;
* @author Marcin Roguski (Kaelthas)
*/
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());
- public static final String ARMETURE_NODE_MARKER = "armeture-node";
-
- /** A map of bones and their old memory addresses. */
- private Map bonesOMAs = new HashMap();
+ public static final String ARMETURE_NODE_MARKER = "armeture-node";
- /**
- * 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
- * @param fixUpAxis
- * a variable that indicates if the Y asxis is the UP axis or not
- */
- public ArmatureHelper(String blenderVersion, boolean fixUpAxis) {
- super(blenderVersion, fixUpAxis);
- }
+ /** A map of bones and their old memory addresses. */
+ private Map bonesOMAs = new HashMap();
- /**
- * This method builds the object's bones structure.
- *
- * @param boneStructure
- * the structure containing the bones' data
- * @param parent
- * the parent bone
- * @param result
- * the list where the newly created bone will be added
- * @param bonesPoseChannels
- * a map of bones poses channels
- * @param blenderContext
- * the blender context
- * @throws BlenderFileException
- * an exception is thrown when there is problem with the blender
- * file
- */
- public void buildBones(Long armatureObjectOMA, Structure boneStructure, Bone parent, List result, Matrix4f arbt, final Map bonesPoseChannels, BlenderContext blenderContext) throws BlenderFileException {
- BoneContext bc = new BoneContext(armatureObjectOMA, boneStructure, arbt, bonesPoseChannels, blenderContext);
- bc.buildBone(result, bonesOMAs, blenderContext);
- }
+ /**
+ * 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
+ * @param fixUpAxis
+ * a variable that indicates if the Y asxis is the UP axis or not
+ */
+ public ArmatureHelper(String blenderVersion, boolean fixUpAxis) {
+ super(blenderVersion, fixUpAxis);
+ }
- /**
- * This method returns the old memory address of a bone. If the bone does
- * not exist in the blend file - zero is returned.
- *
- * @param bone
- * the bone whose old memory address we seek
- * @return the old memory address of the given bone
- */
- public Long getBoneOMA(Bone bone) {
- Long result = bonesOMAs.get(bone);
- if (result == null) {
- result = Long.valueOf(0);
- }
- return result;
- }
+ /**
+ * This method builds the object's bones structure.
+ *
+ * @param boneStructure
+ * the structure containing the bones' data
+ * @param parent
+ * the parent bone
+ * @param result
+ * the list where the newly created bone will be added
+ * @param bonesPoseChannels
+ * a map of bones poses channels
+ * @param blenderContext
+ * the blender context
+ * @throws BlenderFileException
+ * an exception is thrown when there is problem with the blender
+ * file
+ */
+ public void buildBones(Long armatureObjectOMA, Structure boneStructure, Bone parent, List result, Matrix4f arbt, final Map bonesPoseChannels, BlenderContext blenderContext) throws BlenderFileException {
+ BoneContext bc = new BoneContext(armatureObjectOMA, boneStructure, arbt, bonesPoseChannels, blenderContext);
+ bc.buildBone(result, bonesOMAs, blenderContext);
+ }
- /**
- * 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.
- *
- * @param defBaseStructure
- * a bPose structure of the object
- * @return bone group-to-index map
- * @throws BlenderFileException
- * this exception is thrown when the blender file is somehow
- * corrupted
- */
- public Map getGroupToBoneIndexMap(Structure defBaseStructure, Skeleton skeleton, BlenderContext blenderContext) throws BlenderFileException {
- Map result = null;
- if (skeleton.getBoneCount() != 0) {
- result = new HashMap();
- List deformGroups = defBaseStructure.evaluateListBase(blenderContext);// bDeformGroup
- int groupIndex = 0;
- for (Structure deformGroup : deformGroups) {
- String deformGroupName = deformGroup.getFieldValue("name").toString();
- int boneIndex = this.getBoneIndex(skeleton, deformGroupName);
- if (boneIndex >= 0) {
- result.put(groupIndex, boneIndex);
- }
- ++groupIndex;
- }
- }
- return result;
- }
+ /**
+ * This method returns the old memory address of a bone. If the bone does
+ * not exist in the blend file - zero is returned.
+ *
+ * @param bone
+ * the bone whose old memory address we seek
+ * @return the old memory address of the given bone
+ */
+ public Long getBoneOMA(Bone bone) {
+ Long result = bonesOMAs.get(bone);
+ if (result == null) {
+ result = Long.valueOf(0);
+ }
+ return result;
+ }
- @Override
- public boolean shouldBeLoaded(Structure structure, BlenderContext blenderContext) {
- return true;
- }
+ /**
+ * 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.
+ *
+ * @param defBaseStructure
+ * a bPose structure of the object
+ * @return bone group-to-index map
+ * @throws BlenderFileException
+ * this exception is thrown when the blender file is somehow
+ * corrupted
+ */
+ public Map getGroupToBoneIndexMap(Structure defBaseStructure, Skeleton skeleton, BlenderContext blenderContext) throws BlenderFileException {
+ Map result = null;
+ if (skeleton.getBoneCount() != 0) {
+ result = new HashMap();
+ List deformGroups = defBaseStructure.evaluateListBase(blenderContext);// bDeformGroup
+ int groupIndex = 0;
+ for (Structure deformGroup : deformGroups) {
+ String deformGroupName = deformGroup.getFieldValue("name").toString();
+ int boneIndex = this.getBoneIndex(skeleton, deformGroupName);
+ if (boneIndex >= 0) {
+ result.put(groupIndex, boneIndex);
+ }
+ ++groupIndex;
+ }
+ }
+ return result;
+ }
- /**
- * This method retuns the bone tracks for animation.
- *
- * @param actionStructure
- * the structure containing the tracks
- * @param blenderContext
- * the blender context
- * @return a list of tracks for the specified animation
- * @throws BlenderFileException
- * an exception is thrown when there are problems with the blend
- * file
- */
- public BoneTrack[] getTracks(Structure actionStructure, Skeleton skeleton, BlenderContext blenderContext) throws BlenderFileException {
- if (blenderVersion < 250) {
- return this.getTracks249(actionStructure, skeleton, blenderContext);
- } else {
- return this.getTracks250(actionStructure, skeleton, blenderContext);
- }
- }
+ @Override
+ public boolean shouldBeLoaded(Structure structure, BlenderContext blenderContext) {
+ return true;
+ }
- /**
- * This method retuns the bone tracks for animation for blender version 2.50
- * and higher.
- *
- * @param actionStructure
- * the structure containing the tracks
- * @param blenderContext
- * the blender context
- * @return a list of tracks for the specified animation
- * @throws BlenderFileException
- * an exception is thrown when there are problems with the blend
- * file
- */
- private BoneTrack[] getTracks250(Structure actionStructure, Skeleton skeleton, BlenderContext blenderContext) throws BlenderFileException {
- LOGGER.log(Level.FINE, "Getting tracks!");
- IpoHelper ipoHelper = blenderContext.getHelper(IpoHelper.class);
- int fps = blenderContext.getBlenderKey().getFps();
- Structure groups = (Structure) actionStructure.getFieldValue("groups");
- List actionGroups = groups.evaluateListBase(blenderContext);// bActionGroup
- List tracks = new ArrayList();
- for (Structure actionGroup : actionGroups) {
- String name = actionGroup.getFieldValue("name").toString();
- int boneIndex = this.getBoneIndex(skeleton, name);
- if (boneIndex >= 0) {
- List channels = ((Structure) actionGroup.getFieldValue("channels")).evaluateListBase(blenderContext);
- BezierCurve[] bezierCurves = new BezierCurve[channels.size()];
- int channelCounter = 0;
- for (Structure c : channels) {
- int type = ipoHelper.getCurveType(c, blenderContext);
- Pointer pBezTriple = (Pointer) c.getFieldValue("bezt");
- List bezTriples = pBezTriple.fetchData(blenderContext.getInputStream());
- bezierCurves[channelCounter++] = new BezierCurve(type, bezTriples, 2);
- }
+ /**
+ * This method retuns the bone tracks for animation.
+ *
+ * @param actionStructure
+ * the structure containing the tracks
+ * @param blenderContext
+ * the blender context
+ * @return a list of tracks for the specified animation
+ * @throws BlenderFileException
+ * an exception is thrown when there are problems with the blend
+ * file
+ */
+ public BoneTrack[] getTracks(Structure actionStructure, Skeleton skeleton, BlenderContext blenderContext) throws BlenderFileException {
+ if (blenderVersion < 250) {
+ return this.getTracks249(actionStructure, skeleton, blenderContext);
+ } else {
+ return this.getTracks250(actionStructure, skeleton, blenderContext);
+ }
+ }
- Bone bone = skeleton.getBone(boneIndex);
- Ipo ipo = new Ipo(bezierCurves, fixUpAxis, blenderContext.getBlenderVersion());
- tracks.add((BoneTrack) ipo.calculateTrack(boneIndex, bone.getLocalRotation(), 0, ipo.getLastFrame(), fps, false));
- }
- }
- return tracks.toArray(new BoneTrack[tracks.size()]);
- }
+ /**
+ * This method retuns the bone tracks for animation for blender version 2.50
+ * and higher.
+ *
+ * @param actionStructure
+ * the structure containing the tracks
+ * @param blenderContext
+ * the blender context
+ * @return a list of tracks for the specified animation
+ * @throws BlenderFileException
+ * an exception is thrown when there are problems with the blend
+ * file
+ */
+ private BoneTrack[] getTracks250(Structure actionStructure, Skeleton skeleton, BlenderContext blenderContext) throws BlenderFileException {
+ LOGGER.log(Level.FINE, "Getting tracks!");
+ IpoHelper ipoHelper = blenderContext.getHelper(IpoHelper.class);
+ int fps = blenderContext.getBlenderKey().getFps();
+ Structure groups = (Structure) actionStructure.getFieldValue("groups");
+ List actionGroups = groups.evaluateListBase(blenderContext);// bActionGroup
+ List tracks = new ArrayList();
+ for (Structure actionGroup : actionGroups) {
+ String name = actionGroup.getFieldValue("name").toString();
+ int boneIndex = this.getBoneIndex(skeleton, name);
+ if (boneIndex >= 0) {
+ List channels = ((Structure) actionGroup.getFieldValue("channels")).evaluateListBase(blenderContext);
+ BezierCurve[] bezierCurves = new BezierCurve[channels.size()];
+ int channelCounter = 0;
+ for (Structure c : channels) {
+ int type = ipoHelper.getCurveType(c, blenderContext);
+ Pointer pBezTriple = (Pointer) c.getFieldValue("bezt");
+ List bezTriples = pBezTriple.fetchData(blenderContext.getInputStream());
+ bezierCurves[channelCounter++] = new BezierCurve(type, bezTriples, 2);
+ }
- /**
- * This method retuns the bone tracks for animation for blender version 2.49
- * (and probably several lower versions too).
- *
- * @param actionStructure
- * the structure containing the tracks
- * @param blenderContext
- * the blender context
- * @return a list of tracks for the specified animation
- * @throws BlenderFileException
- * an exception is thrown when there are problems with the blend
- * file
- */
- private BoneTrack[] getTracks249(Structure actionStructure, Skeleton skeleton, BlenderContext blenderContext) throws BlenderFileException {
- LOGGER.log(Level.FINE, "Getting tracks!");
- IpoHelper ipoHelper = blenderContext.getHelper(IpoHelper.class);
- int fps = blenderContext.getBlenderKey().getFps();
- Structure chanbase = (Structure) actionStructure.getFieldValue("chanbase");
- List actionChannels = chanbase.evaluateListBase(blenderContext);// bActionChannel
- List tracks = new ArrayList();
- for (Structure bActionChannel : actionChannels) {
- String name = bActionChannel.getFieldValue("name").toString();
- int boneIndex = this.getBoneIndex(skeleton, name);
- if (boneIndex >= 0) {
- Pointer p = (Pointer) bActionChannel.getFieldValue("ipo");
- if (!p.isNull()) {
- Structure ipoStructure = p.fetchData(blenderContext.getInputStream()).get(0);
-
- Bone bone = skeleton.getBone(boneIndex);
- Ipo ipo = ipoHelper.fromIpoStructure(ipoStructure, blenderContext);
- if(ipo != null) {
- tracks.add((BoneTrack) ipo.calculateTrack(boneIndex, bone.getLocalRotation(), 0, ipo.getLastFrame(), fps, false));
- }
- }
- }
- }
- return tracks.toArray(new BoneTrack[tracks.size()]);
- }
+ Bone bone = skeleton.getBone(boneIndex);
+ Ipo ipo = new Ipo(bezierCurves, fixUpAxis, blenderContext.getBlenderVersion());
+ tracks.add((BoneTrack) ipo.calculateTrack(boneIndex, bone.getLocalRotation(), 0, ipo.getLastFrame(), fps, false));
+ }
+ }
+ return tracks.toArray(new BoneTrack[tracks.size()]);
+ }
- /**
- * This method returns the index of the bone in the given skeleton.
- *
- * @param skeleton
- * the skeleton
- * @param boneName
- * the name of the bone
- * @return the index of the bone
- */
- private int getBoneIndex(Skeleton skeleton, String boneName) {
- int result = -1;
- for (int i = 0; i < skeleton.getBoneCount() && result == -1; ++i) {
- if (boneName.equals(skeleton.getBone(i).getName())) {
- result = i;
- }
- }
- return result;
- }
+ /**
+ * This method retuns the bone tracks for animation for blender version 2.49
+ * (and probably several lower versions too).
+ *
+ * @param actionStructure
+ * the structure containing the tracks
+ * @param blenderContext
+ * the blender context
+ * @return a list of tracks for the specified animation
+ * @throws BlenderFileException
+ * an exception is thrown when there are problems with the blend
+ * file
+ */
+ private BoneTrack[] getTracks249(Structure actionStructure, Skeleton skeleton, BlenderContext blenderContext) throws BlenderFileException {
+ LOGGER.log(Level.FINE, "Getting tracks!");
+ IpoHelper ipoHelper = blenderContext.getHelper(IpoHelper.class);
+ int fps = blenderContext.getBlenderKey().getFps();
+ Structure chanbase = (Structure) actionStructure.getFieldValue("chanbase");
+ List actionChannels = chanbase.evaluateListBase(blenderContext);// bActionChannel
+ List tracks = new ArrayList();
+ for (Structure bActionChannel : actionChannels) {
+ String name = bActionChannel.getFieldValue("name").toString();
+ int boneIndex = this.getBoneIndex(skeleton, name);
+ if (boneIndex >= 0) {
+ Pointer p = (Pointer) bActionChannel.getFieldValue("ipo");
+ if (!p.isNull()) {
+ Structure ipoStructure = p.fetchData(blenderContext.getInputStream()).get(0);
+
+ Bone bone = skeleton.getBone(boneIndex);
+ Ipo ipo = ipoHelper.fromIpoStructure(ipoStructure, blenderContext);
+ if (ipo != null) {
+ tracks.add((BoneTrack) ipo.calculateTrack(boneIndex, bone.getLocalRotation(), 0, ipo.getLastFrame(), fps, false));
+ }
+ }
+ }
+ }
+ return tracks.toArray(new BoneTrack[tracks.size()]);
+ }
+
+ /**
+ * This method returns the index of the bone in the given skeleton.
+ *
+ * @param skeleton
+ * the skeleton
+ * @param boneName
+ * the name of the bone
+ * @return the index of the bone
+ */
+ private int getBoneIndex(Skeleton skeleton, String boneName) {
+ int result = -1;
+ for (int i = 0; i < skeleton.getBoneCount() && result == -1; ++i) {
+ if (boneName.equals(skeleton.getBone(i).getName())) {
+ result = i;
+ }
+ }
+ return result;
+ }
}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/animations/BoneContext.java b/engine/src/blender/com/jme3/scene/plugins/blender/animations/BoneContext.java
index 9635abfdd..b426d48e5 100644
--- a/engine/src/blender/com/jme3/scene/plugins/blender/animations/BoneContext.java
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/animations/BoneContext.java
@@ -21,215 +21,215 @@ import com.jme3.scene.plugins.blender.objects.ObjectHelper;
* @author Marcin Roguski (Kaelthas)
*/
public class BoneContext {
- /** The OMA of the bone's armature object. */
- private Long armatureObjectOMA;
- /** The structure of the bone. */
- private Structure boneStructure;
- /** Bone's pose channel structure. */
- private Structure poseChannel;
- /** Bone's name. */
- private String boneName;
- /** This variable indicates if the Y axis should be the UP axis. */
- private boolean fixUpAxis;
- /** The bone's armature matrix. */
- private Matrix4f armatureMatrix;
- /** The parent context. */
- private BoneContext parent;
- /** The children of this context. */
- private List children = new ArrayList();
- /** Created bone (available after calling 'buildBone' method). */
- private Bone bone;
- /** Bone's pose transform (available after calling 'buildBone' method). */
- private Transform poseTransform = new Transform();
- /** The bone's rest matrix. */
- private Matrix4f restMatrix;
- /** Bone's total inverse transformation. */
- private Matrix4f inverseTotalTransformation;
- /** Bone's parent inverse matrix. */
- private Matrix4f inverseParentMatrix;
- /** The length of the bone. */
- private float length;
-
- /**
- * Constructor. Creates the basic set of bone's data.
- *
- * @param armatureObjectOMA
- * the OMA of the bone's armature object
- * @param boneStructure
- * the bone's structure
- * @param objectToArmatureMatrix
- * object-to-armature transformation matrix
- * @param bonesPoseChannels
- * a map of pose channels for each bone OMA
- * @param blenderContext
- * the blender context
- * @throws BlenderFileException
- * an exception is thrown when problem with blender data reading
- * occurs
- */
- public BoneContext(Long armatureObjectOMA, Structure boneStructure, Matrix4f objectToArmatureMatrix, final Map bonesPoseChannels, BlenderContext blenderContext) throws BlenderFileException {
- this(boneStructure, armatureObjectOMA, null, objectToArmatureMatrix, bonesPoseChannels, blenderContext);
- }
-
- /**
- * Constructor. Creates the basic set of bone's data.
- *
- * @param boneStructure
- * the bone's structure
- * @param armatureObjectOMA
- * the OMA of the bone's armature object
- * @param parent
- * bone's parent (null if the bone is the root bone)
- * @param objectToArmatureMatrix
- * object-to-armature transformation matrix
- * @param bonesPoseChannels
- * a map of pose channels for each bone OMA
- * @param blenderContext
- * the blender context
- * @throws BlenderFileException
- * an exception is thrown when problem with blender data reading
- * occurs
- */
- private BoneContext(Structure boneStructure, Long armatureObjectOMA, BoneContext parent, Matrix4f objectToArmatureMatrix, final Map bonesPoseChannels, BlenderContext blenderContext) throws BlenderFileException {
- this.parent = parent;
- this.boneStructure = boneStructure;
- this.armatureObjectOMA = armatureObjectOMA;
- boneName = boneStructure.getFieldValue("name").toString();
- length = ((Number)boneStructure.getFieldValue("length")).floatValue();
- ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class);
- armatureMatrix = objectHelper.getMatrix(boneStructure, "arm_mat", true);
-
- fixUpAxis = blenderContext.getBlenderKey().isFixUpAxis();
- this.computeRestMatrix(objectToArmatureMatrix);
- List childbase = ((Structure) boneStructure.getFieldValue("childbase")).evaluateListBase(blenderContext);
- for (Structure child : childbase) {
- this.children.add(new BoneContext(child, armatureObjectOMA, this, objectToArmatureMatrix, bonesPoseChannels, blenderContext));
- }
-
- poseChannel = bonesPoseChannels.get(boneStructure.getOldMemoryAddress());
-
- blenderContext.setBoneContext(boneStructure.getOldMemoryAddress(), this);
- }
-
- /**
- * This method computes the rest matrix for the bone.
- *
- * @param objectToArmatureMatrix
- * object-to-armature transformation matrix
- */
- private void computeRestMatrix(Matrix4f objectToArmatureMatrix) {
- if (parent != null) {
- inverseParentMatrix = parent.inverseTotalTransformation.clone();
- } else if (fixUpAxis) {
- inverseParentMatrix = objectToArmatureMatrix.clone();
- } else {
- inverseParentMatrix = Matrix4f.IDENTITY.clone();
- }
-
- restMatrix = armatureMatrix.clone();
- inverseTotalTransformation = restMatrix.invert();
-
- restMatrix = inverseParentMatrix.mult(restMatrix);
-
- for (BoneContext child : this.children) {
- child.computeRestMatrix(objectToArmatureMatrix);
- }
- }
-
- /**
- * This method computes the pose transform for the bone.
- */
- @SuppressWarnings("unchecked")
- private void computePoseTransform() {
- DynamicArray loc = (DynamicArray) poseChannel.getFieldValue("loc");
- DynamicArray size = (DynamicArray) poseChannel.getFieldValue("size");
- DynamicArray quat = (DynamicArray) poseChannel.getFieldValue("quat");
- if (fixUpAxis) {
- poseTransform.setTranslation(loc.get(0).floatValue(), -loc.get(2).floatValue(), loc.get(1).floatValue());
- poseTransform.setRotation(new Quaternion(quat.get(1).floatValue(), quat.get(3).floatValue(), -quat.get(2).floatValue(), quat.get(0).floatValue()));
- poseTransform.setScale(size.get(0).floatValue(), size.get(2).floatValue(), size.get(1).floatValue());
- } else {
- poseTransform.setTranslation(loc.get(0).floatValue(), loc.get(1).floatValue(), loc.get(2).floatValue());
- poseTransform.setRotation(new Quaternion(quat.get(0).floatValue(), quat.get(1).floatValue(), quat.get(2).floatValue(), quat.get(3).floatValue()));
- poseTransform.setScale(size.get(0).floatValue(), size.get(1).floatValue(), size.get(2).floatValue());
- }
-
- Transform localTransform = new Transform(bone.getLocalPosition(), bone.getLocalRotation());
- localTransform.setScale(bone.getLocalScale());
- localTransform.getTranslation().addLocal(poseTransform.getTranslation());
- localTransform.getRotation().multLocal(poseTransform.getRotation());
- localTransform.getScale().multLocal(poseTransform.getScale());
-
- poseTransform.set(localTransform);
- }
-
- /**
- * This method builds the bone. It recursively builds the bone's children.
- *
- * @param bones
- * a list of bones where the newly created bone will be added
- * @param boneOMAs
- * the map between bone and its old memory address
- * @param blenderContext
- * the blender context
- * @return newly created bone
- */
- public Bone buildBone(List bones, Map boneOMAs, BlenderContext blenderContext) {
- Long boneOMA = boneStructure.getOldMemoryAddress();
- bone = new Bone(boneName);
- bones.add(bone);
- boneOMAs.put(bone, boneOMA);
- blenderContext.addLoadedFeatures(boneOMA, boneName, boneStructure, bone);
-
- Matrix4f pose = this.restMatrix.clone();
- ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class);
-
- Vector3f poseLocation = pose.toTranslationVector();
- Quaternion rotation = pose.toRotationQuat();
- Vector3f scale = objectHelper.getScale(pose);
-
- bone.setBindTransforms(poseLocation, rotation, scale);
- for (BoneContext child : children) {
- bone.addChild(child.buildBone(bones, boneOMAs, blenderContext));
- }
-
- this.computePoseTransform();
-
- return bone;
- }
-
- /**
- * @return bone's pose transformation
- */
- public Transform getPoseTransform() {
- return poseTransform;
- }
-
- /**
- * @return built bone (available after calling 'buildBone' method)
- */
- public Bone getBone() {
- return bone;
- }
-
- /**
- * @return the old memory address of the bone
- */
- public Long getBoneOma() {
- return boneStructure.getOldMemoryAddress();
- }
-
- /**
- * @return the length of the bone
- */
- public float getLength() {
- return length;
- }
-
- /**
- * @return OMA of the bone's armature object
- */
- public Long getArmatureObjectOMA() {
- return armatureObjectOMA;
- }
+ /** The OMA of the bone's armature object. */
+ private Long armatureObjectOMA;
+ /** The structure of the bone. */
+ private Structure boneStructure;
+ /** Bone's pose channel structure. */
+ private Structure poseChannel;
+ /** Bone's name. */
+ private String boneName;
+ /** This variable indicates if the Y axis should be the UP axis. */
+ private boolean fixUpAxis;
+ /** The bone's armature matrix. */
+ private Matrix4f armatureMatrix;
+ /** The parent context. */
+ private BoneContext parent;
+ /** The children of this context. */
+ private List children = new ArrayList();
+ /** Created bone (available after calling 'buildBone' method). */
+ private Bone bone;
+ /** Bone's pose transform (available after calling 'buildBone' method). */
+ private Transform poseTransform = new Transform();
+ /** The bone's rest matrix. */
+ private Matrix4f restMatrix;
+ /** Bone's total inverse transformation. */
+ private Matrix4f inverseTotalTransformation;
+ /** Bone's parent inverse matrix. */
+ private Matrix4f inverseParentMatrix;
+ /** The length of the bone. */
+ private float length;
+
+ /**
+ * Constructor. Creates the basic set of bone's data.
+ *
+ * @param armatureObjectOMA
+ * the OMA of the bone's armature object
+ * @param boneStructure
+ * the bone's structure
+ * @param objectToArmatureMatrix
+ * object-to-armature transformation matrix
+ * @param bonesPoseChannels
+ * a map of pose channels for each bone OMA
+ * @param blenderContext
+ * the blender context
+ * @throws BlenderFileException
+ * an exception is thrown when problem with blender data reading
+ * occurs
+ */
+ public BoneContext(Long armatureObjectOMA, Structure boneStructure, Matrix4f objectToArmatureMatrix, final Map bonesPoseChannels, BlenderContext blenderContext) throws BlenderFileException {
+ this(boneStructure, armatureObjectOMA, null, objectToArmatureMatrix, bonesPoseChannels, blenderContext);
+ }
+
+ /**
+ * Constructor. Creates the basic set of bone's data.
+ *
+ * @param boneStructure
+ * the bone's structure
+ * @param armatureObjectOMA
+ * the OMA of the bone's armature object
+ * @param parent
+ * bone's parent (null if the bone is the root bone)
+ * @param objectToArmatureMatrix
+ * object-to-armature transformation matrix
+ * @param bonesPoseChannels
+ * a map of pose channels for each bone OMA
+ * @param blenderContext
+ * the blender context
+ * @throws BlenderFileException
+ * an exception is thrown when problem with blender data reading
+ * occurs
+ */
+ private BoneContext(Structure boneStructure, Long armatureObjectOMA, BoneContext parent, Matrix4f objectToArmatureMatrix, final Map bonesPoseChannels, BlenderContext blenderContext) throws BlenderFileException {
+ this.parent = parent;
+ this.boneStructure = boneStructure;
+ this.armatureObjectOMA = armatureObjectOMA;
+ boneName = boneStructure.getFieldValue("name").toString();
+ length = ((Number) boneStructure.getFieldValue("length")).floatValue();
+ ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class);
+ armatureMatrix = objectHelper.getMatrix(boneStructure, "arm_mat", true);
+
+ fixUpAxis = blenderContext.getBlenderKey().isFixUpAxis();
+ this.computeRestMatrix(objectToArmatureMatrix);
+ List childbase = ((Structure) boneStructure.getFieldValue("childbase")).evaluateListBase(blenderContext);
+ for (Structure child : childbase) {
+ this.children.add(new BoneContext(child, armatureObjectOMA, this, objectToArmatureMatrix, bonesPoseChannels, blenderContext));
+ }
+
+ poseChannel = bonesPoseChannels.get(boneStructure.getOldMemoryAddress());
+
+ blenderContext.setBoneContext(boneStructure.getOldMemoryAddress(), this);
+ }
+
+ /**
+ * This method computes the rest matrix for the bone.
+ *
+ * @param objectToArmatureMatrix
+ * object-to-armature transformation matrix
+ */
+ private void computeRestMatrix(Matrix4f objectToArmatureMatrix) {
+ if (parent != null) {
+ inverseParentMatrix = parent.inverseTotalTransformation.clone();
+ } else if (fixUpAxis) {
+ inverseParentMatrix = objectToArmatureMatrix.clone();
+ } else {
+ inverseParentMatrix = Matrix4f.IDENTITY.clone();
+ }
+
+ restMatrix = armatureMatrix.clone();
+ inverseTotalTransformation = restMatrix.invert();
+
+ restMatrix = inverseParentMatrix.mult(restMatrix);
+
+ for (BoneContext child : this.children) {
+ child.computeRestMatrix(objectToArmatureMatrix);
+ }
+ }
+
+ /**
+ * This method computes the pose transform for the bone.
+ */
+ @SuppressWarnings("unchecked")
+ private void computePoseTransform() {
+ DynamicArray loc = (DynamicArray) poseChannel.getFieldValue("loc");
+ DynamicArray size = (DynamicArray) poseChannel.getFieldValue("size");
+ DynamicArray quat = (DynamicArray) poseChannel.getFieldValue("quat");
+ if (fixUpAxis) {
+ poseTransform.setTranslation(loc.get(0).floatValue(), -loc.get(2).floatValue(), loc.get(1).floatValue());
+ poseTransform.setRotation(new Quaternion(quat.get(1).floatValue(), quat.get(3).floatValue(), -quat.get(2).floatValue(), quat.get(0).floatValue()));
+ poseTransform.setScale(size.get(0).floatValue(), size.get(2).floatValue(), size.get(1).floatValue());
+ } else {
+ poseTransform.setTranslation(loc.get(0).floatValue(), loc.get(1).floatValue(), loc.get(2).floatValue());
+ poseTransform.setRotation(new Quaternion(quat.get(0).floatValue(), quat.get(1).floatValue(), quat.get(2).floatValue(), quat.get(3).floatValue()));
+ poseTransform.setScale(size.get(0).floatValue(), size.get(1).floatValue(), size.get(2).floatValue());
+ }
+
+ Transform localTransform = new Transform(bone.getLocalPosition(), bone.getLocalRotation());
+ localTransform.setScale(bone.getLocalScale());
+ localTransform.getTranslation().addLocal(poseTransform.getTranslation());
+ localTransform.getRotation().multLocal(poseTransform.getRotation());
+ localTransform.getScale().multLocal(poseTransform.getScale());
+
+ poseTransform.set(localTransform);
+ }
+
+ /**
+ * This method builds the bone. It recursively builds the bone's children.
+ *
+ * @param bones
+ * a list of bones where the newly created bone will be added
+ * @param boneOMAs
+ * the map between bone and its old memory address
+ * @param blenderContext
+ * the blender context
+ * @return newly created bone
+ */
+ public Bone buildBone(List bones, Map boneOMAs, BlenderContext blenderContext) {
+ Long boneOMA = boneStructure.getOldMemoryAddress();
+ bone = new Bone(boneName);
+ bones.add(bone);
+ boneOMAs.put(bone, boneOMA);
+ blenderContext.addLoadedFeatures(boneOMA, boneName, boneStructure, bone);
+
+ Matrix4f pose = this.restMatrix.clone();
+ ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class);
+
+ Vector3f poseLocation = pose.toTranslationVector();
+ Quaternion rotation = pose.toRotationQuat();
+ Vector3f scale = objectHelper.getScale(pose);
+
+ bone.setBindTransforms(poseLocation, rotation, scale);
+ for (BoneContext child : children) {
+ bone.addChild(child.buildBone(bones, boneOMAs, blenderContext));
+ }
+
+ this.computePoseTransform();
+
+ return bone;
+ }
+
+ /**
+ * @return bone's pose transformation
+ */
+ public Transform getPoseTransform() {
+ return poseTransform;
+ }
+
+ /**
+ * @return built bone (available after calling 'buildBone' method)
+ */
+ public Bone getBone() {
+ return bone;
+ }
+
+ /**
+ * @return the old memory address of the bone
+ */
+ public Long getBoneOma() {
+ return boneStructure.getOldMemoryAddress();
+ }
+
+ /**
+ * @return the length of the bone
+ */
+ public float getLength() {
+ return length;
+ }
+
+ /**
+ * @return OMA of the bone's armature object
+ */
+ public Long getArmatureObjectOMA() {
+ return armatureObjectOMA;
+ }
}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/animations/CalculationBone.java b/engine/src/blender/com/jme3/scene/plugins/blender/animations/CalculationBone.java
index 90994ccf1..a8386203f 100644
--- a/engine/src/blender/com/jme3/scene/plugins/blender/animations/CalculationBone.java
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/animations/CalculationBone.java
@@ -13,116 +13,116 @@ import java.util.Arrays;
* @author Marcin Roguski (Kaelthas)
*/
public class CalculationBone extends Node {
- private Bone bone;
- /** The bone's tracks. Will be altered at the end of calculation process. */
- private BoneTrack track;
- /** The starting position of the bone. */
- private Vector3f startTranslation;
- /** The starting rotation of the bone. */
- private Quaternion startRotation;
- /** The starting scale of the bone. */
- private Vector3f startScale;
- private Vector3f[] translations;
- private Quaternion[] rotations;
- private Vector3f[] scales;
+ private Bone bone;
+ /** The bone's tracks. Will be altered at the end of calculation process. */
+ private BoneTrack track;
+ /** The starting position of the bone. */
+ private Vector3f startTranslation;
+ /** The starting rotation of the bone. */
+ private Quaternion startRotation;
+ /** The starting scale of the bone. */
+ private Vector3f startScale;
+ private Vector3f[] translations;
+ private Quaternion[] rotations;
+ private Vector3f[] scales;
- public CalculationBone(Bone bone, int boneFramesCount) {
- this.bone = bone;
- this.startRotation = bone.getModelSpaceRotation().clone();
- this.startTranslation = bone.getModelSpacePosition().clone();
- this.startScale = bone.getModelSpaceScale().clone();
- this.reset();
- if(boneFramesCount > 0) {
- this.translations = new Vector3f[boneFramesCount];
- this.rotations = new Quaternion[boneFramesCount];
- this.scales = new Vector3f[boneFramesCount];
-
- Arrays.fill(this.translations, 0, boneFramesCount, this.startTranslation);
- Arrays.fill(this.rotations, 0, boneFramesCount, this.startRotation);
- Arrays.fill(this.scales, 0, boneFramesCount, this.startScale);
- }
- }
-
- /**
- * Constructor. Stores the track, starting transformation and sets the transformation to the starting positions.
- * @param bone
- * the bone this class will imitate
- * @param track
- * the bone's tracks
- */
- public CalculationBone(Bone bone, BoneTrack track) {
- this(bone, 0);
- this.track = track;
- this.translations = track.getTranslations();
- this.rotations = track.getRotations();
- this.scales = track.getScales();
- }
+ public CalculationBone(Bone bone, int boneFramesCount) {
+ this.bone = bone;
+ this.startRotation = bone.getModelSpaceRotation().clone();
+ this.startTranslation = bone.getModelSpacePosition().clone();
+ this.startScale = bone.getModelSpaceScale().clone();
+ this.reset();
+ if (boneFramesCount > 0) {
+ this.translations = new Vector3f[boneFramesCount];
+ this.rotations = new Quaternion[boneFramesCount];
+ this.scales = new Vector3f[boneFramesCount];
- public int getBoneFramesCount() {
- return this.translations==null ? 0 : this.translations.length;
- }
-
- /**
- * This method returns the end point of the bone. If the bone has parent it is calculated from the start point
- * of parent to the start point of this bone. If the bone doesn't have a parent the end location is considered
- * to be 1 point up along Y axis (scale is applied if set to != 1.0);
- * @return the end point of this bone
- */
- //TODO: set to Z axis if user defined it this way
- public Vector3f getEndPoint() {
- if (this.getParent() == null) {
- return new Vector3f(0, this.getLocalScale().y, 0);
- } else {
- Node parent = this.getParent();
- return parent.getWorldTranslation().subtract(this.getWorldTranslation()).multLocal(this.getWorldScale());
- }
- }
+ Arrays.fill(this.translations, 0, boneFramesCount, this.startTranslation);
+ Arrays.fill(this.rotations, 0, boneFramesCount, this.startRotation);
+ Arrays.fill(this.scales, 0, boneFramesCount, this.startScale);
+ }
+ }
- /**
- * This method resets the calculation bone to the starting position.
- */
- public void reset() {
- this.setLocalTranslation(startTranslation);
- this.setLocalRotation(startRotation);
- this.setLocalScale(startScale);
- }
+ /**
+ * Constructor. Stores the track, starting transformation and sets the transformation to the starting positions.
+ * @param bone
+ * the bone this class will imitate
+ * @param track
+ * the bone's tracks
+ */
+ public CalculationBone(Bone bone, BoneTrack track) {
+ this(bone, 0);
+ this.track = track;
+ this.translations = track.getTranslations();
+ this.rotations = track.getRotations();
+ this.scales = track.getScales();
+ }
- @Override
- public int attachChild(Spatial child) {
- if (this.getChildren() != null && this.getChildren().size() > 1) {
- throw new IllegalStateException(this.getClass().getName() + " class instance can only have one child!");
- }
- return super.attachChild(child);
- }
+ public int getBoneFramesCount() {
+ return this.translations == null ? 0 : this.translations.length;
+ }
- public Spatial rotate(Quaternion rot, int frame) {
- Spatial spatial = super.rotate(rot);
- this.updateWorldTransforms();
- if (this.getChildren() != null && this.getChildren().size() > 0) {
- CalculationBone child = (CalculationBone) this.getChild(0);
- child.updateWorldTransforms();
- }
- rotations[frame].set(this.getLocalRotation());
- translations[frame].set(this.getLocalTranslation());
- if (scales != null) {
- scales[frame].set(this.getLocalScale());
- }
- return spatial;
- }
+ /**
+ * This method returns the end point of the bone. If the bone has parent it is calculated from the start point
+ * of parent to the start point of this bone. If the bone doesn't have a parent the end location is considered
+ * to be 1 point up along Y axis (scale is applied if set to != 1.0);
+ * @return the end point of this bone
+ */
+ // TODO: set to Z axis if user defined it this way
+ public Vector3f getEndPoint() {
+ if (this.getParent() == null) {
+ return new Vector3f(0, this.getLocalScale().y, 0);
+ } else {
+ Node parent = this.getParent();
+ return parent.getWorldTranslation().subtract(this.getWorldTranslation()).multLocal(this.getWorldScale());
+ }
+ }
- public void applyCalculatedTracks() {
- if(track != null) {
- track.setKeyframes(track.getTimes(), translations, rotations, scales);
- } else {
- bone.setUserControl(true);
- bone.setUserTransforms(translations[0], rotations[0], scales[0]);
- bone.setUserControl(false);
- bone.updateWorldVectors();
- }
- }
+ /**
+ * This method resets the calculation bone to the starting position.
+ */
+ public void reset() {
+ this.setLocalTranslation(startTranslation);
+ this.setLocalRotation(startRotation);
+ this.setLocalScale(startScale);
+ }
- @Override
- public String toString() {
- return bone.getName() + ": " + this.getLocalRotation() + " " + this.getLocalTranslation();
- }
+ @Override
+ public int attachChild(Spatial child) {
+ if (this.getChildren() != null && this.getChildren().size() > 1) {
+ throw new IllegalStateException(this.getClass().getName() + " class instance can only have one child!");
+ }
+ return super.attachChild(child);
+ }
+
+ public Spatial rotate(Quaternion rot, int frame) {
+ Spatial spatial = super.rotate(rot);
+ this.updateWorldTransforms();
+ if (this.getChildren() != null && this.getChildren().size() > 0) {
+ CalculationBone child = (CalculationBone) this.getChild(0);
+ child.updateWorldTransforms();
+ }
+ rotations[frame].set(this.getLocalRotation());
+ translations[frame].set(this.getLocalTranslation());
+ if (scales != null) {
+ scales[frame].set(this.getLocalScale());
+ }
+ return spatial;
+ }
+
+ public void applyCalculatedTracks() {
+ if (track != null) {
+ track.setKeyframes(track.getTimes(), translations, rotations, scales);
+ } else {
+ bone.setUserControl(true);
+ bone.setUserTransforms(translations[0], rotations[0], scales[0]);
+ bone.setUserControl(false);
+ bone.updateWorldVectors();
+ }
+ }
+
+ @Override
+ public String toString() {
+ return bone.getName() + ": " + this.getLocalRotation() + " " + this.getLocalTranslation();
+ }
}
\ No newline at end of file
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/animations/Ipo.java b/engine/src/blender/com/jme3/scene/plugins/blender/animations/Ipo.java
index a969922b6..790269262 100644
--- a/engine/src/blender/com/jme3/scene/plugins/blender/animations/Ipo.java
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/animations/Ipo.java
@@ -18,234 +18,234 @@ import com.jme3.scene.plugins.blender.curves.BezierCurve;
* @author Marcin Roguski
*/
public class Ipo {
- private static final Logger LOGGER = Logger.getLogger(Ipo.class.getName());
-
- 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. */
- private BezierCurve[] bezierCurves;
- /** Each ipo contains one bone track. */
- private Track calculatedTrack;
- /** This variable indicates if the Y asxis is the UP axis or not. */
- protected boolean fixUpAxis;
- /** Depending on the blender version rotations are stored in degrees or radians so we need to know the version that is used. */
- protected final int blenderVersion;
-
- /**
- * Constructor. Stores the bezier curves.
- *
- * @param bezierCurves
- * a table of bezier curves
- * @param fixUpAxis
- * indicates if the Y is the up axis or not
- * @param blenderVersion
- * the blender version that is currently used
- */
- public Ipo(BezierCurve[] bezierCurves, boolean fixUpAxis, int blenderVersion) {
- this.bezierCurves = bezierCurves;
- this.fixUpAxis = fixUpAxis;
- this.blenderVersion = blenderVersion;
- }
-
- /**
- * This method calculates the ipo value for the first curve.
- *
- * @param frame
- * the frame for which the value is calculated
- * @return calculated ipo value
- */
- public float calculateValue(int frame) {
- 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 curves amount. Alway chech the
- * amount of curves before calling this method.
- *
- * @param frame
- * the frame for which the value is calculated
- * @param curveIndex
- * the index of the curve
- * @return calculated ipo value
- */
- public float calculateValue(int frame, int curveIndex) {
- return bezierCurves[curveIndex].evaluate(frame, BezierCurve.Y_VALUE);
- }
-
- /**
- * This method returns the curves amount.
- *
- * @return the curves amount
- */
- public int getCurvesAmount() {
- return bezierCurves.length;
- }
-
- /**
- * 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
- */
- public int getLastFrame() {
- int result = 1;
- for (int i = 0; i < bezierCurves.length; ++i) {
- int tempResult = bezierCurves[i].getLastFrame();
- if (tempResult > result) {
- result = tempResult;
- }
- }
- return result;
- }
-
- /**
- * This method calculates the value of the curves as a bone track between
- * the specified frames.
- *
- * @param targetIndex
- * the index of the target for which the method calculates the
- * tracks IMPORTANT! Aet to -1 (or any negative number) if you
- * want to load spatial animation.
- * @param localQuaternionRotation
- * the local rotation of the object/bone that will be animated by
- * the track
- * @param startFrame
- * the firs frame of tracks (inclusive)
- * @param stopFrame
- * the last frame of the tracks (inclusive)
- * @param fps
- * frame rate (frames per second)
- * @param spatialTrack
- * this flag indicates if the track belongs to a spatial or to a
- * bone; the diference is important because it appears that bones
- * in blender have the same type of coordinate system (Y as UP)
- * as jme while other features have different one (Z is UP)
- * @return bone track for the specified bone
- */
- public Track calculateTrack(int targetIndex, Quaternion localQuaternionRotation, int startFrame, int stopFrame, int fps, boolean spatialTrack) {
- if (calculatedTrack == null) {
- // preparing data for track
- int framesAmount = stopFrame - startFrame;
- float timeBetweenFrames = 1.0f / fps;
-
- float[] times = new float[framesAmount + 1];
- Vector3f[] translations = new Vector3f[framesAmount + 1];
- float[] translation = new float[3];
- Quaternion[] rotations = new Quaternion[framesAmount + 1];
- float[] quaternionRotation = new float[] { 0, 0, 0, 1 };
- float[] objectRotation = new float[3];
- Vector3f[] scales = new Vector3f[framesAmount + 1];
- float[] scale = new float[] { 1.0f, 1.0f, 1.0f };
- float degreeToRadiansFactor = 1;
- if(blenderVersion < 250) {//in blender earlier than 2.50 the values are stored in degrees
- degreeToRadiansFactor *= FastMath.DEG_TO_RAD * 10;// the values in blender are divided by 10, so we need to mult it here
- }
-
- // calculating track data
- for (int frame = startFrame; frame <= stopFrame; ++frame) {
- int index = frame - startFrame;
- times[index] = index * timeBetweenFrames;//start + (frame - 1) * timeBetweenFrames;
- for (int j = 0; j < bezierCurves.length; ++j) {
- double value = bezierCurves[j].evaluate(frame, BezierCurve.Y_VALUE);
- switch (bezierCurves[j].getType()) {
- // LOCATION
- case AC_LOC_X:
- translation[0] = (float) value;
- break;
- case AC_LOC_Y:
- if (fixUpAxis) {
- translation[2] = (float) -value;
- } else {
- translation[1] = (float) value;
- }
- break;
- case AC_LOC_Z:
- translation[fixUpAxis ? 1 : 2] = (float) value;
- break;
-
- // ROTATION (used with object animation)
- // the value here is in degrees divided by 10 (so in
- // example: 9 = PI/2)
- case OB_ROT_X:
- objectRotation[0] = (float) value * degreeToRadiansFactor;
- break;
- case OB_ROT_Y:
- if (fixUpAxis) {
- objectRotation[2] = (float) -value * degreeToRadiansFactor;
- } else {
- objectRotation[1] = (float) value * degreeToRadiansFactor;
- }
- break;
- case OB_ROT_Z:
- objectRotation[fixUpAxis ? 1 : 2] = (float) value * degreeToRadiansFactor;
- break;
-
- // SIZE
- case AC_SIZE_X:
- scale[0] = (float) value;
- break;
- case AC_SIZE_Y:
- if (fixUpAxis) {
- scale[2] = (float) value;
- } else {
- scale[1] = (float) value;
- }
- break;
- case AC_SIZE_Z:
- scale[fixUpAxis ? 1 : 2] = (float) value;
- break;
-
- // QUATERNION ROTATION (used with bone animation), dunno
- // why but here we shouldn't check the
- // spatialTrack flag value
- case AC_QUAT_W:
- quaternionRotation[3] = (float) value;
- break;
- case AC_QUAT_X:
- quaternionRotation[0] = (float) value;
- break;
- case AC_QUAT_Y:
- if (fixUpAxis) {
- quaternionRotation[2] = -(float) value;
- } else {
- quaternionRotation[1] = (float) value;
- }
- break;
- case AC_QUAT_Z:
- if (fixUpAxis) {
- quaternionRotation[1] = (float) value;
- } else {
- quaternionRotation[2] = (float) value;
- }
- break;
- default:
- LOGGER.warning("Unknown ipo curve type: " + bezierCurves[j].getType());
- }
- }
- translations[index] = localQuaternionRotation.multLocal(new Vector3f(translation[0], translation[1], translation[2]));
- rotations[index] = spatialTrack ? new Quaternion().fromAngles(objectRotation) : new Quaternion(quaternionRotation[0], quaternionRotation[1], quaternionRotation[2], quaternionRotation[3]);
- scales[index] = new Vector3f(scale[0], scale[1], scale[2]);
- }
- if (spatialTrack) {
- calculatedTrack = new SpatialTrack(times, translations, rotations, scales);
- } else {
- calculatedTrack = new BoneTrack(targetIndex, times, translations, rotations, scales);
- }
- }
- return calculatedTrack;
- }
+ private static final Logger LOGGER = Logger.getLogger(Ipo.class.getName());
+
+ 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. */
+ private BezierCurve[] bezierCurves;
+ /** Each ipo contains one bone track. */
+ private Track calculatedTrack;
+ /** This variable indicates if the Y asxis is the UP axis or not. */
+ protected boolean fixUpAxis;
+ /** Depending on the blender version rotations are stored in degrees or radians so we need to know the version that is used. */
+ protected final int blenderVersion;
+
+ /**
+ * Constructor. Stores the bezier curves.
+ *
+ * @param bezierCurves
+ * a table of bezier curves
+ * @param fixUpAxis
+ * indicates if the Y is the up axis or not
+ * @param blenderVersion
+ * the blender version that is currently used
+ */
+ public Ipo(BezierCurve[] bezierCurves, boolean fixUpAxis, int blenderVersion) {
+ this.bezierCurves = bezierCurves;
+ this.fixUpAxis = fixUpAxis;
+ this.blenderVersion = blenderVersion;
+ }
+
+ /**
+ * This method calculates the ipo value for the first curve.
+ *
+ * @param frame
+ * the frame for which the value is calculated
+ * @return calculated ipo value
+ */
+ public float calculateValue(int frame) {
+ 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 curves amount. Alway chech the
+ * amount of curves before calling this method.
+ *
+ * @param frame
+ * the frame for which the value is calculated
+ * @param curveIndex
+ * the index of the curve
+ * @return calculated ipo value
+ */
+ public float calculateValue(int frame, int curveIndex) {
+ return bezierCurves[curveIndex].evaluate(frame, BezierCurve.Y_VALUE);
+ }
+
+ /**
+ * This method returns the curves amount.
+ *
+ * @return the curves amount
+ */
+ public int getCurvesAmount() {
+ return bezierCurves.length;
+ }
+
+ /**
+ * 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
+ */
+ public int getLastFrame() {
+ int result = 1;
+ for (int i = 0; i < bezierCurves.length; ++i) {
+ int tempResult = bezierCurves[i].getLastFrame();
+ if (tempResult > result) {
+ result = tempResult;
+ }
+ }
+ return result;
+ }
+
+ /**
+ * This method calculates the value of the curves as a bone track between
+ * the specified frames.
+ *
+ * @param targetIndex
+ * the index of the target for which the method calculates the
+ * tracks IMPORTANT! Aet to -1 (or any negative number) if you
+ * want to load spatial animation.
+ * @param localQuaternionRotation
+ * the local rotation of the object/bone that will be animated by
+ * the track
+ * @param startFrame
+ * the firs frame of tracks (inclusive)
+ * @param stopFrame
+ * the last frame of the tracks (inclusive)
+ * @param fps
+ * frame rate (frames per second)
+ * @param spatialTrack
+ * this flag indicates if the track belongs to a spatial or to a
+ * bone; the diference is important because it appears that bones
+ * in blender have the same type of coordinate system (Y as UP)
+ * as jme while other features have different one (Z is UP)
+ * @return bone track for the specified bone
+ */
+ public Track calculateTrack(int targetIndex, Quaternion localQuaternionRotation, int startFrame, int stopFrame, int fps, boolean spatialTrack) {
+ if (calculatedTrack == null) {
+ // preparing data for track
+ int framesAmount = stopFrame - startFrame;
+ float timeBetweenFrames = 1.0f / fps;
+
+ float[] times = new float[framesAmount + 1];
+ Vector3f[] translations = new Vector3f[framesAmount + 1];
+ float[] translation = new float[3];
+ Quaternion[] rotations = new Quaternion[framesAmount + 1];
+ float[] quaternionRotation = new float[] { 0, 0, 0, 1 };
+ float[] objectRotation = new float[3];
+ Vector3f[] scales = new Vector3f[framesAmount + 1];
+ float[] scale = new float[] { 1.0f, 1.0f, 1.0f };
+ float degreeToRadiansFactor = 1;
+ if (blenderVersion < 250) {// in blender earlier than 2.50 the values are stored in degrees
+ degreeToRadiansFactor *= FastMath.DEG_TO_RAD * 10;// the values in blender are divided by 10, so we need to mult it here
+ }
+
+ // calculating track data
+ for (int frame = startFrame; frame <= stopFrame; ++frame) {
+ int index = frame - startFrame;
+ times[index] = index * timeBetweenFrames;// start + (frame - 1) * timeBetweenFrames;
+ for (int j = 0; j < bezierCurves.length; ++j) {
+ double value = bezierCurves[j].evaluate(frame, BezierCurve.Y_VALUE);
+ switch (bezierCurves[j].getType()) {
+ // LOCATION
+ case AC_LOC_X:
+ translation[0] = (float) value;
+ break;
+ case AC_LOC_Y:
+ if (fixUpAxis) {
+ translation[2] = (float) -value;
+ } else {
+ translation[1] = (float) value;
+ }
+ break;
+ case AC_LOC_Z:
+ translation[fixUpAxis ? 1 : 2] = (float) value;
+ break;
+
+ // ROTATION (used with object animation)
+ // the value here is in degrees divided by 10 (so in
+ // example: 9 = PI/2)
+ case OB_ROT_X:
+ objectRotation[0] = (float) value * degreeToRadiansFactor;
+ break;
+ case OB_ROT_Y:
+ if (fixUpAxis) {
+ objectRotation[2] = (float) -value * degreeToRadiansFactor;
+ } else {
+ objectRotation[1] = (float) value * degreeToRadiansFactor;
+ }
+ break;
+ case OB_ROT_Z:
+ objectRotation[fixUpAxis ? 1 : 2] = (float) value * degreeToRadiansFactor;
+ break;
+
+ // SIZE
+ case AC_SIZE_X:
+ scale[0] = (float) value;
+ break;
+ case AC_SIZE_Y:
+ if (fixUpAxis) {
+ scale[2] = (float) value;
+ } else {
+ scale[1] = (float) value;
+ }
+ break;
+ case AC_SIZE_Z:
+ scale[fixUpAxis ? 1 : 2] = (float) value;
+ break;
+
+ // QUATERNION ROTATION (used with bone animation), dunno
+ // why but here we shouldn't check the
+ // spatialTrack flag value
+ case AC_QUAT_W:
+ quaternionRotation[3] = (float) value;
+ break;
+ case AC_QUAT_X:
+ quaternionRotation[0] = (float) value;
+ break;
+ case AC_QUAT_Y:
+ if (fixUpAxis) {
+ quaternionRotation[2] = -(float) value;
+ } else {
+ quaternionRotation[1] = (float) value;
+ }
+ break;
+ case AC_QUAT_Z:
+ if (fixUpAxis) {
+ quaternionRotation[1] = (float) value;
+ } else {
+ quaternionRotation[2] = (float) value;
+ }
+ break;
+ default:
+ LOGGER.warning("Unknown ipo curve type: " + bezierCurves[j].getType());
+ }
+ }
+ translations[index] = localQuaternionRotation.multLocal(new Vector3f(translation[0], translation[1], translation[2]));
+ rotations[index] = spatialTrack ? new Quaternion().fromAngles(objectRotation) : new Quaternion(quaternionRotation[0], quaternionRotation[1], quaternionRotation[2], quaternionRotation[3]);
+ scales[index] = new Vector3f(scale[0], scale[1], scale[2]);
+ }
+ if (spatialTrack) {
+ calculatedTrack = new SpatialTrack(times, translations, rotations, scales);
+ } else {
+ calculatedTrack = new BoneTrack(targetIndex, times, translations, rotations, scales);
+ }
+ }
+ return calculatedTrack;
+ }
}
\ No newline at end of file
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/animations/IpoHelper.java b/engine/src/blender/com/jme3/scene/plugins/blender/animations/IpoHelper.java
index d6e6cd50b..6e0d9961e 100644
--- a/engine/src/blender/com/jme3/scene/plugins/blender/animations/IpoHelper.java
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/animations/IpoHelper.java
@@ -21,182 +21,182 @@ import java.util.logging.Logger;
* @author Marcin Roguski
*/
public class IpoHelper extends AbstractBlenderHelper {
- private static final Logger LOGGER = Logger.getLogger(IpoHelper.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
- * @param fixUpAxis
- * a variable that indicates if the Y asxis is the UP axis or not
- */
- public IpoHelper(String blenderVersion, boolean fixUpAxis) {
- super(blenderVersion, fixUpAxis);
- }
-
- /**
- * This method creates an ipo object used for interpolation calculations.
- *
- * @param ipoStructure
- * the structure with ipo definition
- * @param blenderContext
- * the blender context
- * @return the ipo object
- * @throws BlenderFileException
- * this exception is thrown when the blender file is somehow
- * corrupted
- */
- public Ipo fromIpoStructure(Structure ipoStructure, BlenderContext blenderContext) throws BlenderFileException {
- Structure curvebase = (Structure) ipoStructure.getFieldValue("curve");
-
- // preparing bezier curves
- Ipo result = null;
- List curves = curvebase.evaluateListBase(blenderContext);// IpoCurve
- if (curves.size() > 0) {
- BezierCurve[] bezierCurves = new BezierCurve[curves.size()];
- int frame = 0;
- for (Structure curve : curves) {
- Pointer pBezTriple = (Pointer) curve.getFieldValue("bezt");
- List bezTriples = pBezTriple.fetchData(blenderContext.getInputStream());
- int type = ((Number) curve.getFieldValue("adrcode")).intValue();
- bezierCurves[frame++] = new BezierCurve(type, bezTriples, 2);
- }
- curves.clear();
- result = new Ipo(bezierCurves, fixUpAxis, blenderContext.getBlenderVersion());
- blenderContext.addLoadedFeatures(ipoStructure.getOldMemoryAddress(), ipoStructure.getName(), ipoStructure, result);
- }
- return result;
- }
-
- /**
- * This method creates an ipo object used for interpolation calculations. It
- * should be called for blender version 2.50 and higher.
- *
- * @param actionStructure
- * the structure with action definition
- * @param blenderContext
- * the blender context
- * @return the ipo object
- * @throws BlenderFileException
- * this exception is thrown when the blender file is somehow
- * corrupted
- */
- public Ipo fromAction(Structure actionStructure, BlenderContext blenderContext) throws BlenderFileException {
- Ipo result = null;
- List curves = ((Structure) actionStructure.getFieldValue("curves")).evaluateListBase(blenderContext);// FCurve
- if (curves.size() > 0) {
- BezierCurve[] bezierCurves = new BezierCurve[curves.size()];
- int frame = 0;
- for (Structure curve : curves) {
- Pointer pBezTriple = (Pointer) curve.getFieldValue("bezt");
- List bezTriples = pBezTriple.fetchData(blenderContext.getInputStream());
- int type = this.getCurveType(curve, blenderContext);
- bezierCurves[frame++] = new BezierCurve(type, bezTriples, 2);
- }
- curves.clear();
- result = new Ipo(bezierCurves, fixUpAxis, blenderContext.getBlenderVersion());
- }
- return result;
- }
-
- /**
- * This method returns the type of the ipo curve.
- *
- * @param structure
- * the structure must contain the 'rna_path' field and
- * 'array_index' field (the type is not important here)
- * @param blenderContext
- * the blender context
- * @return the type of the curve
- */
- public int getCurveType(Structure structure, BlenderContext blenderContext) {
- // reading rna path first
- BlenderInputStream bis = blenderContext.getInputStream();
- int currentPosition = bis.getPosition();
- Pointer pRnaPath = (Pointer) structure.getFieldValue("rna_path");
- FileBlockHeader dataFileBlock = blenderContext.getFileBlock(pRnaPath.getOldMemoryAddress());
- bis.setPosition(dataFileBlock.getBlockPosition());
- String rnaPath = bis.readString();
- bis.setPosition(currentPosition);
- int arrayIndex = ((Number) structure.getFieldValue("array_index")).intValue();
-
- // determining the curve type
- if (rnaPath.endsWith("location")) {
- return Ipo.AC_LOC_X + arrayIndex;
- }
- if (rnaPath.endsWith("rotation_quaternion")) {
- return Ipo.AC_QUAT_W + arrayIndex;
- }
- if (rnaPath.endsWith("scale")) {
- return Ipo.AC_SIZE_X + arrayIndex;
- }
- if (rnaPath.endsWith("rotation") || rnaPath.endsWith("rotation_euler")) {
- return Ipo.OB_ROT_X + arrayIndex;
- }
- LOGGER.warning("Unknown curve rna path: " + rnaPath);
- return -1;
- }
-
- /**
- * 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 fromValue(float constValue) {
- return new ConstIpo(constValue);
- }
-
- @Override
- public boolean shouldBeLoaded(Structure structure, BlenderContext blenderContext) {
- return true;
- }
-
- /**
- * 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. */
- private float constValue;
-
- /**
- * Constructor. Stores the constant value of this ipo.
- *
- * @param constValue
- * the constant value of this ipo
- */
- public ConstIpo(float constValue) {
- super(null, false, 0);//the version is not important here
- this.constValue = constValue;
- }
-
- @Override
- public float calculateValue(int frame) {
- return constValue;
- }
-
- @Override
- public float calculateValue(int frame, int curveIndex) {
- return constValue;
- }
-
- @Override
- public int getCurvesAmount() {
- return 0;
- }
-
- @Override
- public BoneTrack calculateTrack(int boneIndex, Quaternion localQuaternionRotation, int startFrame, int stopFrame, int fps, boolean boneTrack) {
- throw new IllegalStateException("Constatnt ipo object cannot be used for calculating bone tracks!");
- }
- }
+ private static final Logger LOGGER = Logger.getLogger(IpoHelper.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
+ * @param fixUpAxis
+ * a variable that indicates if the Y asxis is the UP axis or not
+ */
+ public IpoHelper(String blenderVersion, boolean fixUpAxis) {
+ super(blenderVersion, fixUpAxis);
+ }
+
+ /**
+ * This method creates an ipo object used for interpolation calculations.
+ *
+ * @param ipoStructure
+ * the structure with ipo definition
+ * @param blenderContext
+ * the blender context
+ * @return the ipo object
+ * @throws BlenderFileException
+ * this exception is thrown when the blender file is somehow
+ * corrupted
+ */
+ public Ipo fromIpoStructure(Structure ipoStructure, BlenderContext blenderContext) throws BlenderFileException {
+ Structure curvebase = (Structure) ipoStructure.getFieldValue("curve");
+
+ // preparing bezier curves
+ Ipo result = null;
+ List curves = curvebase.evaluateListBase(blenderContext);// IpoCurve
+ if (curves.size() > 0) {
+ BezierCurve[] bezierCurves = new BezierCurve[curves.size()];
+ int frame = 0;
+ for (Structure curve : curves) {
+ Pointer pBezTriple = (Pointer) curve.getFieldValue("bezt");
+ List bezTriples = pBezTriple.fetchData(blenderContext.getInputStream());
+ int type = ((Number) curve.getFieldValue("adrcode")).intValue();
+ bezierCurves[frame++] = new BezierCurve(type, bezTriples, 2);
+ }
+ curves.clear();
+ result = new Ipo(bezierCurves, fixUpAxis, blenderContext.getBlenderVersion());
+ blenderContext.addLoadedFeatures(ipoStructure.getOldMemoryAddress(), ipoStructure.getName(), ipoStructure, result);
+ }
+ return result;
+ }
+
+ /**
+ * This method creates an ipo object used for interpolation calculations. It
+ * should be called for blender version 2.50 and higher.
+ *
+ * @param actionStructure
+ * the structure with action definition
+ * @param blenderContext
+ * the blender context
+ * @return the ipo object
+ * @throws BlenderFileException
+ * this exception is thrown when the blender file is somehow
+ * corrupted
+ */
+ public Ipo fromAction(Structure actionStructure, BlenderContext blenderContext) throws BlenderFileException {
+ Ipo result = null;
+ List curves = ((Structure) actionStructure.getFieldValue("curves")).evaluateListBase(blenderContext);// FCurve
+ if (curves.size() > 0) {
+ BezierCurve[] bezierCurves = new BezierCurve[curves.size()];
+ int frame = 0;
+ for (Structure curve : curves) {
+ Pointer pBezTriple = (Pointer) curve.getFieldValue("bezt");
+ List bezTriples = pBezTriple.fetchData(blenderContext.getInputStream());
+ int type = this.getCurveType(curve, blenderContext);
+ bezierCurves[frame++] = new BezierCurve(type, bezTriples, 2);
+ }
+ curves.clear();
+ result = new Ipo(bezierCurves, fixUpAxis, blenderContext.getBlenderVersion());
+ }
+ return result;
+ }
+
+ /**
+ * This method returns the type of the ipo curve.
+ *
+ * @param structure
+ * the structure must contain the 'rna_path' field and
+ * 'array_index' field (the type is not important here)
+ * @param blenderContext
+ * the blender context
+ * @return the type of the curve
+ */
+ public int getCurveType(Structure structure, BlenderContext blenderContext) {
+ // reading rna path first
+ BlenderInputStream bis = blenderContext.getInputStream();
+ int currentPosition = bis.getPosition();
+ Pointer pRnaPath = (Pointer) structure.getFieldValue("rna_path");
+ FileBlockHeader dataFileBlock = blenderContext.getFileBlock(pRnaPath.getOldMemoryAddress());
+ bis.setPosition(dataFileBlock.getBlockPosition());
+ String rnaPath = bis.readString();
+ bis.setPosition(currentPosition);
+ int arrayIndex = ((Number) structure.getFieldValue("array_index")).intValue();
+
+ // determining the curve type
+ if (rnaPath.endsWith("location")) {
+ return Ipo.AC_LOC_X + arrayIndex;
+ }
+ if (rnaPath.endsWith("rotation_quaternion")) {
+ return Ipo.AC_QUAT_W + arrayIndex;
+ }
+ if (rnaPath.endsWith("scale")) {
+ return Ipo.AC_SIZE_X + arrayIndex;
+ }
+ if (rnaPath.endsWith("rotation") || rnaPath.endsWith("rotation_euler")) {
+ return Ipo.OB_ROT_X + arrayIndex;
+ }
+ LOGGER.warning("Unknown curve rna path: " + rnaPath);
+ return -1;
+ }
+
+ /**
+ * 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 fromValue(float constValue) {
+ return new ConstIpo(constValue);
+ }
+
+ @Override
+ public boolean shouldBeLoaded(Structure structure, BlenderContext blenderContext) {
+ return true;
+ }
+
+ /**
+ * 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. */
+ private float constValue;
+
+ /**
+ * Constructor. Stores the constant value of this ipo.
+ *
+ * @param constValue
+ * the constant value of this ipo
+ */
+ public ConstIpo(float constValue) {
+ super(null, false, 0);// the version is not important here
+ this.constValue = constValue;
+ }
+
+ @Override
+ public float calculateValue(int frame) {
+ return constValue;
+ }
+
+ @Override
+ public float calculateValue(int frame, int curveIndex) {
+ return constValue;
+ }
+
+ @Override
+ public int getCurvesAmount() {
+ return 0;
+ }
+
+ @Override
+ public BoneTrack calculateTrack(int boneIndex, Quaternion localQuaternionRotation, int startFrame, int stopFrame, int fps, boolean boneTrack) {
+ throw new IllegalStateException("Constatnt ipo object cannot be used for calculating bone tracks!");
+ }
+ }
}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/cameras/CameraHelper.java b/engine/src/blender/com/jme3/scene/plugins/blender/cameras/CameraHelper.java
index 4fc529ff9..044df1676 100644
--- a/engine/src/blender/com/jme3/scene/plugins/blender/cameras/CameraHelper.java
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/cameras/CameraHelper.java
@@ -17,59 +17,59 @@ import java.util.logging.Logger;
*/
public class CameraHelper extends AbstractBlenderHelper {
- private static final Logger LOGGER = Logger.getLogger(CameraHelper.class.getName());
- protected static final int DEFAULT_CAM_WIDTH = 640;
- protected static final int DEFAULT_CAM_HEIGHT = 480;
-
+ private static final Logger LOGGER = Logger.getLogger(CameraHelper.class.getName());
+ protected static final int DEFAULT_CAM_WIDTH = 640;
+ protected static final int DEFAULT_CAM_HEIGHT = 480;
+
/**
* 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
+ * the version read from the blend file
* @param fixUpAxis
- * a variable that indicates if the Y asxis is the UP axis or not
+ * a variable that indicates if the Y asxis is the UP axis or not
*/
public CameraHelper(String blenderVersion, boolean fixUpAxis) {
super(blenderVersion, fixUpAxis);
}
-
- /**
- * This method converts the given structure to jme camera.
- *
- * @param structure
- * camera structure
- * @return jme camera object
- * @throws BlenderFileException
- * an exception is thrown when there are problems with the
- * blender file
- */
+
+ /**
+ * This method converts the given structure to jme camera.
+ *
+ * @param structure
+ * camera structure
+ * @return jme camera object
+ * @throws BlenderFileException
+ * an exception is thrown when there are problems with the
+ * blender file
+ */
public CameraNode toCamera(Structure structure, BlenderContext blenderContext) throws BlenderFileException {
- if (blenderVersion >= 250) {
+ if (blenderVersion >= 250) {
return this.toCamera250(structure, blenderContext.getSceneStructure());
} else {
- return this.toCamera249(structure);
+ return this.toCamera249(structure);
}
}
- /**
- * This method converts the given structure to jme camera. Should be used form blender 2.5+.
- *
- * @param structure
- * camera structure
- * @param sceneStructure
- * scene structure
- * @return jme camera object
- * @throws BlenderFileException
- * an exception is thrown when there are problems with the
- * blender file
- */
+ /**
+ * This method converts the given structure to jme camera. Should be used form blender 2.5+.
+ *
+ * @param structure
+ * camera structure
+ * @param sceneStructure
+ * scene structure
+ * @return jme camera object
+ * @throws BlenderFileException
+ * an exception is thrown when there are problems with the
+ * blender file
+ */
private CameraNode toCamera250(Structure structure, Structure sceneStructure) throws BlenderFileException {
int width = DEFAULT_CAM_WIDTH;
int height = DEFAULT_CAM_HEIGHT;
if (sceneStructure != null) {
- Structure renderData = (Structure)sceneStructure.getFieldValue("r");
- width = ((Number)renderData.getFieldValue("xsch")).shortValue();
- height = ((Number)renderData.getFieldValue("ysch")).shortValue();
+ Structure renderData = (Structure) sceneStructure.getFieldValue("r");
+ width = ((Number) renderData.getFieldValue("xsch")).shortValue();
+ height = ((Number) renderData.getFieldValue("ysch")).shortValue();
}
Camera camera = new Camera(width, height);
int type = ((Number) structure.getFieldValue("type")).intValue();
@@ -77,9 +77,9 @@ public class CameraHelper extends AbstractBlenderHelper {
LOGGER.log(Level.WARNING, "Unknown camera type: {0}. Perspective camera is being used!", type);
type = 0;
}
- //type==0 - perspective; type==1 - orthographic; perspective is used as default
+ // type==0 - perspective; type==1 - orthographic; perspective is used as default
camera.setParallelProjection(type == 1);
- float aspect = width / (float)height;
+ float aspect = width / (float) height;
float fovY; // Vertical field of view in degrees
float clipsta = ((Number) structure.getFieldValue("clipsta")).floatValue();
float clipend = ((Number) structure.getFieldValue("clipend")).floatValue();
@@ -88,7 +88,7 @@ public class CameraHelper extends AbstractBlenderHelper {
// Default sensor size prior to 2.60 was 32.
float sensor = 32.0f;
boolean sensorVertical = false;
- Number sensorFit = (Number)structure.getFieldValue("sensor_fit");
+ Number sensorFit = (Number) structure.getFieldValue("sensor_fit");
if (sensorFit != null) {
// If sensor_fit is vert (2), then sensor_y is used
sensorVertical = sensorFit.byteValue() == 2;
@@ -113,17 +113,17 @@ public class CameraHelper extends AbstractBlenderHelper {
camera.setFrustumPerspective(fovY, aspect, clipsta, clipend);
return new CameraNode(null, camera);
}
-
+
/**
- * This method converts the given structure to jme camera. Should be used form blender 2.49.
- *
- * @param structure
- * camera structure
- * @return jme camera object
- * @throws BlenderFileException
- * an exception is thrown when there are problems with the
- * blender file
- */
+ * This method converts the given structure to jme camera. Should be used form blender 2.49.
+ *
+ * @param structure
+ * camera structure
+ * @return jme camera object
+ * @throws BlenderFileException
+ * an exception is thrown when there are problems with the
+ * blender file
+ */
private CameraNode toCamera249(Structure structure) throws BlenderFileException {
Camera camera = new Camera(DEFAULT_CAM_WIDTH, DEFAULT_CAM_HEIGHT);
int type = ((Number) structure.getFieldValue("type")).intValue();
@@ -131,7 +131,7 @@ public class CameraHelper extends AbstractBlenderHelper {
LOGGER.log(Level.WARNING, "Unknown camera type: {0}. Perspective camera is being used!", type);
type = 0;
}
- //type==0 - perspective; type==1 - orthographic; perspective is used as default
+ // type==0 - perspective; type==1 - orthographic; perspective is used as default
camera.setParallelProjection(type == 1);
float aspect = 0;
float clipsta = ((Number) structure.getFieldValue("clipsta")).floatValue();
@@ -145,8 +145,8 @@ public class CameraHelper extends AbstractBlenderHelper {
return new CameraNode(null, camera);
}
- @Override
- public boolean shouldBeLoaded(Structure structure, BlenderContext blenderContext) {
- return (blenderContext.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.CAMERAS) != 0;
- }
+ @Override
+ public boolean shouldBeLoaded(Structure structure, BlenderContext blenderContext) {
+ return (blenderContext.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.CAMERAS) != 0;
+ }
}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/constraints/BoneConstraint.java b/engine/src/blender/com/jme3/scene/plugins/blender/constraints/BoneConstraint.java
index 593fa5667..386242ed5 100644
--- a/engine/src/blender/com/jme3/scene/plugins/blender/constraints/BoneConstraint.java
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/constraints/BoneConstraint.java
@@ -26,194 +26,193 @@ import com.jme3.scene.plugins.ogre.AnimData;
* Constraint applied on the bone.
* @author Marcin Roguski (Kaelthas)
*/
-/*package*/ class BoneConstraint extends Constraint {
- private static final Logger LOGGER = Logger.getLogger(BoneConstraint.class.getName());
-
- protected boolean isNodeTarget;
-
- /**
- * The bone constraint constructor.
- *
- * @param constraintStructure
- * the constraint's structure
- * @param ownerOMA
- * the OMA of the bone that owns the constraint
- * @param influenceIpo
- * the influence interpolation curve
- * @param blenderContext
- * the blender context
- * @throws BlenderFileException
- * exception thrown when problems with blender file occur
- */
- public BoneConstraint(Structure constraintStructure, Long ownerOMA, Ipo influenceIpo, BlenderContext blenderContext)
- throws BlenderFileException {
- super(constraintStructure, ownerOMA, influenceIpo, blenderContext);
- }
-
- @Override
- protected boolean validate() {
- if(targetOMA != null) {
- Spatial nodeTarget = (Spatial)blenderContext.getLoadedFeature(targetOMA, LoadedFeatureDataType.LOADED_FEATURE);
- //the second part of the if expression verifies if the found node (if any) is an armature node
- if(nodeTarget == null || nodeTarget.getUserData(ArmatureHelper.ARMETURE_NODE_MARKER) != null) {
- //if the target is not an object node then it is an Armature, so make sure the bone is in the current skeleton
- BoneContext boneContext = blenderContext.getBoneContext(ownerOMA);
- if(targetOMA.longValue() != boneContext.getArmatureObjectOMA().longValue()) {
- LOGGER.log(Level.WARNING, "Bone constraint {0} must target bone in the its own skeleton! Targeting bone in another skeleton is not supported!", name);
- return false;
- }
- } else {
- isNodeTarget = true;
- }
- }
-
- return true;
- }
-
- @Override
- public void performBakingOperation() {
- Bone owner = blenderContext.getBoneContext(ownerOMA).getBone();
-
- if(targetOMA != null) {
- if(isNodeTarget) {
- Spatial target = (Spatial) blenderContext.getLoadedFeature(targetOMA, LoadedFeatureDataType.LOADED_FEATURE);
- this.prepareTracksForApplyingConstraints();
- AnimData animData = blenderContext.getAnimData(ownerOMA);
- if(animData != null) {
- for(Animation animation : animData.anims) {
- Transform ownerTransform = constraintHelper.getBoneTransform(ownerSpace, owner);
- Transform targetTransform = constraintHelper.getNodeObjectTransform(targetSpace, targetOMA, blenderContext);
-
- Track boneTrack = constraintHelper.getTrack(owner, animData.skeleton, animation);
- Track targetTrack = constraintHelper.getTrack(target, animation);
-
- constraintDefinition.bake(ownerTransform, targetTransform, boneTrack, targetTrack, this.ipo);
- }
- }
- } else {
- BoneContext boneContext = blenderContext.getBoneByName(subtargetName);
- Bone target = boneContext.getBone();
- this.targetOMA = boneContext.getBoneOma();
-
- this.prepareTracksForApplyingConstraints();
- AnimData animData = blenderContext.getAnimData(ownerOMA);
- if(animData != null) {
- for(Animation animation : animData.anims) {
- Transform ownerTransform = constraintHelper.getBoneTransform(ownerSpace, owner);
- Transform targetTransform = constraintHelper.getBoneTransform(targetSpace, target);
-
- Track boneTrack = constraintHelper.getTrack(owner, animData.skeleton, animation);
- Track targetTrack = constraintHelper.getTrack(target, animData.skeleton, animation);
-
- constraintDefinition.bake(ownerTransform, targetTransform, boneTrack, targetTrack, this.ipo);
- }
- }
- }
- } else {
- this.prepareTracksForApplyingConstraints();
- AnimData animData = blenderContext.getAnimData(ownerOMA);
- if(animData != null) {
- for(Animation animation : animData.anims) {
- Transform ownerTransform = constraintHelper.getBoneTransform(ownerSpace, owner);
- Track boneTrack = constraintHelper.getTrack(owner, animData.skeleton, animation);
-
- constraintDefinition.bake(ownerTransform, null, boneTrack, null, this.ipo);
- }
- }
- }
- }
-
- @Override
- protected void prepareTracksForApplyingConstraints() {
- Long[] bonesOMAs = new Long[] { ownerOMA, targetOMA };
- Space[] spaces = new Space[] { ownerSpace, targetSpace };
-
- //creating animations for current objects if at least on of their parents have an animation
- for (int i = 0; i < bonesOMAs.length; ++i) {
- Long oma = bonesOMAs[i];
- if(this.hasAnimation(oma)) {
- Bone currentBone = blenderContext.getBoneContext(oma).getBone();
- Bone parent = currentBone.getParent();
- boolean foundAnimation = false;
- AnimData animData = null;
- while(parent != null && !foundAnimation) {
- BoneContext boneContext = blenderContext.getBoneByName(parent.getName());
- foundAnimation = this.hasAnimation(boneContext.getBoneOma());
- animData = blenderContext.getAnimData(boneContext.getBoneOma());
- parent = parent.getParent();
- }
-
- if(foundAnimation) {
- this.applyAnimData(blenderContext.getBoneContext(oma), spaces[i], animData);
- }
- }
- }
-
- //creating animation for owner if it doesn't have one already and if the target has it
- if(!this.hasAnimation(ownerOMA) && this.hasAnimation(targetOMA)) {
- AnimData targetAnimData = blenderContext.getAnimData(targetOMA);
- this.applyAnimData(blenderContext.getBoneContext(ownerOMA), ownerSpace, targetAnimData);
- }
- }
-
- /**
- * The method determines if the bone has animations.
- *
- * @param animOwnerOMA
- * OMA of the animation's owner
- * @return true if the target has animations and false otherwise
- */
- protected boolean hasAnimation(Long animOwnerOMA) {
- AnimData animData = blenderContext.getAnimData(animOwnerOMA);
- if(animData != null) {
- if(!isNodeTarget) {
- Bone bone = blenderContext.getBoneContext(animOwnerOMA).getBone();
- int boneIndex = animData.skeleton.getBoneIndex(bone);
- for(Animation animation : animData.anims) {
- for(Track track : animation.getTracks()) {
- if(track instanceof BoneTrack && ((BoneTrack) track).getTargetBoneIndex() == boneIndex) {
- return true;
- }
- }
- }
- } else {
- return true;
- }
- }
- return false;
- }
-
- /**
- * The method applies bone's current position to all of the traces of the
- * given animations.
- *
- * @param boneContext
- * the bone context
- * @param space
- * the bone's evaluation space
- * @param referenceAnimData
- * the object containing the animations
- */
- protected void applyAnimData(BoneContext boneContext, Space space, AnimData referenceAnimData) {
- ConstraintHelper constraintHelper = blenderContext.getHelper(ConstraintHelper.class);
- Transform transform = constraintHelper.getBoneTransform(space, boneContext.getBone());
-
- AnimData animData = blenderContext.getAnimData(boneContext.getBoneOma());
-
- for(Animation animation : referenceAnimData.anims) {
- BoneTrack parentTrack = (BoneTrack) animation.getTracks()[0];
-
- float[] times = parentTrack.getTimes();
- Vector3f[] translations = new Vector3f[times.length];
- Quaternion[] rotations = new Quaternion[times.length];
- Vector3f[] scales = new Vector3f[times.length];
- Arrays.fill(translations, transform.getTranslation());
- Arrays.fill(rotations, transform.getRotation());
- Arrays.fill(scales, transform.getScale());
- for(Animation anim : animData.anims) {
- anim.addTrack(new BoneTrack(animData.skeleton.getBoneIndex(boneContext.getBone()), times, translations, rotations, scales));
- }
- }
- blenderContext.setAnimData(boneContext.getBoneOma(), animData);
- }
+/* package */class BoneConstraint extends Constraint {
+ private static final Logger LOGGER = Logger.getLogger(BoneConstraint.class.getName());
+
+ protected boolean isNodeTarget;
+
+ /**
+ * The bone constraint constructor.
+ *
+ * @param constraintStructure
+ * the constraint's structure
+ * @param ownerOMA
+ * the OMA of the bone that owns the constraint
+ * @param influenceIpo
+ * the influence interpolation curve
+ * @param blenderContext
+ * the blender context
+ * @throws BlenderFileException
+ * exception thrown when problems with blender file occur
+ */
+ public BoneConstraint(Structure constraintStructure, Long ownerOMA, Ipo influenceIpo, BlenderContext blenderContext) throws BlenderFileException {
+ super(constraintStructure, ownerOMA, influenceIpo, blenderContext);
+ }
+
+ @Override
+ protected boolean validate() {
+ if (targetOMA != null) {
+ Spatial nodeTarget = (Spatial) blenderContext.getLoadedFeature(targetOMA, LoadedFeatureDataType.LOADED_FEATURE);
+ // the second part of the if expression verifies if the found node (if any) is an armature node
+ if (nodeTarget == null || nodeTarget.getUserData(ArmatureHelper.ARMETURE_NODE_MARKER) != null) {
+ // if the target is not an object node then it is an Armature, so make sure the bone is in the current skeleton
+ BoneContext boneContext = blenderContext.getBoneContext(ownerOMA);
+ if (targetOMA.longValue() != boneContext.getArmatureObjectOMA().longValue()) {
+ LOGGER.log(Level.WARNING, "Bone constraint {0} must target bone in the its own skeleton! Targeting bone in another skeleton is not supported!", name);
+ return false;
+ }
+ } else {
+ isNodeTarget = true;
+ }
+ }
+
+ return true;
+ }
+
+ @Override
+ public void performBakingOperation() {
+ Bone owner = blenderContext.getBoneContext(ownerOMA).getBone();
+
+ if (targetOMA != null) {
+ if (isNodeTarget) {
+ Spatial target = (Spatial) blenderContext.getLoadedFeature(targetOMA, LoadedFeatureDataType.LOADED_FEATURE);
+ this.prepareTracksForApplyingConstraints();
+ AnimData animData = blenderContext.getAnimData(ownerOMA);
+ if (animData != null) {
+ for (Animation animation : animData.anims) {
+ Transform ownerTransform = constraintHelper.getBoneTransform(ownerSpace, owner);
+ Transform targetTransform = constraintHelper.getNodeObjectTransform(targetSpace, targetOMA, blenderContext);
+
+ Track boneTrack = constraintHelper.getTrack(owner, animData.skeleton, animation);
+ Track targetTrack = constraintHelper.getTrack(target, animation);
+
+ constraintDefinition.bake(ownerTransform, targetTransform, boneTrack, targetTrack, this.ipo);
+ }
+ }
+ } else {
+ BoneContext boneContext = blenderContext.getBoneByName(subtargetName);
+ Bone target = boneContext.getBone();
+ this.targetOMA = boneContext.getBoneOma();
+
+ this.prepareTracksForApplyingConstraints();
+ AnimData animData = blenderContext.getAnimData(ownerOMA);
+ if (animData != null) {
+ for (Animation animation : animData.anims) {
+ Transform ownerTransform = constraintHelper.getBoneTransform(ownerSpace, owner);
+ Transform targetTransform = constraintHelper.getBoneTransform(targetSpace, target);
+
+ Track boneTrack = constraintHelper.getTrack(owner, animData.skeleton, animation);
+ Track targetTrack = constraintHelper.getTrack(target, animData.skeleton, animation);
+
+ constraintDefinition.bake(ownerTransform, targetTransform, boneTrack, targetTrack, this.ipo);
+ }
+ }
+ }
+ } else {
+ this.prepareTracksForApplyingConstraints();
+ AnimData animData = blenderContext.getAnimData(ownerOMA);
+ if (animData != null) {
+ for (Animation animation : animData.anims) {
+ Transform ownerTransform = constraintHelper.getBoneTransform(ownerSpace, owner);
+ Track boneTrack = constraintHelper.getTrack(owner, animData.skeleton, animation);
+
+ constraintDefinition.bake(ownerTransform, null, boneTrack, null, this.ipo);
+ }
+ }
+ }
+ }
+
+ @Override
+ protected void prepareTracksForApplyingConstraints() {
+ Long[] bonesOMAs = new Long[] { ownerOMA, targetOMA };
+ Space[] spaces = new Space[] { ownerSpace, targetSpace };
+
+ // creating animations for current objects if at least on of their parents have an animation
+ for (int i = 0; i < bonesOMAs.length; ++i) {
+ Long oma = bonesOMAs[i];
+ if (this.hasAnimation(oma)) {
+ Bone currentBone = blenderContext.getBoneContext(oma).getBone();
+ Bone parent = currentBone.getParent();
+ boolean foundAnimation = false;
+ AnimData animData = null;
+ while (parent != null && !foundAnimation) {
+ BoneContext boneContext = blenderContext.getBoneByName(parent.getName());
+ foundAnimation = this.hasAnimation(boneContext.getBoneOma());
+ animData = blenderContext.getAnimData(boneContext.getBoneOma());
+ parent = parent.getParent();
+ }
+
+ if (foundAnimation) {
+ this.applyAnimData(blenderContext.getBoneContext(oma), spaces[i], animData);
+ }
+ }
+ }
+
+ // creating animation for owner if it doesn't have one already and if the target has it
+ if (!this.hasAnimation(ownerOMA) && this.hasAnimation(targetOMA)) {
+ AnimData targetAnimData = blenderContext.getAnimData(targetOMA);
+ this.applyAnimData(blenderContext.getBoneContext(ownerOMA), ownerSpace, targetAnimData);
+ }
+ }
+
+ /**
+ * The method determines if the bone has animations.
+ *
+ * @param animOwnerOMA
+ * OMA of the animation's owner
+ * @return true if the target has animations and false otherwise
+ */
+ protected boolean hasAnimation(Long animOwnerOMA) {
+ AnimData animData = blenderContext.getAnimData(animOwnerOMA);
+ if (animData != null) {
+ if (!isNodeTarget) {
+ Bone bone = blenderContext.getBoneContext(animOwnerOMA).getBone();
+ int boneIndex = animData.skeleton.getBoneIndex(bone);
+ for (Animation animation : animData.anims) {
+ for (Track track : animation.getTracks()) {
+ if (track instanceof BoneTrack && ((BoneTrack) track).getTargetBoneIndex() == boneIndex) {
+ return true;
+ }
+ }
+ }
+ } else {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * The method applies bone's current position to all of the traces of the
+ * given animations.
+ *
+ * @param boneContext
+ * the bone context
+ * @param space
+ * the bone's evaluation space
+ * @param referenceAnimData
+ * the object containing the animations
+ */
+ protected void applyAnimData(BoneContext boneContext, Space space, AnimData referenceAnimData) {
+ ConstraintHelper constraintHelper = blenderContext.getHelper(ConstraintHelper.class);
+ Transform transform = constraintHelper.getBoneTransform(space, boneContext.getBone());
+
+ AnimData animData = blenderContext.getAnimData(boneContext.getBoneOma());
+
+ for (Animation animation : referenceAnimData.anims) {
+ BoneTrack parentTrack = (BoneTrack) animation.getTracks()[0];
+
+ float[] times = parentTrack.getTimes();
+ Vector3f[] translations = new Vector3f[times.length];
+ Quaternion[] rotations = new Quaternion[times.length];
+ Vector3f[] scales = new Vector3f[times.length];
+ Arrays.fill(translations, transform.getTranslation());
+ Arrays.fill(rotations, transform.getRotation());
+ Arrays.fill(scales, transform.getScale());
+ for (Animation anim : animData.anims) {
+ anim.addTrack(new BoneTrack(animData.skeleton.getBoneIndex(boneContext.getBone()), times, translations, rotations, scales));
+ }
+ }
+ blenderContext.setAnimData(boneContext.getBoneOma(), animData);
+ }
}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/constraints/Constraint.java b/engine/src/blender/com/jme3/scene/plugins/blender/constraints/Constraint.java
index 07792e652..7a883c8d0 100644
--- a/engine/src/blender/com/jme3/scene/plugins/blender/constraints/Constraint.java
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/constraints/Constraint.java
@@ -19,109 +19,109 @@ import com.jme3.scene.plugins.blender.file.Structure;
* @author Marcin Roguski (Kaelthas)
*/
public abstract class Constraint {
- private static final Logger LOGGER = Logger.getLogger(Constraint.class.getName());
-
- /** The name of this constraint. */
- protected final String name;
- /** Indicates if the constraint is already baked or not. */
- protected boolean baked;
-
- protected Space ownerSpace;
- protected final ConstraintDefinition constraintDefinition;
- protected Long ownerOMA;
-
- protected Long targetOMA;
- protected Space targetSpace;
- protected String subtargetName;
-
- /** The ipo object defining influence. */
- protected final Ipo ipo;
- /** The blender context. */
- protected final BlenderContext blenderContext;
- protected final ConstraintHelper constraintHelper;
-
- /**
- * This constructor creates the constraint instance.
- *
- * @param constraintStructure
- * the constraint's structure (bConstraint clss in blender 2.49).
- * @param ownerOMA
- * the old memory address of the constraint owner
- * @param influenceIpo
- * the ipo curve of the influence factor
- * @param blenderContext
- * the blender context
- * @throws BlenderFileException
- * this exception is thrown when the blender file is somehow
- * corrupted
- */
- public Constraint(Structure constraintStructure, Long ownerOMA, Ipo influenceIpo, BlenderContext blenderContext) throws BlenderFileException {
- this.blenderContext = blenderContext;
- this.name = constraintStructure.getFieldValue("name").toString();
- Pointer pData = (Pointer) constraintStructure.getFieldValue("data");
- if (pData.isNotNull()) {
- Structure data = pData.fetchData(blenderContext.getInputStream()).get(0);
- constraintDefinition = ConstraintDefinitionFactory.createConstraintDefinition(data, blenderContext);
- Pointer pTar = (Pointer)data.getFieldValue("tar");
- if(pTar!= null && pTar.isNotNull()) {
- this.targetOMA = pTar.getOldMemoryAddress();
- this.targetSpace = Space.valueOf(((Number) constraintStructure.getFieldValue("tarspace")).byteValue());
- Object subtargetValue = data.getFieldValue("subtarget");
- if(subtargetValue != null) {//not all constraint data have the subtarget field
- subtargetName = subtargetValue.toString();
- }
- }
- } else {
- //Null constraint has no data, so create it here
- constraintDefinition = ConstraintDefinitionFactory.createConstraintDefinition(null, blenderContext);
- }
- this.ownerSpace = Space.valueOf(((Number) constraintStructure.getFieldValue("ownspace")).byteValue());
- this.ipo = influenceIpo;
- this.ownerOMA = ownerOMA;
- this.constraintHelper = blenderContext.getHelper(ConstraintHelper.class);
- }
-
- /**
- * This method bakes the required sontraints into its owner. It checks if the constraint is invalid
- * or if it isn't yet baked. It also performs baking of its target constraints so that the proper baking
- * order is kept.
- */
- public void bake() {
- if(!this.validate()) {
- LOGGER.warning("The constraint " + name + " is invalid and will not be applied.");
- } else if(!baked) {
- if(targetOMA != null) {
- List targetConstraints = blenderContext.getConstraints(targetOMA);
- if(targetConstraints != null && targetConstraints.size() > 0) {
- LOGGER.log(Level.FINE, "Baking target constraints of constraint: {0}", name);
- for(Constraint targetConstraint : targetConstraints) {
- targetConstraint.bake();
- }
- }
- }
-
- LOGGER.log(Level.FINE, "Performing baking of constraint: {0}", name);
- this.performBakingOperation();
- baked = true;
- }
- }
-
- /**
- * Performs validation before baking. Checks factors that can prevent constraint from baking that could not be
- * checked during constraint loading.
- */
- protected abstract boolean validate();
-
- /**
- * This method should be overwridden and perform the baking opertion.
- */
- protected abstract void performBakingOperation();
-
- /**
- * This method prepares the tracks for both owner and parent. If either owner or parent have no track while its parent has -
- * the tracks are created. The tracks will not modify the owner/target movement but will be there ready for applying constraints.
- * For example if the owner is a spatial and has no animation but its parent is moving then the track is created for the owner
- * that will have non modifying values for translation, rotation and scale and will have the same amount of frames as its parent has.
- */
- protected abstract void prepareTracksForApplyingConstraints();
+ private static final Logger LOGGER = Logger.getLogger(Constraint.class.getName());
+
+ /** The name of this constraint. */
+ protected final String name;
+ /** Indicates if the constraint is already baked or not. */
+ protected boolean baked;
+
+ protected Space ownerSpace;
+ protected final ConstraintDefinition constraintDefinition;
+ protected Long ownerOMA;
+
+ protected Long targetOMA;
+ protected Space targetSpace;
+ protected String subtargetName;
+
+ /** The ipo object defining influence. */
+ protected final Ipo ipo;
+ /** The blender context. */
+ protected final BlenderContext blenderContext;
+ protected final ConstraintHelper constraintHelper;
+
+ /**
+ * This constructor creates the constraint instance.
+ *
+ * @param constraintStructure
+ * the constraint's structure (bConstraint clss in blender 2.49).
+ * @param ownerOMA
+ * the old memory address of the constraint owner
+ * @param influenceIpo
+ * the ipo curve of the influence factor
+ * @param blenderContext
+ * the blender context
+ * @throws BlenderFileException
+ * this exception is thrown when the blender file is somehow
+ * corrupted
+ */
+ public Constraint(Structure constraintStructure, Long ownerOMA, Ipo influenceIpo, BlenderContext blenderContext) throws BlenderFileException {
+ this.blenderContext = blenderContext;
+ this.name = constraintStructure.getFieldValue("name").toString();
+ Pointer pData = (Pointer) constraintStructure.getFieldValue("data");
+ if (pData.isNotNull()) {
+ Structure data = pData.fetchData(blenderContext.getInputStream()).get(0);
+ constraintDefinition = ConstraintDefinitionFactory.createConstraintDefinition(data, blenderContext);
+ Pointer pTar = (Pointer) data.getFieldValue("tar");
+ if (pTar != null && pTar.isNotNull()) {
+ this.targetOMA = pTar.getOldMemoryAddress();
+ this.targetSpace = Space.valueOf(((Number) constraintStructure.getFieldValue("tarspace")).byteValue());
+ Object subtargetValue = data.getFieldValue("subtarget");
+ if (subtargetValue != null) {// not all constraint data have the subtarget field
+ subtargetName = subtargetValue.toString();
+ }
+ }
+ } else {
+ // Null constraint has no data, so create it here
+ constraintDefinition = ConstraintDefinitionFactory.createConstraintDefinition(null, blenderContext);
+ }
+ this.ownerSpace = Space.valueOf(((Number) constraintStructure.getFieldValue("ownspace")).byteValue());
+ this.ipo = influenceIpo;
+ this.ownerOMA = ownerOMA;
+ this.constraintHelper = blenderContext.getHelper(ConstraintHelper.class);
+ }
+
+ /**
+ * This method bakes the required sontraints into its owner. It checks if the constraint is invalid
+ * or if it isn't yet baked. It also performs baking of its target constraints so that the proper baking
+ * order is kept.
+ */
+ public void bake() {
+ if (!this.validate()) {
+ LOGGER.warning("The constraint " + name + " is invalid and will not be applied.");
+ } else if (!baked) {
+ if (targetOMA != null) {
+ List targetConstraints = blenderContext.getConstraints(targetOMA);
+ if (targetConstraints != null && targetConstraints.size() > 0) {
+ LOGGER.log(Level.FINE, "Baking target constraints of constraint: {0}", name);
+ for (Constraint targetConstraint : targetConstraints) {
+ targetConstraint.bake();
+ }
+ }
+ }
+
+ LOGGER.log(Level.FINE, "Performing baking of constraint: {0}", name);
+ this.performBakingOperation();
+ baked = true;
+ }
+ }
+
+ /**
+ * Performs validation before baking. Checks factors that can prevent constraint from baking that could not be
+ * checked during constraint loading.
+ */
+ protected abstract boolean validate();
+
+ /**
+ * This method should be overwridden and perform the baking opertion.
+ */
+ protected abstract void performBakingOperation();
+
+ /**
+ * This method prepares the tracks for both owner and parent. If either owner or parent have no track while its parent has -
+ * the tracks are created. The tracks will not modify the owner/target movement but will be there ready for applying constraints.
+ * For example if the owner is a spatial and has no animation but its parent is moving then the track is created for the owner
+ * that will have non modifying values for translation, rotation and scale and will have the same amount of frames as its parent has.
+ */
+ protected abstract void prepareTracksForApplyingConstraints();
}
\ No newline at end of file
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintHelper.java b/engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintHelper.java
index af06ae44a..6ecef8cc1 100644
--- a/engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintHelper.java
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintHelper.java
@@ -32,444 +32,444 @@ import com.jme3.scene.plugins.blender.file.Structure;
* @author Marcin Roguski (Kaelthas)
*/
public class ConstraintHelper extends AbstractBlenderHelper {
- 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
- * @param fixUpAxis
- * a variable that indicates if the Y asxis is the UP axis or not
- */
- public ConstraintHelper(String blenderVersion, BlenderContext blenderContext, boolean fixUpAxis) {
- super(blenderVersion, fixUpAxis);
- }
-
- /**
- * This method reads constraints for for the given structure. The
- * constraints are loaded only once for object/bone.
- *
- * @param objectStructure
- * the structure we read constraint's for
- * @param blenderContext
- * the blender context
- * @throws BlenderFileException
- */
- public void loadConstraints(Structure objectStructure, BlenderContext blenderContext) throws BlenderFileException {
- LOGGER.fine("Loading constraints.");
- // reading influence ipos for the constraints
- IpoHelper ipoHelper = blenderContext.getHelper(IpoHelper.class);
- Map> constraintsIpos = new HashMap>();
- Pointer pActions = (Pointer) objectStructure.getFieldValue("action");
- if (pActions.isNotNull()) {
- List actions = pActions.fetchData(blenderContext.getInputStream());
- for (Structure action : actions) {
- Structure chanbase = (Structure) action.getFieldValue("chanbase");
- List actionChannels = chanbase.evaluateListBase(blenderContext);
- for (Structure actionChannel : actionChannels) {
- Map ipos = new HashMap();
- Structure constChannels = (Structure) actionChannel.getFieldValue("constraintChannels");
- List constraintChannels = constChannels.evaluateListBase(blenderContext);
- for (Structure constraintChannel : constraintChannels) {
- Pointer pIpo = (Pointer) constraintChannel.getFieldValue("ipo");
- if (pIpo.isNotNull()) {
- String constraintName = constraintChannel.getFieldValue("name").toString();
- Ipo ipo = ipoHelper.fromIpoStructure(pIpo.fetchData(blenderContext.getInputStream()).get(0), blenderContext);
- ipos.put(constraintName, ipo);
- }
- }
- String actionName = actionChannel.getFieldValue("name").toString();
- constraintsIpos.put(actionName, ipos);
- }
- }
- }
-
- //loading constraints connected with the object's bones
- Pointer pPose = (Pointer) objectStructure.getFieldValue("pose");
- if (pPose.isNotNull()) {
- List poseChannels = ((Structure) pPose.fetchData(blenderContext.getInputStream()).get(0).getFieldValue("chanbase")).evaluateListBase(blenderContext);
- for (Structure poseChannel : poseChannels) {
- List constraintsList = new ArrayList();
- Long boneOMA = Long.valueOf(((Pointer) poseChannel.getFieldValue("bone")).getOldMemoryAddress());
-
- //the name is read directly from structure because bone might not yet be loaded
- String name = blenderContext.getFileBlock(boneOMA).getStructure(blenderContext).getFieldValue("name").toString();
- List constraints = ((Structure) poseChannel.getFieldValue("constraints")).evaluateListBase(blenderContext);
- for (Structure constraint : constraints) {
- String constraintName = constraint.getFieldValue("name").toString();
- Map ipoMap = constraintsIpos.get(name);
- Ipo ipo = ipoMap==null ? null : ipoMap.get(constraintName);
- if (ipo == null) {
- float enforce = ((Number) constraint.getFieldValue("enforce")).floatValue();
- ipo = ipoHelper.fromValue(enforce);
- }
- constraintsList.add(new BoneConstraint(constraint, boneOMA, ipo, blenderContext));
- }
- blenderContext.addConstraints(boneOMA, constraintsList);
- }
- }
-
- //loading constraints connected with the object itself
- List constraints = ((Structure)objectStructure.getFieldValue("constraints")).evaluateListBase(blenderContext);
- if(constraints != null && constraints.size() > 0) {
- Pointer pData = (Pointer) objectStructure.getFieldValue("data");
- String dataType = pData.isNotNull() ? pData.fetchData(blenderContext.getInputStream()).get(0).getType() : null;
- List constraintsList = new ArrayList(constraints.size());
-
- for(Structure constraint : constraints) {
- String constraintName = constraint.getFieldValue("name").toString();
- String objectName = objectStructure.getName();
-
- Map objectConstraintsIpos = constraintsIpos.get(objectName);
- Ipo ipo = objectConstraintsIpos!=null ? objectConstraintsIpos.get(constraintName) : null;
- if (ipo == null) {
- float enforce = ((Number) constraint.getFieldValue("enforce")).floatValue();
- ipo = ipoHelper.fromValue(enforce);
- }
-
- constraintsList.add(this.getConstraint(dataType, constraint, objectStructure.getOldMemoryAddress(), ipo, blenderContext));
- }
- blenderContext.addConstraints(objectStructure.getOldMemoryAddress(), constraintsList);
- }
- }
-
- /**
- * This method creates a proper constraint object depending on the object's
- * data type. Supported data types: Mesh Armature Camera
- * Lamp Bone constraints are created in a different place.
- *
- * @param dataType
- * the type of the object's data
- * @param constraintStructure
- * the constraint structure
- * @param ownerOMA
- * the owner OMA
- * @param influenceIpo
- * the influence interpolation curve
- * @param blenderContext
- * the blender context
- * @return constraint object for the required type
- * @throws BlenderFileException
- * thrown when problems with blender file occured
- */
- private Constraint getConstraint(String dataType, Structure constraintStructure, Long ownerOMA, Ipo influenceIpo, BlenderContext blenderContext) throws BlenderFileException {
- if(dataType == null || "Mesh".equalsIgnoreCase(dataType) || "Camera".equalsIgnoreCase(dataType) || "Lamp".equalsIgnoreCase(dataType)) {
- return new SpatialConstraint(constraintStructure, ownerOMA, influenceIpo, blenderContext);
- } else if("Armature".equalsIgnoreCase(dataType)) {
- return new SkeletonConstraint(constraintStructure, ownerOMA, influenceIpo, blenderContext);
- } else {
- throw new IllegalArgumentException("Unsupported data type for applying constraints: " + dataType);
- }
- }
-
- /**
- * The method bakes all available and valid constraints.
- *
- * @param blenderContext
- * the blender context
- */
- public void bakeConstraints(BlenderContext blenderContext) {
- for(Constraint constraint : blenderContext.getAllConstraints()) {
- constraint.bake();
- }
- }
-
- /**
- * The method returns track for bone.
- *
- * @param bone
- * the bone
- * @param skeleton
- * the bone's skeleton
- * @param animation
- * the bone's animation
- * @return track for the given bone that was found among the given
- * animations or null if none is found
- */
- /*package*/ BoneTrack getTrack(Bone bone, Skeleton skeleton, Animation animation) {
- int boneIndex = skeleton.getBoneIndex(bone);
- for (Track track : animation.getTracks()) {
+ 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
+ * @param fixUpAxis
+ * a variable that indicates if the Y asxis is the UP axis or not
+ */
+ public ConstraintHelper(String blenderVersion, BlenderContext blenderContext, boolean fixUpAxis) {
+ super(blenderVersion, fixUpAxis);
+ }
+
+ /**
+ * This method reads constraints for for the given structure. The
+ * constraints are loaded only once for object/bone.
+ *
+ * @param objectStructure
+ * the structure we read constraint's for
+ * @param blenderContext
+ * the blender context
+ * @throws BlenderFileException
+ */
+ public void loadConstraints(Structure objectStructure, BlenderContext blenderContext) throws BlenderFileException {
+ LOGGER.fine("Loading constraints.");
+ // reading influence ipos for the constraints
+ IpoHelper ipoHelper = blenderContext.getHelper(IpoHelper.class);
+ Map> constraintsIpos = new HashMap>();
+ Pointer pActions = (Pointer) objectStructure.getFieldValue("action");
+ if (pActions.isNotNull()) {
+ List actions = pActions.fetchData(blenderContext.getInputStream());
+ for (Structure action : actions) {
+ Structure chanbase = (Structure) action.getFieldValue("chanbase");
+ List actionChannels = chanbase.evaluateListBase(blenderContext);
+ for (Structure actionChannel : actionChannels) {
+ Map ipos = new HashMap();
+ Structure constChannels = (Structure) actionChannel.getFieldValue("constraintChannels");
+ List constraintChannels = constChannels.evaluateListBase(blenderContext);
+ for (Structure constraintChannel : constraintChannels) {
+ Pointer pIpo = (Pointer) constraintChannel.getFieldValue("ipo");
+ if (pIpo.isNotNull()) {
+ String constraintName = constraintChannel.getFieldValue("name").toString();
+ Ipo ipo = ipoHelper.fromIpoStructure(pIpo.fetchData(blenderContext.getInputStream()).get(0), blenderContext);
+ ipos.put(constraintName, ipo);
+ }
+ }
+ String actionName = actionChannel.getFieldValue("name").toString();
+ constraintsIpos.put(actionName, ipos);
+ }
+ }
+ }
+
+ // loading constraints connected with the object's bones
+ Pointer pPose = (Pointer) objectStructure.getFieldValue("pose");
+ if (pPose.isNotNull()) {
+ List poseChannels = ((Structure) pPose.fetchData(blenderContext.getInputStream()).get(0).getFieldValue("chanbase")).evaluateListBase(blenderContext);
+ for (Structure poseChannel : poseChannels) {
+ List constraintsList = new ArrayList();
+ Long boneOMA = Long.valueOf(((Pointer) poseChannel.getFieldValue("bone")).getOldMemoryAddress());
+
+ // the name is read directly from structure because bone might not yet be loaded
+ String name = blenderContext.getFileBlock(boneOMA).getStructure(blenderContext).getFieldValue("name").toString();
+ List constraints = ((Structure) poseChannel.getFieldValue("constraints")).evaluateListBase(blenderContext);
+ for (Structure constraint : constraints) {
+ String constraintName = constraint.getFieldValue("name").toString();
+ Map ipoMap = constraintsIpos.get(name);
+ Ipo ipo = ipoMap == null ? null : ipoMap.get(constraintName);
+ if (ipo == null) {
+ float enforce = ((Number) constraint.getFieldValue("enforce")).floatValue();
+ ipo = ipoHelper.fromValue(enforce);
+ }
+ constraintsList.add(new BoneConstraint(constraint, boneOMA, ipo, blenderContext));
+ }
+ blenderContext.addConstraints(boneOMA, constraintsList);
+ }
+ }
+
+ // loading constraints connected with the object itself
+ List