* @author Marcin Roguski (Kaelthas)\r
*/\r
public class BlenderKey extends ModelKey {\r
- protected static final int DEFAULT_FPS = 25;\r
-\r
- /**\r
- * Animation definitions. The key is the object name that owns the animation. The value is a map between animation\r
- * name and its start and stop frames. Blender stores a pointer for animation within object. Therefore one object\r
- * can only have one animation at the time. We want to be able to switch between animations for one object so we\r
- * need to map the object name to animation names the object will use.\r
- */\r
- protected Map<String, Map<String, int[]>> animations;\r
- /**\r
- * FramesPerSecond parameter describe how many frames there are in each second. It allows to calculate the time\r
- * between the frames.\r
- */\r
- protected int fps = DEFAULT_FPS;\r
- /** Width of generated textures (in pixels). Blender uses 140x140 by default. */\r
- protected int generatedTextureWidth = 140;\r
- /** Height of generated textures (in pixels). Blender uses 140x140 by default. */\r
- protected int generatedTextureHeight = 140;\r
- /**\r
- * This variable is a bitwise flag of FeatureToLoad interface values; By default everything is being loaded.\r
- */\r
- protected int featuresToLoad = FeaturesToLoad.ALL;\r
- /** The root path for all the assets. */\r
- protected String assetRootPath;\r
- /** This variable indicate if Y axis is UP axis. If not then Z is up. By default set to true. */\r
- protected boolean fixUpAxis = true;\r
- /**\r
- * The name of world settings that the importer will use. If not set or specified name does not occur in the file\r
- * then the first world settings in the file will be used.\r
- */\r
- protected String usedWorld;\r
- /**\r
- * User's default material that is set fo objects that have no material definition in blender. The default value is\r
- * null. If the value is null the importer will use its own default material (gray color - like in blender).\r
- */\r
- protected Material defaultMaterial;\r
- /** Face cull mode. By default it is disabled. */\r
- protected FaceCullMode faceCullMode = FaceCullMode.Off;\r
- /** \r
- * Variable describes which layers will be loaded. N-th bit set means N-th layer will be loaded.\r
- * If set to -1 then the current layer will be loaded.\r
- */\r
- protected int layersToLoad = -1;\r
- \r
- /**\r
- * Constructor used by serialization mechanisms.\r
- */\r
- public BlenderKey() {}\r
-\r
- /**\r
- * Constructor. Creates a key for the given file name.\r
- * @param name\r
- * the name (path) of a file\r
- */\r
- public BlenderKey(String name) {\r
- super(name);\r
- }\r
-\r
- /**\r
- * This method adds an animation definition. If a definition already eixists in the key then it is replaced.\r
- * @param objectName\r
- * the name of animation's owner\r
- * @param name\r
- * the name of the animation\r
- * @param start\r
- * the start frame of the animation\r
- * @param stop\r
- * the stop frame of the animation\r
- */\r
- public synchronized void addAnimation(String objectName, String name, int start, int stop) {\r
- if(objectName == null) {\r
- throw new IllegalArgumentException("Object name cannot be null!");\r
- }\r
- if(name == null) {\r
- throw new IllegalArgumentException("Animation name cannot be null!");\r
- }\r
- if(start > stop) {\r
- throw new IllegalArgumentException("Start frame cannot be greater than stop frame!");\r
- }\r
- if(animations == null) {\r
- animations = new HashMap<String, Map<String, int[]>>();\r
- animations.put(objectName, new HashMap<String, int[]>());\r
- }\r
- Map<String, int[]> objectAnimations = animations.get(objectName);\r
- if(objectAnimations == null) {\r
- objectAnimations = new HashMap<String, int[]>();\r
- animations.put(objectName, objectAnimations);\r
- }\r
- objectAnimations.put(name, new int[] {start, stop});\r
- }\r
-\r
- /**\r
- * This method returns the animation frames boundaries.\r
- * @param objectName\r
- * the name of animation's owner\r
- * @param name\r
- * animation name\r
- * @return animation frame boundaries in a table [start, stop] or null if animation of the given name does not\r
- * exists\r
- */\r
- public int[] getAnimationFrames(String objectName, String name) {\r
- Map<String, int[]> objectAnimations = animations == null ? null : animations.get(objectName);\r
- int[] frames = objectAnimations == null ? null : objectAnimations.get(name);\r
- return frames == null ? null : frames.clone();\r
- }\r
-\r
- /**\r
- * This method returns the animation names for the given object name.\r
- * @param objectName\r
- * the name of the object\r
- * @return an array of animations for this object\r
- */\r
- public Set<String> getAnimationNames(String objectName) {\r
- Map<String, int[]> objectAnimations = animations == null ? null : animations.get(objectName);\r
- return objectAnimations == null ? null : objectAnimations.keySet();\r
- }\r
-\r
- /**\r
- * This method returns the animations map.\r
- * @return the animations map\r
- */\r
- public Map<String, Map<String, int[]>> getAnimations() {\r
- return animations;\r
- }\r
-\r
- /**\r
- * This method returns frames per second amount. The default value is BlenderKey.DEFAULT_FPS = 25.\r
- * @return the frames per second amount\r
- */\r
- public int getFps() {\r
- return fps;\r
- }\r
-\r
- /**\r
- * This method sets frames per second amount.\r
- * @param fps\r
- * the frames per second amount\r
- */\r
- public void setFps(int fps) {\r
- this.fps = fps;\r
- }\r
-\r
- /**\r
- * This method sets the width of generated texture (in pixels). By default the value is 140 px.\r
- * @param generatedTextureWidth\r
- * the width of generated texture\r
- */\r
- public void setGeneratedTextureWidth(int generatedTextureWidth) {\r
- this.generatedTextureWidth = generatedTextureWidth;\r
- }\r
-\r
- /**\r
- * This method returns the width of generated texture (in pixels). By default the value is 140 px.\r
- * @return the width of generated texture\r
- */\r
- public int getGeneratedTextureWidth() {\r
- return generatedTextureWidth;\r
- }\r
-\r
- /**\r
- * This method sets the height of generated texture (in pixels). By default the value is 140 px.\r
- * @param generatedTextureHeight\r
- * the height of generated texture\r
- */\r
- public void setGeneratedTextureHeight(int generatedTextureHeight) {\r
- this.generatedTextureHeight = generatedTextureHeight;\r
- }\r
-\r
- /**\r
- * This method returns the height of generated texture (in pixels). By default the value is 140 px.\r
- * @return the height of generated texture\r
- */\r
- public int getGeneratedTextureHeight() {\r
- return generatedTextureHeight;\r
- }\r
-\r
- /**\r
- * This method returns the face cull mode.\r
- * @return the face cull mode\r
- */\r
- public FaceCullMode getFaceCullMode() {\r
- return faceCullMode;\r
- }\r
-\r
- /**\r
- * This method sets the face cull mode.\r
- * @param faceCullMode\r
- * the face cull mode\r
- */\r
- public void setFaceCullMode(FaceCullMode faceCullMode) {\r
- this.faceCullMode = faceCullMode;\r
- }\r
-\r
- /**\r
- * This method sets layers to be loaded.\r
- * @param layersToLoad layers to be loaded\r
- */\r
- public void setLayersToLoad(int layersToLoad) {\r
- this.layersToLoad = layersToLoad;\r
- }\r
- \r
- /**\r
- * This method returns layers to be loaded.\r
- * @return layers to be loaded\r
- */\r
- public int getLayersToLoad() {\r
- return layersToLoad;\r
- }\r
- \r
- /**\r
- * This method sets the asset root path.\r
- * @param assetRootPath\r
- * the assets root path\r
- */\r
- public void setAssetRootPath(String assetRootPath) {\r
- this.assetRootPath = assetRootPath;\r
- }\r
-\r
- /**\r
- * This method returns the asset root path.\r
- * @return the asset root path\r
- */\r
- public String getAssetRootPath() {\r
- return assetRootPath;\r
- }\r
-\r
- /**\r
- * This method adds features to be loaded.\r
- * @param featuresToLoad\r
- * bitwise flag of FeaturesToLoad interface values\r
- */\r
- public void includeInLoading(int featuresToLoad) {\r
- this.featuresToLoad |= featuresToLoad;\r
- }\r
-\r
- /**\r
- * This method removes features from being loaded.\r
- * @param featuresToLoad\r
- * bitwise flag of FeaturesToLoad interface values\r
- */\r
- public void excludeFromLoading(int featuresNotToLoad) {\r
- this.featuresToLoad &= ~featuresNotToLoad;\r
- }\r
-\r
- /**\r
- * This method returns bitwise value of FeaturesToLoad interface value. It describes features that will be loaded by\r
- * the blender file loader.\r
- * @return features that will be loaded by the blender file loader\r
- */\r
- public int getFeaturesToLoad() {\r
- return featuresToLoad;\r
- }\r
-\r
- /**\r
- * This method creates an object where loading results will be stores. Only those features will be allowed to store\r
- * that were specified by features-to-load flag.\r
- * @return an object to store loading results\r
- */\r
- public LoadingResults prepareLoadingResults() {\r
- return new LoadingResults(featuresToLoad);\r
- }\r
-\r
- /**\r
- * 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\r
- * is up axis.\r
- * @param fixUpAxis\r
- * the up axis state variable\r
- */\r
- public void setFixUpAxis(boolean fixUpAxis) {\r
- this.fixUpAxis = fixUpAxis;\r
- }\r
-\r
- /**\r
- * This method returns the fix up axis state. If set to true then Y is up axis. Otherwise the up i Z axis. By\r
- * default Y is up axis.\r
- * @return the up axis state variable\r
- */\r
- public boolean isFixUpAxis() {\r
- return fixUpAxis;\r
- }\r
-\r
- /**\r
- * This mehtod sets the name of the WORLD data block taht should be used during file loading. By default the name is\r
- * 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\r
- * during loading (assumin any exists in the file).\r
- * @param usedWorld\r
- * the name of the WORLD block used during loading\r
- */\r
- public void setUsedWorld(String usedWorld) {\r
- this.usedWorld = usedWorld;\r
- }\r
-\r
- /**\r
- * This mehtod returns the name of the WORLD data block taht should be used during file loading.\r
- * @return the name of the WORLD block used during loading\r
- */\r
- public String getUsedWorld() {\r
- return usedWorld;\r
- }\r
-\r
- /**\r
- * This method sets the default material for objects.\r
- * @param defaultMaterial\r
- * the default material\r
- */\r
- public void setDefaultMaterial(Material defaultMaterial) {\r
- this.defaultMaterial = defaultMaterial;\r
- }\r
-\r
- /**\r
- * This method returns the default material.\r
- * @return the default material\r
- */\r
- public Material getDefaultMaterial() {\r
- return defaultMaterial;\r
- }\r
-\r
- @Override\r
- public void write(JmeExporter e) throws IOException {\r
- super.write(e);\r
- OutputCapsule oc = e.getCapsule(this);\r
- //saving animations\r
- oc.write(animations == null ? 0 : animations.size(), "anim-size", 0);\r
- if(animations != null) {\r
- int objectCounter = 0;\r
- for(Entry<String, Map<String, int[]>> animEntry : animations.entrySet()) {\r
- oc.write(animEntry.getKey(), "animated-object-" + objectCounter, null);\r
- int animsAmount = animEntry.getValue().size();\r
- oc.write(animsAmount, "anims-amount-" + objectCounter, 0);\r
- for(Entry<String, int[]> animsEntry : animEntry.getValue().entrySet()) {\r
- oc.write(animsEntry.getKey(), "anim-name-" + objectCounter, null);\r
- oc.write(animsEntry.getValue(), "anim-frames-" + objectCounter, null);\r
- }\r
- ++objectCounter;\r
- }\r
- }\r
- //saving the rest of the data\r
- oc.write(fps, "fps", DEFAULT_FPS);\r
- oc.write(featuresToLoad, "features-to-load", FeaturesToLoad.ALL);\r
- oc.write(assetRootPath, "asset-root-path", null);\r
- oc.write(fixUpAxis, "fix-up-axis", true);\r
- oc.write(usedWorld, "used-world", null);\r
- oc.write(defaultMaterial, "default-material", null);\r
- oc.write(faceCullMode, "face-cull-mode", FaceCullMode.Off);\r
- oc.write(layersToLoad, "layers-to-load", -1);\r
- }\r
-\r
- @Override\r
- public void read(JmeImporter e) throws IOException {\r
- super.read(e);\r
- InputCapsule ic = e.getCapsule(this);\r
- //reading animations\r
- int animSize = ic.readInt("anim-size", 0);\r
- if(animSize > 0) {\r
- if(animations == null) {\r
- animations = new HashMap<String, Map<String, int[]>>(animSize);\r
- } else {\r
- animations.clear();\r
- }\r
- for(int i = 0; i < animSize; ++i) {\r
- String objectName = ic.readString("animated-object-" + i, null);\r
- int animationsAmount = ic.readInt("anims-amount-" + i, 0);\r
- Map<String, int[]> objectAnimations = new HashMap<String, int[]>(animationsAmount);\r
- for(int j = 0; j < animationsAmount; ++j) {\r
- String animName = ic.readString("anim-name-" + i, null);\r
- int[] animFrames = ic.readIntArray("anim-frames-" + i, null);\r
- objectAnimations.put(animName, animFrames);\r
- }\r
- animations.put(objectName, objectAnimations);\r
- }\r
- }\r
-\r
- //reading the rest of the data\r
- fps = ic.readInt("fps", DEFAULT_FPS);\r
- featuresToLoad = ic.readInt("features-to-load", FeaturesToLoad.ALL);\r
- assetRootPath = ic.readString("asset-root-path", null);\r
- fixUpAxis = ic.readBoolean("fix-up-axis", true);\r
- usedWorld = ic.readString("used-world", null);\r
- defaultMaterial = (Material)ic.readSavable("default-material", null);\r
- faceCullMode = ic.readEnum("face-cull-mode", FaceCullMode.class, FaceCullMode.Off);\r
- layersToLoad = ic.readInt("layers-to=load", -1);\r
- }\r
-\r
- @Override\r
- public int hashCode() {\r
- final int prime = 31;\r
- int result = super.hashCode();\r
- result = prime * result + (animations == null ? 0 : animations.hashCode());\r
- result = prime * result + (assetRootPath == null ? 0 : assetRootPath.hashCode());\r
- result = prime * result + (defaultMaterial == null ? 0 : defaultMaterial.hashCode());\r
- result = prime * result + (faceCullMode == null ? 0 : faceCullMode.hashCode());\r
- result = prime * result + featuresToLoad;\r
- result = prime * result + (fixUpAxis ? 1231 : 1237);\r
- result = prime * result + fps;\r
- result = prime * result + generatedTextureHeight;\r
- result = prime * result + generatedTextureWidth;\r
- result = prime * result + layersToLoad;\r
- result = prime * result + (usedWorld == null ? 0 : usedWorld.hashCode());\r
- return result;\r
- }\r
-\r
- @Override\r
- public boolean equals(Object obj) {\r
- if (this == obj) {\r
- return true;\r
- }\r
- if (!super.equals(obj)) {\r
- return false;\r
- }\r
- if (this.getClass() != obj.getClass()) {\r
- return false;\r
- }\r
- BlenderKey other = (BlenderKey) obj;\r
- if (animations == null) {\r
- if (other.animations != null) {\r
- return false;\r
- }\r
- } else if (!animations.equals(other.animations)) {\r
- return false;\r
- }\r
- if (assetRootPath == null) {\r
- if (other.assetRootPath != null) {\r
- return false;\r
- }\r
- } else if (!assetRootPath.equals(other.assetRootPath)) {\r
- return false;\r
- }\r
- if (defaultMaterial == null) {\r
- if (other.defaultMaterial != null) {\r
- return false;\r
- }\r
- } else if (!defaultMaterial.equals(other.defaultMaterial)) {\r
- return false;\r
- }\r
- if (faceCullMode != other.faceCullMode) {\r
- return false;\r
- }\r
- if (featuresToLoad != other.featuresToLoad) {\r
- return false;\r
- }\r
- if (fixUpAxis != other.fixUpAxis) {\r
- return false;\r
- }\r
- if (fps != other.fps) {\r
- return false;\r
- }\r
- if (generatedTextureHeight != other.generatedTextureHeight) {\r
- return false;\r
- }\r
- if (generatedTextureWidth != other.generatedTextureWidth) {\r
- return false;\r
- }\r
- if (layersToLoad != other.layersToLoad) {\r
- return false;\r
- }\r
- if (usedWorld == null) {\r
- if (other.usedWorld != null) {\r
- return false;\r
- }\r
- } else if (!usedWorld.equals(other.usedWorld)) {\r
- return false;\r
- }\r
- return true;\r
- }\r
-\r
- /**\r
- * This interface describes the features of the scene that are to be loaded.\r
- * @author Marcin Roguski (Kaelthas)\r
- */\r
- public static interface FeaturesToLoad {\r
- int SCENES = 0x0000FFFF;\r
- int OBJECTS = 0x0000000B;\r
- int ANIMATIONS = 0x00000004;\r
- int MATERIALS = 0x00000003;\r
- int TEXTURES = 0x00000001;\r
- int CAMERAS = 0x00000020;\r
- int LIGHTS = 0x00000010;\r
- int ALL = 0xFFFFFFFF;\r
- }\r
-\r
- /**\r
- * This class holds the loading results according to the given loading flag.\r
- * @author Marcin Roguski (Kaelthas)\r
- */\r
- public static class LoadingResults extends Spatial {\r
- /** Bitwise mask of features that are to be loaded. */\r
- private final int featuresToLoad;\r
- /** The scenes from the file. */\r
- private List<Node> scenes;\r
- /** Objects from all scenes. */\r
- private List<Node> objects;\r
- /** Materials from all objects. */\r
- private List<Material> materials;\r
- /** Textures from all objects. */\r
- private List<Texture> textures;\r
- /** Animations of all objects. */\r
- private List<AnimData> animations;\r
- /** All cameras from the file. */\r
- private List<Camera> cameras;\r
- /** All lights from the file. */\r
- private List<Light> lights;\r
-\r
- /**\r
- * Private constructor prevents users to create an instance of this class from outside the\r
- * @param featuresToLoad\r
- * bitwise mask of features that are to be loaded\r
- * @see FeaturesToLoad FeaturesToLoad\r
- */\r
- private LoadingResults(int featuresToLoad) {\r
- this.featuresToLoad = featuresToLoad;\r
- if((featuresToLoad & FeaturesToLoad.SCENES) != 0) {\r
- scenes = new ArrayList<Node>();\r
- }\r
- if((featuresToLoad & FeaturesToLoad.OBJECTS) != 0) {\r
- objects = new ArrayList<Node>();\r
- if((featuresToLoad & FeaturesToLoad.MATERIALS) != 0) {\r
- materials = new ArrayList<Material>();\r
- if((featuresToLoad & FeaturesToLoad.TEXTURES) != 0) {\r
- textures = new ArrayList<Texture>();\r
- }\r
- }\r
- if((featuresToLoad & FeaturesToLoad.ANIMATIONS) != 0) {\r
- animations = new ArrayList<AnimData>();\r
- }\r
- }\r
- if((featuresToLoad & FeaturesToLoad.CAMERAS) != 0) {\r
- cameras = new ArrayList<Camera>();\r
- }\r
- if((featuresToLoad & FeaturesToLoad.LIGHTS) != 0) {\r
- lights = new ArrayList<Light>();\r
- }\r
- }\r
-\r
- /**\r
- * This method returns a bitwise flag describing what features of the blend file will be included in the result.\r
- * @return bitwise mask of features that are to be loaded\r
- * @see FeaturesToLoad FeaturesToLoad\r
- */\r
- public int getLoadedFeatures() {\r
- return featuresToLoad;\r
- }\r
-\r
- /**\r
- * This method adds a scene to the result set.\r
- * @param scene\r
- * scene to be added to the result set\r
- */\r
- public void addScene(Node scene) {\r
- if(scenes != null) {\r
- scenes.add(scene);\r
- }\r
- }\r
-\r
- /**\r
- * This method adds an object to the result set.\r
- * @param object\r
- * object to be added to the result set\r
- */\r
- public void addObject(Node object) {\r
- if(objects != null) {\r
- objects.add(object);\r
- }\r
- }\r
-\r
- /**\r
- * This method adds a material to the result set.\r
- * @param material\r
- * material to be added to the result set\r
- */\r
- public void addMaterial(Material material) {\r
- if(materials != null) {\r
- materials.add(material);\r
- }\r
- }\r
-\r
- /**\r
- * This method adds a texture to the result set.\r
- * @param texture\r
- * texture to be added to the result set\r
- */\r
- public void addTexture(Texture texture) {\r
- if(textures != null) {\r
- textures.add(texture);\r
- }\r
- }\r
-\r
- /**\r
- * This method adds a camera to the result set.\r
- * @param camera\r
- * camera to be added to the result set\r
- */\r
- public void addCamera(Camera camera) {\r
- if(cameras != null) {\r
- cameras.add(camera);\r
- }\r
- }\r
-\r
- /**\r
- * This method adds a light to the result set.\r
- * @param light\r
- * light to be added to the result set\r
- */\r
- @Override\r
- public void addLight(Light light) {\r
- if(lights != null) {\r
- lights.add(light);\r
- }\r
- }\r
-\r
- /**\r
- * This method returns all loaded scenes.\r
- * @return all loaded scenes\r
- */\r
- public List<Node> getScenes() {\r
- return scenes;\r
- }\r
-\r
- /**\r
- * This method returns all loaded objects.\r
- * @return all loaded objects\r
- */\r
- public List<Node> getObjects() {\r
- return objects;\r
- }\r
-\r
- /**\r
- * This method returns all loaded materials.\r
- * @return all loaded materials\r
- */\r
- public List<Material> getMaterials() {\r
- return materials;\r
- }\r
-\r
- /**\r
- * This method returns all loaded textures.\r
- * @return all loaded textures\r
- */\r
- public List<Texture> getTextures() {\r
- return textures;\r
- }\r
-\r
- /**\r
- * This method returns all loaded animations.\r
- * @return all loaded animations\r
- */\r
- public List<AnimData> getAnimations() {\r
- return animations;\r
- }\r
-\r
- /**\r
- * This method returns all loaded cameras.\r
- * @return all loaded cameras\r
- */\r
- public List<Camera> getCameras() {\r
- return cameras;\r
- }\r
-\r
- /**\r
- * This method returns all loaded lights.\r
- * @return all loaded lights\r
- */\r
- public List<Light> getLights() {\r
- return lights;\r
- }\r
-\r
- @Override\r
- public int collideWith(Collidable other, CollisionResults results) throws UnsupportedCollisionException {\r
- return 0;\r
- }\r
-\r
- @Override\r
- public void updateModelBound() {}\r
-\r
- @Override\r
- public void setModelBound(BoundingVolume modelBound) {}\r
-\r
- @Override\r
- public int getVertexCount() {\r
- return 0;\r
- }\r
-\r
- @Override\r
- public int getTriangleCount() {\r
- return 0;\r
- }\r
-\r
- @Override\r
- public Spatial deepClone() {\r
- return null;\r
- }\r
-\r
- @Override\r
- public void depthFirstTraversal(SceneGraphVisitor visitor) {}\r
-\r
- @Override\r
- protected void breadthFirstTraversal(SceneGraphVisitor visitor, Queue<Spatial> queue) {}\r
- }\r
-\r
- /**\r
- * The WORLD file block contains various data that could be added to the scene. The contained data includes: ambient\r
- * light.\r
- * @author Marcin Roguski (Kaelthas)\r
- */\r
- public static class WorldData {\r
- /** The ambient light. */\r
- private AmbientLight ambientLight;\r
-\r
- /**\r
- * This method returns the world's ambient light.\r
- * @return the world's ambient light\r
- */\r
- public AmbientLight getAmbientLight() {\r
- return ambientLight;\r
- }\r
-\r
- /**\r
- * This method sets the world's ambient light.\r
- * @param ambientLight\r
- * the world's ambient light\r
- */\r
- public void setAmbientLight(AmbientLight ambientLight) {\r
- this.ambientLight = ambientLight;\r
- }\r
- }\r
+\r
+ protected static final int DEFAULT_FPS = 25;\r
+ /**\r
+ * Animation definitions. The key is the object name that owns the animation. The value is a map between animation\r
+ * name and its start and stop frames. Blender stores a pointer for animation within object. Therefore one object\r
+ * can only have one animation at the time. We want to be able to switch between animations for one object so we\r
+ * need to map the object name to animation names the object will use.\r
+ */\r
+ protected Map<String, Map<String, int[]>> animations;\r
+ /**\r
+ * FramesPerSecond parameter describe how many frames there are in each second. It allows to calculate the time\r
+ * between the frames.\r
+ */\r
+ protected int fps = DEFAULT_FPS;\r
+ /** Width of generated textures (in pixels). Blender uses 140x140 by default. */\r
+ protected int generatedTextureWidth = 140;\r
+ /** Height of generated textures (in pixels). Blender uses 140x140 by default. */\r
+ protected int generatedTextureHeight = 140;\r
+ /**\r
+ * This variable is a bitwise flag of FeatureToLoad interface values; By default everything is being loaded.\r
+ */\r
+ protected int featuresToLoad = FeaturesToLoad.ALL;\r
+ /** The root path for all the assets. */\r
+ protected String assetRootPath;\r
+ /** This variable indicate if Y axis is UP axis. If not then Z is up. By default set to true. */\r
+ protected boolean fixUpAxis = true;\r
+ /**\r
+ * The name of world settings that the importer will use. If not set or specified name does not occur in the file\r
+ * then the first world settings in the file will be used.\r
+ */\r
+ protected String usedWorld;\r
+ /**\r
+ * User's default material that is set fo objects that have no material definition in blender. The default value is\r
+ * null. If the value is null the importer will use its own default material (gray color - like in blender).\r
+ */\r
+ protected Material defaultMaterial;\r
+ /** Face cull mode. By default it is disabled. */\r
+ protected FaceCullMode faceCullMode = FaceCullMode.Off;\r
+ /** \r
+ * Variable describes which layers will be loaded. N-th bit set means N-th layer will be loaded.\r
+ * If set to -1 then the current layer will be loaded.\r
+ */\r
+ protected int layersToLoad = -1;\r
+\r
+ /**\r
+ * Constructor used by serialization mechanisms.\r
+ */\r
+ public BlenderKey() {\r
+ }\r
+\r
+ /**\r
+ * Constructor. Creates a key for the given file name.\r
+ * @param name\r
+ * the name (path) of a file\r
+ */\r
+ public BlenderKey(String name) {\r
+ super(name);\r
+ }\r
+\r
+ /**\r
+ * This method adds an animation definition. If a definition already eixists in the key then it is replaced.\r
+ * @param objectName\r
+ * the name of animation's owner\r
+ * @param name\r
+ * the name of the animation\r
+ * @param start\r
+ * the start frame of the animation\r
+ * @param stop\r
+ * the stop frame of the animation\r
+ */\r
+ public synchronized void addAnimation(String objectName, String name, int start, int stop) {\r
+ if (objectName == null) {\r
+ throw new IllegalArgumentException("Object name cannot be null!");\r
+ }\r
+ if (name == null) {\r
+ throw new IllegalArgumentException("Animation name cannot be null!");\r
+ }\r
+ if (start > stop) {\r
+ throw new IllegalArgumentException("Start frame cannot be greater than stop frame!");\r
+ }\r
+ if (animations == null) {\r
+ animations = new HashMap<String, Map<String, int[]>>();\r
+ animations.put(objectName, new HashMap<String, int[]>());\r
+ }\r
+ Map<String, int[]> objectAnimations = animations.get(objectName);\r
+ if (objectAnimations == null) {\r
+ objectAnimations = new HashMap<String, int[]>();\r
+ animations.put(objectName, objectAnimations);\r
+ }\r
+ objectAnimations.put(name, new int[]{start, stop});\r
+ }\r
+\r
+ /**\r
+ * This method returns the animation frames boundaries.\r
+ * @param objectName\r
+ * the name of animation's owner\r
+ * @param name\r
+ * animation name\r
+ * @return animation frame boundaries in a table [start, stop] or null if animation of the given name does not\r
+ * exists\r
+ */\r
+ public int[] getAnimationFrames(String objectName, String name) {\r
+ Map<String, int[]> objectAnimations = animations == null ? null : animations.get(objectName);\r
+ int[] frames = objectAnimations == null ? null : objectAnimations.get(name);\r
+ return frames == null ? null : frames.clone();\r
+ }\r
+\r
+ /**\r
+ * This method returns the animation names for the given object name.\r
+ * @param objectName\r
+ * the name of the object\r
+ * @return an array of animations for this object\r
+ */\r
+ public Set<String> getAnimationNames(String objectName) {\r
+ Map<String, int[]> objectAnimations = animations == null ? null : animations.get(objectName);\r
+ return objectAnimations == null ? null : objectAnimations.keySet();\r
+ }\r
+\r
+ /**\r
+ * This method returns the animations map.\r
+ * @return the animations map\r
+ */\r
+ public Map<String, Map<String, int[]>> getAnimations() {\r
+ return animations;\r
+ }\r
+\r
+ /**\r
+ * This method returns frames per second amount. The default value is BlenderKey.DEFAULT_FPS = 25.\r
+ * @return the frames per second amount\r
+ */\r
+ public int getFps() {\r
+ return fps;\r
+ }\r
+\r
+ /**\r
+ * This method sets frames per second amount.\r
+ * @param fps\r
+ * the frames per second amount\r
+ */\r
+ public void setFps(int fps) {\r
+ this.fps = fps;\r
+ }\r
+\r
+ /**\r
+ * This method sets the width of generated texture (in pixels). By default the value is 140 px.\r
+ * @param generatedTextureWidth\r
+ * the width of generated texture\r
+ */\r
+ public void setGeneratedTextureWidth(int generatedTextureWidth) {\r
+ this.generatedTextureWidth = generatedTextureWidth;\r
+ }\r
+\r
+ /**\r
+ * This method returns the width of generated texture (in pixels). By default the value is 140 px.\r
+ * @return the width of generated texture\r
+ */\r
+ public int getGeneratedTextureWidth() {\r
+ return generatedTextureWidth;\r
+ }\r
+\r
+ /**\r
+ * This method sets the height of generated texture (in pixels). By default the value is 140 px.\r
+ * @param generatedTextureHeight\r
+ * the height of generated texture\r
+ */\r
+ public void setGeneratedTextureHeight(int generatedTextureHeight) {\r
+ this.generatedTextureHeight = generatedTextureHeight;\r
+ }\r
+\r
+ /**\r
+ * This method returns the height of generated texture (in pixels). By default the value is 140 px.\r
+ * @return the height of generated texture\r
+ */\r
+ public int getGeneratedTextureHeight() {\r
+ return generatedTextureHeight;\r
+ }\r
+\r
+ /**\r
+ * This method returns the face cull mode.\r
+ * @return the face cull mode\r
+ */\r
+ public FaceCullMode getFaceCullMode() {\r
+ return faceCullMode;\r
+ }\r
+\r
+ /**\r
+ * This method sets the face cull mode.\r
+ * @param faceCullMode\r
+ * the face cull mode\r
+ */\r
+ public void setFaceCullMode(FaceCullMode faceCullMode) {\r
+ this.faceCullMode = faceCullMode;\r
+ }\r
+\r
+ /**\r
+ * This method sets layers to be loaded.\r
+ * @param layersToLoad layers to be loaded\r
+ */\r
+ public void setLayersToLoad(int layersToLoad) {\r
+ this.layersToLoad = layersToLoad;\r
+ }\r
+\r
+ /**\r
+ * This method returns layers to be loaded.\r
+ * @return layers to be loaded\r
+ */\r
+ public int getLayersToLoad() {\r
+ return layersToLoad;\r
+ }\r
+\r
+ /**\r
+ * This method sets the asset root path.\r
+ * @param assetRootPath\r
+ * the assets root path\r
+ */\r
+ public void setAssetRootPath(String assetRootPath) {\r
+ this.assetRootPath = assetRootPath;\r
+ }\r
+\r
+ /**\r
+ * This method returns the asset root path.\r
+ * @return the asset root path\r
+ */\r
+ public String getAssetRootPath() {\r
+ return assetRootPath;\r
+ }\r
+\r
+ /**\r
+ * This method adds features to be loaded.\r
+ * @param featuresToLoad\r
+ * bitwise flag of FeaturesToLoad interface values\r
+ */\r
+ public void includeInLoading(int featuresToLoad) {\r
+ this.featuresToLoad |= featuresToLoad;\r
+ }\r
+\r
+ /**\r
+ * This method removes features from being loaded.\r
+ * @param featuresToLoad\r
+ * bitwise flag of FeaturesToLoad interface values\r
+ */\r
+ public void excludeFromLoading(int featuresNotToLoad) {\r
+ this.featuresToLoad &= ~featuresNotToLoad;\r
+ }\r
+\r
+ /**\r
+ * This method returns bitwise value of FeaturesToLoad interface value. It describes features that will be loaded by\r
+ * the blender file loader.\r
+ * @return features that will be loaded by the blender file loader\r
+ */\r
+ public int getFeaturesToLoad() {\r
+ return featuresToLoad;\r
+ }\r
+\r
+ /**\r
+ * This method creates an object where loading results will be stores. Only those features will be allowed to store\r
+ * that were specified by features-to-load flag.\r
+ * @return an object to store loading results\r
+ */\r
+ public LoadingResults prepareLoadingResults() {\r
+ return new LoadingResults(featuresToLoad);\r
+ }\r
+\r
+ /**\r
+ * 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\r
+ * is up axis.\r
+ * @param fixUpAxis\r
+ * the up axis state variable\r
+ */\r
+ public void setFixUpAxis(boolean fixUpAxis) {\r
+ this.fixUpAxis = fixUpAxis;\r
+ }\r
+\r
+ /**\r
+ * This method returns the fix up axis state. If set to true then Y is up axis. Otherwise the up i Z axis. By\r
+ * default Y is up axis.\r
+ * @return the up axis state variable\r
+ */\r
+ public boolean isFixUpAxis() {\r
+ return fixUpAxis;\r
+ }\r
+\r
+ /**\r
+ * This mehtod sets the name of the WORLD data block taht should be used during file loading. By default the name is\r
+ * 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\r
+ * during loading (assumin any exists in the file).\r
+ * @param usedWorld\r
+ * the name of the WORLD block used during loading\r
+ */\r
+ public void setUsedWorld(String usedWorld) {\r
+ this.usedWorld = usedWorld;\r
+ }\r
+\r
+ /**\r
+ * This mehtod returns the name of the WORLD data block taht should be used during file loading.\r
+ * @return the name of the WORLD block used during loading\r
+ */\r
+ public String getUsedWorld() {\r
+ return usedWorld;\r
+ }\r
+\r
+ /**\r
+ * This method sets the default material for objects.\r
+ * @param defaultMaterial\r
+ * the default material\r
+ */\r
+ public void setDefaultMaterial(Material defaultMaterial) {\r
+ this.defaultMaterial = defaultMaterial;\r
+ }\r
+\r
+ /**\r
+ * This method returns the default material.\r
+ * @return the default material\r
+ */\r
+ public Material getDefaultMaterial() {\r
+ return defaultMaterial;\r
+ }\r
+\r
+ @Override\r
+ public void write(JmeExporter e) throws IOException {\r
+ super.write(e);\r
+ OutputCapsule oc = e.getCapsule(this);\r
+ //saving animations\r
+ oc.write(animations == null ? 0 : animations.size(), "anim-size", 0);\r
+ if (animations != null) {\r
+ int objectCounter = 0;\r
+ for (Entry<String, Map<String, int[]>> animEntry : animations.entrySet()) {\r
+ oc.write(animEntry.getKey(), "animated-object-" + objectCounter, null);\r
+ int animsAmount = animEntry.getValue().size();\r
+ oc.write(animsAmount, "anims-amount-" + objectCounter, 0);\r
+ for (Entry<String, int[]> animsEntry : animEntry.getValue().entrySet()) {\r
+ oc.write(animsEntry.getKey(), "anim-name-" + objectCounter, null);\r
+ oc.write(animsEntry.getValue(), "anim-frames-" + objectCounter, null);\r
+ }\r
+ ++objectCounter;\r
+ }\r
+ }\r
+ //saving the rest of the data\r
+ oc.write(fps, "fps", DEFAULT_FPS);\r
+ oc.write(featuresToLoad, "features-to-load", FeaturesToLoad.ALL);\r
+ oc.write(assetRootPath, "asset-root-path", null);\r
+ oc.write(fixUpAxis, "fix-up-axis", true);\r
+ oc.write(usedWorld, "used-world", null);\r
+ oc.write(defaultMaterial, "default-material", null);\r
+ oc.write(faceCullMode, "face-cull-mode", FaceCullMode.Off);\r
+ oc.write(layersToLoad, "layers-to-load", -1);\r
+ }\r
+\r
+ @Override\r
+ public void read(JmeImporter e) throws IOException {\r
+ super.read(e);\r
+ InputCapsule ic = e.getCapsule(this);\r
+ //reading animations\r
+ int animSize = ic.readInt("anim-size", 0);\r
+ if (animSize > 0) {\r
+ if (animations == null) {\r
+ animations = new HashMap<String, Map<String, int[]>>(animSize);\r
+ } else {\r
+ animations.clear();\r
+ }\r
+ for (int i = 0; i < animSize; ++i) {\r
+ String objectName = ic.readString("animated-object-" + i, null);\r
+ int animationsAmount = ic.readInt("anims-amount-" + i, 0);\r
+ Map<String, int[]> objectAnimations = new HashMap<String, int[]>(animationsAmount);\r
+ for (int j = 0; j < animationsAmount; ++j) {\r
+ String animName = ic.readString("anim-name-" + i, null);\r
+ int[] animFrames = ic.readIntArray("anim-frames-" + i, null);\r
+ objectAnimations.put(animName, animFrames);\r
+ }\r
+ animations.put(objectName, objectAnimations);\r
+ }\r
+ }\r
+\r
+ //reading the rest of the data\r
+ fps = ic.readInt("fps", DEFAULT_FPS);\r
+ featuresToLoad = ic.readInt("features-to-load", FeaturesToLoad.ALL);\r
+ assetRootPath = ic.readString("asset-root-path", null);\r
+ fixUpAxis = ic.readBoolean("fix-up-axis", true);\r
+ usedWorld = ic.readString("used-world", null);\r
+ defaultMaterial = (Material) ic.readSavable("default-material", null);\r
+ faceCullMode = ic.readEnum("face-cull-mode", FaceCullMode.class, FaceCullMode.Off);\r
+ layersToLoad = ic.readInt("layers-to=load", -1);\r
+ }\r
+\r
+ @Override\r
+ public int hashCode() {\r
+ final int prime = 31;\r
+ int result = super.hashCode();\r
+ result = prime * result + (animations == null ? 0 : animations.hashCode());\r
+ result = prime * result + (assetRootPath == null ? 0 : assetRootPath.hashCode());\r
+ result = prime * result + (defaultMaterial == null ? 0 : defaultMaterial.hashCode());\r
+ result = prime * result + (faceCullMode == null ? 0 : faceCullMode.hashCode());\r
+ result = prime * result + featuresToLoad;\r
+ result = prime * result + (fixUpAxis ? 1231 : 1237);\r
+ result = prime * result + fps;\r
+ result = prime * result + generatedTextureHeight;\r
+ result = prime * result + generatedTextureWidth;\r
+ result = prime * result + layersToLoad;\r
+ result = prime * result + (usedWorld == null ? 0 : usedWorld.hashCode());\r
+ return result;\r
+ }\r
+\r
+ @Override\r
+ public boolean equals(Object obj) {\r
+ if (this == obj) {\r
+ return true;\r
+ }\r
+ if (!super.equals(obj)) {\r
+ return false;\r
+ }\r
+ if (this.getClass() != obj.getClass()) {\r
+ return false;\r
+ }\r
+ BlenderKey other = (BlenderKey) obj;\r
+ if (animations == null) {\r
+ if (other.animations != null) {\r
+ return false;\r
+ }\r
+ } else if (!animations.equals(other.animations)) {\r
+ return false;\r
+ }\r
+ if (assetRootPath == null) {\r
+ if (other.assetRootPath != null) {\r
+ return false;\r
+ }\r
+ } else if (!assetRootPath.equals(other.assetRootPath)) {\r
+ return false;\r
+ }\r
+ if (defaultMaterial == null) {\r
+ if (other.defaultMaterial != null) {\r
+ return false;\r
+ }\r
+ } else if (!defaultMaterial.equals(other.defaultMaterial)) {\r
+ return false;\r
+ }\r
+ if (faceCullMode != other.faceCullMode) {\r
+ return false;\r
+ }\r
+ if (featuresToLoad != other.featuresToLoad) {\r
+ return false;\r
+ }\r
+ if (fixUpAxis != other.fixUpAxis) {\r
+ return false;\r
+ }\r
+ if (fps != other.fps) {\r
+ return false;\r
+ }\r
+ if (generatedTextureHeight != other.generatedTextureHeight) {\r
+ return false;\r
+ }\r
+ if (generatedTextureWidth != other.generatedTextureWidth) {\r
+ return false;\r
+ }\r
+ if (layersToLoad != other.layersToLoad) {\r
+ return false;\r
+ }\r
+ if (usedWorld == null) {\r
+ if (other.usedWorld != null) {\r
+ return false;\r
+ }\r
+ } else if (!usedWorld.equals(other.usedWorld)) {\r
+ return false;\r
+ }\r
+ return true;\r
+ }\r
+\r
+ /**\r
+ * This interface describes the features of the scene that are to be loaded.\r
+ * @author Marcin Roguski (Kaelthas)\r
+ */\r
+ public static interface FeaturesToLoad {\r
+\r
+ int SCENES = 0x0000FFFF;\r
+ int OBJECTS = 0x0000000B;\r
+ int ANIMATIONS = 0x00000004;\r
+ int MATERIALS = 0x00000003;\r
+ int TEXTURES = 0x00000001;\r
+ int CAMERAS = 0x00000020;\r
+ int LIGHTS = 0x00000010;\r
+ int ALL = 0xFFFFFFFF;\r
+ }\r
+\r
+ /**\r
+ * This class holds the loading results according to the given loading flag.\r
+ * @author Marcin Roguski (Kaelthas)\r
+ */\r
+ public static class LoadingResults extends Spatial {\r
+\r
+ /** Bitwise mask of features that are to be loaded. */\r
+ private final int featuresToLoad;\r
+ /** The scenes from the file. */\r
+ private List<Node> scenes;\r
+ /** Objects from all scenes. */\r
+ private List<Node> objects;\r
+ /** Materials from all objects. */\r
+ private List<Material> materials;\r
+ /** Textures from all objects. */\r
+ private List<Texture> textures;\r
+ /** Animations of all objects. */\r
+ private List<AnimData> animations;\r
+ /** All cameras from the file. */\r
+ private List<Camera> cameras;\r
+ /** All lights from the file. */\r
+ private List<Light> lights;\r
+\r
+ /**\r
+ * Private constructor prevents users to create an instance of this class from outside the\r
+ * @param featuresToLoad\r
+ * bitwise mask of features that are to be loaded\r
+ * @see FeaturesToLoad FeaturesToLoad\r
+ */\r
+ private LoadingResults(int featuresToLoad) {\r
+ this.featuresToLoad = featuresToLoad;\r
+ if ((featuresToLoad & FeaturesToLoad.SCENES) != 0) {\r
+ scenes = new ArrayList<Node>();\r
+ }\r
+ if ((featuresToLoad & FeaturesToLoad.OBJECTS) != 0) {\r
+ objects = new ArrayList<Node>();\r
+ if ((featuresToLoad & FeaturesToLoad.MATERIALS) != 0) {\r
+ materials = new ArrayList<Material>();\r
+ if ((featuresToLoad & FeaturesToLoad.TEXTURES) != 0) {\r
+ textures = new ArrayList<Texture>();\r
+ }\r
+ }\r
+ if ((featuresToLoad & FeaturesToLoad.ANIMATIONS) != 0) {\r
+ animations = new ArrayList<AnimData>();\r
+ }\r
+ }\r
+ if ((featuresToLoad & FeaturesToLoad.CAMERAS) != 0) {\r
+ cameras = new ArrayList<Camera>();\r
+ }\r
+ if ((featuresToLoad & FeaturesToLoad.LIGHTS) != 0) {\r
+ lights = new ArrayList<Light>();\r
+ }\r
+ }\r
+\r
+ /**\r
+ * This method returns a bitwise flag describing what features of the blend file will be included in the result.\r
+ * @return bitwise mask of features that are to be loaded\r
+ * @see FeaturesToLoad FeaturesToLoad\r
+ */\r
+ public int getLoadedFeatures() {\r
+ return featuresToLoad;\r
+ }\r
+\r
+ /**\r
+ * This method adds a scene to the result set.\r
+ * @param scene\r
+ * scene to be added to the result set\r
+ */\r
+ public void addScene(Node scene) {\r
+ if (scenes != null) {\r
+ scenes.add(scene);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * This method adds an object to the result set.\r
+ * @param object\r
+ * object to be added to the result set\r
+ */\r
+ public void addObject(Node object) {\r
+ if (objects != null) {\r
+ objects.add(object);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * This method adds a material to the result set.\r
+ * @param material\r
+ * material to be added to the result set\r
+ */\r
+ public void addMaterial(Material material) {\r
+ if (materials != null) {\r
+ materials.add(material);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * This method adds a texture to the result set.\r
+ * @param texture\r
+ * texture to be added to the result set\r
+ */\r
+ public void addTexture(Texture texture) {\r
+ if (textures != null) {\r
+ textures.add(texture);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * This method adds a camera to the result set.\r
+ * @param camera\r
+ * camera to be added to the result set\r
+ */\r
+ public void addCamera(Camera camera) {\r
+ if (cameras != null) {\r
+ cameras.add(camera);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * This method adds a light to the result set.\r
+ * @param light\r
+ * light to be added to the result set\r
+ */\r
+ @Override\r
+ public void addLight(Light light) {\r
+ if (lights != null) {\r
+ lights.add(light);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * This method returns all loaded scenes.\r
+ * @return all loaded scenes\r
+ */\r
+ public List<Node> getScenes() {\r
+ return scenes;\r
+ }\r
+\r
+ /**\r
+ * This method returns all loaded objects.\r
+ * @return all loaded objects\r
+ */\r
+ public List<Node> getObjects() {\r
+ return objects;\r
+ }\r
+\r
+ /**\r
+ * This method returns all loaded materials.\r
+ * @return all loaded materials\r
+ */\r
+ public List<Material> getMaterials() {\r
+ return materials;\r
+ }\r
+\r
+ /**\r
+ * This method returns all loaded textures.\r
+ * @return all loaded textures\r
+ */\r
+ public List<Texture> getTextures() {\r
+ return textures;\r
+ }\r
+\r
+ /**\r
+ * This method returns all loaded animations.\r
+ * @return all loaded animations\r
+ */\r
+ public List<AnimData> getAnimations() {\r
+ return animations;\r
+ }\r
+\r
+ /**\r
+ * This method returns all loaded cameras.\r
+ * @return all loaded cameras\r
+ */\r
+ public List<Camera> getCameras() {\r
+ return cameras;\r
+ }\r
+\r
+ /**\r
+ * This method returns all loaded lights.\r
+ * @return all loaded lights\r
+ */\r
+ public List<Light> getLights() {\r
+ return lights;\r
+ }\r
+\r
+ @Override\r
+ public int collideWith(Collidable other, CollisionResults results) throws UnsupportedCollisionException {\r
+ return 0;\r
+ }\r
+\r
+ @Override\r
+ public void updateModelBound() {\r
+ }\r
+\r
+ @Override\r
+ public void setModelBound(BoundingVolume modelBound) {\r
+ }\r
+\r
+ @Override\r
+ public int getVertexCount() {\r
+ return 0;\r
+ }\r
+\r
+ @Override\r
+ public int getTriangleCount() {\r
+ return 0;\r
+ }\r
+\r
+ @Override\r
+ public Spatial deepClone() {\r
+ return null;\r
+ }\r
+\r
+ @Override\r
+ public void depthFirstTraversal(SceneGraphVisitor visitor) {\r
+ }\r
+\r
+ @Override\r
+ protected void breadthFirstTraversal(SceneGraphVisitor visitor, Queue<Spatial> queue) {\r
+ }\r
+ }\r
+\r
+ /**\r
+ * The WORLD file block contains various data that could be added to the scene. The contained data includes: ambient\r
+ * light.\r
+ * @author Marcin Roguski (Kaelthas)\r
+ */\r
+ public static class WorldData {\r
+\r
+ /** The ambient light. */\r
+ private AmbientLight ambientLight;\r
+\r
+ /**\r
+ * This method returns the world's ambient light.\r
+ * @return the world's ambient light\r
+ */\r
+ public AmbientLight getAmbientLight() {\r
+ return ambientLight;\r
+ }\r
+\r
+ /**\r
+ * This method sets the world's ambient light.\r
+ * @param ambientLight\r
+ * the world's ambient light\r
+ */\r
+ public void setAmbientLight(AmbientLight ambientLight) {\r
+ this.ambientLight = ambientLight;\r
+ }\r
+ }\r
}\r
* @author Marcin Roguski\r
*/\r
public class BlenderLoader implements AssetLoader {\r
- private static final Logger LOGGER = Logger.getLogger(BlenderLoader.class.getName());\r
\r
- @Override\r
- public LoadingResults load(AssetInfo assetInfo) throws IOException {\r
- try {\r
- //registering loaders\r
- ModelKey modelKey = (ModelKey)assetInfo.getKey();\r
- BlenderKey blenderKey;\r
- if(modelKey instanceof BlenderKey) {\r
- blenderKey = (BlenderKey)modelKey;\r
- } else {\r
- blenderKey = new BlenderKey(modelKey.getName());\r
- blenderKey.setAssetRootPath(modelKey.getFolder());\r
- }\r
+ private static final Logger LOGGER = Logger.getLogger(BlenderLoader.class.getName());\r
\r
- //opening stream\r
- BlenderInputStream inputStream = new BlenderInputStream(assetInfo.openStream(), assetInfo.getManager());\r
+ @Override\r
+ public LoadingResults load(AssetInfo assetInfo) throws IOException {\r
+ try {\r
+ //registering loaders\r
+ ModelKey modelKey = (ModelKey) assetInfo.getKey();\r
+ BlenderKey blenderKey;\r
+ if (modelKey instanceof BlenderKey) {\r
+ blenderKey = (BlenderKey) modelKey;\r
+ } else {\r
+ blenderKey = new BlenderKey(modelKey.getName());\r
+ blenderKey.setAssetRootPath(modelKey.getFolder());\r
+ }\r
\r
- //reading blocks\r
- List<FileBlockHeader> blocks = new ArrayList<FileBlockHeader>();\r
- FileBlockHeader fileBlock;\r
- DataRepository dataRepository = new DataRepository();\r
- dataRepository.setAssetManager(assetInfo.getManager());\r
- dataRepository.setInputStream(inputStream);\r
- dataRepository.setBlenderKey(blenderKey);\r
- \r
- //creating helpers\r
- dataRepository.putHelper(ArmatureHelper.class, new ArmatureHelper(inputStream.getVersionNumber()));\r
- dataRepository.putHelper(TextureHelper.class, new TextureHelper(inputStream.getVersionNumber()));\r
- dataRepository.putHelper(MeshHelper.class, new MeshHelper(inputStream.getVersionNumber()));\r
- dataRepository.putHelper(ObjectHelper.class, new ObjectHelper(inputStream.getVersionNumber()));\r
- dataRepository.putHelper(CurvesHelper.class, new CurvesHelper(inputStream.getVersionNumber()));\r
- dataRepository.putHelper(LightHelper.class, new LightHelper(inputStream.getVersionNumber()));\r
- dataRepository.putHelper(CameraHelper.class, new CameraHelper(inputStream.getVersionNumber()));\r
- dataRepository.putHelper(ModifierHelper.class, new ModifierHelper(inputStream.getVersionNumber()));\r
- dataRepository.putHelper(MaterialHelper.class, new MaterialHelper(inputStream.getVersionNumber()));\r
- dataRepository.putHelper(ConstraintHelper.class, new ConstraintHelper(inputStream.getVersionNumber(), dataRepository));\r
- dataRepository.putHelper(IpoHelper.class, new IpoHelper(inputStream.getVersionNumber()));\r
- dataRepository.putHelper(NoiseHelper.class, new NoiseHelper(inputStream.getVersionNumber()));\r
- dataRepository.putHelper(ParticlesHelper.class, new ParticlesHelper(inputStream.getVersionNumber()));\r
- \r
- //setting additional data to helpers\r
- if(blenderKey.isFixUpAxis()) {\r
- ObjectHelper objectHelper = dataRepository.getHelper(ObjectHelper.class);\r
- objectHelper.setyIsUpAxis(true);\r
- CurvesHelper curvesHelper = dataRepository.getHelper(CurvesHelper.class);\r
- curvesHelper.setyIsUpAxis(true);\r
- }\r
- MaterialHelper materialHelper = dataRepository.getHelper(MaterialHelper.class);\r
- materialHelper.setFaceCullMode(blenderKey.getFaceCullMode());\r
+ //opening stream\r
+ BlenderInputStream inputStream = new BlenderInputStream(assetInfo.openStream(), assetInfo.getManager());\r
\r
- //reading the blocks (dna block is automatically saved in the data repository when found)//TODO: zmienić to\r
- do {\r
- fileBlock = new FileBlockHeader(inputStream, dataRepository);\r
- if(!fileBlock.isDnaBlock()) {\r
- blocks.add(fileBlock);\r
- }\r
- } while(!fileBlock.isLastBlock());\r
+ //reading blocks\r
+ List<FileBlockHeader> blocks = new ArrayList<FileBlockHeader>();\r
+ FileBlockHeader fileBlock;\r
+ DataRepository dataRepository = new DataRepository();\r
+ dataRepository.setAssetManager(assetInfo.getManager());\r
+ dataRepository.setInputStream(inputStream);\r
+ dataRepository.setBlenderKey(blenderKey);\r
\r
- JmeConverter converter = new JmeConverter(dataRepository);\r
- LoadingResults loadingResults = blenderKey.prepareLoadingResults();\r
- WorldData worldData = null;//a set of data used in different scene aspects\r
- for(FileBlockHeader block : blocks) {\r
- switch(block.getCode()) {\r
- case FileBlockHeader.BLOCK_OB00://Object\r
- Object object = converter.toObject(block.getStructure(dataRepository));\r
- if(object instanceof Node) {\r
- if((blenderKey.getFeaturesToLoad() & FeaturesToLoad.OBJECTS) != 0) {\r
- LOGGER.log(Level.INFO, ((Node)object).getName() + ": " + ((Node)object).getLocalTranslation().toString() + "--> " + (((Node)object).getParent() == null ? "null" : ((Node)object).getParent().getName()));\r
- if(((Node)object).getParent() == null) {\r
- loadingResults.addObject((Node)object);\r
- }\r
- }\r
- } else if(object instanceof Camera) {\r
- if((blenderKey.getFeaturesToLoad() & FeaturesToLoad.CAMERAS) != 0) {\r
- loadingResults.addCamera((Camera)object);\r
- }\r
- } else if(object instanceof Light) {\r
- if((blenderKey.getFeaturesToLoad() & FeaturesToLoad.LIGHTS) != 0) {\r
- loadingResults.addLight((Light)object);\r
- }\r
- }\r
- break;\r
- case FileBlockHeader.BLOCK_MA00://Material\r
- if((blenderKey.getFeaturesToLoad() & FeaturesToLoad.MATERIALS) != 0) {\r
- loadingResults.addMaterial(converter.toMaterial(block.getStructure(dataRepository)));\r
- }\r
- break;\r
- case FileBlockHeader.BLOCK_SC00://Scene\r
- if((blenderKey.getFeaturesToLoad() & FeaturesToLoad.SCENES) != 0) {\r
- loadingResults.addScene(converter.toScene(block.getStructure(dataRepository)));\r
- }\r
- break;\r
- case FileBlockHeader.BLOCK_WO00://World\r
- if(worldData == null) {//onlu one world data is used\r
- Structure worldStructure = block.getStructure(dataRepository);\r
- String worldName = worldStructure.getName();\r
- if(blenderKey.getUsedWorld() == null || blenderKey.getUsedWorld().equals(worldName)) {\r
- worldData = converter.toWorldData(worldStructure);\r
- if((blenderKey.getFeaturesToLoad() & FeaturesToLoad.LIGHTS) != 0) {\r
- loadingResults.addLight(worldData.getAmbientLight());\r
- }\r
- }\r
- }\r
- break;\r
- }\r
- }\r
- try {\r
- inputStream.close();\r
- } catch(IOException e) {\r
- LOGGER.log(Level.SEVERE, e.getMessage(), e);\r
- }\r
- return loadingResults;\r
- } catch(BlenderFileException e) {\r
- LOGGER.log(Level.SEVERE, e.getMessage(), e);\r
- }\r
- return null;\r
- }\r
+ //creating helpers\r
+ dataRepository.putHelper(ArmatureHelper.class, new ArmatureHelper(inputStream.getVersionNumber()));\r
+ dataRepository.putHelper(TextureHelper.class, new TextureHelper(inputStream.getVersionNumber()));\r
+ dataRepository.putHelper(MeshHelper.class, new MeshHelper(inputStream.getVersionNumber()));\r
+ dataRepository.putHelper(ObjectHelper.class, new ObjectHelper(inputStream.getVersionNumber()));\r
+ dataRepository.putHelper(CurvesHelper.class, new CurvesHelper(inputStream.getVersionNumber()));\r
+ dataRepository.putHelper(LightHelper.class, new LightHelper(inputStream.getVersionNumber()));\r
+ dataRepository.putHelper(CameraHelper.class, new CameraHelper(inputStream.getVersionNumber()));\r
+ dataRepository.putHelper(ModifierHelper.class, new ModifierHelper(inputStream.getVersionNumber()));\r
+ dataRepository.putHelper(MaterialHelper.class, new MaterialHelper(inputStream.getVersionNumber()));\r
+ dataRepository.putHelper(ConstraintHelper.class, new ConstraintHelper(inputStream.getVersionNumber(), dataRepository));\r
+ dataRepository.putHelper(IpoHelper.class, new IpoHelper(inputStream.getVersionNumber()));\r
+ dataRepository.putHelper(NoiseHelper.class, new NoiseHelper(inputStream.getVersionNumber()));\r
+ dataRepository.putHelper(ParticlesHelper.class, new ParticlesHelper(inputStream.getVersionNumber()));\r
+\r
+ //setting additional data to helpers\r
+ if (blenderKey.isFixUpAxis()) {\r
+ ObjectHelper objectHelper = dataRepository.getHelper(ObjectHelper.class);\r
+ objectHelper.setyIsUpAxis(true);\r
+ CurvesHelper curvesHelper = dataRepository.getHelper(CurvesHelper.class);\r
+ curvesHelper.setyIsUpAxis(true);\r
+ }\r
+ MaterialHelper materialHelper = dataRepository.getHelper(MaterialHelper.class);\r
+ materialHelper.setFaceCullMode(blenderKey.getFaceCullMode());\r
+\r
+ //reading the blocks (dna block is automatically saved in the data repository when found)//TODO: zmienić to\r
+ do {\r
+ fileBlock = new FileBlockHeader(inputStream, dataRepository);\r
+ if (!fileBlock.isDnaBlock()) {\r
+ blocks.add(fileBlock);\r
+ }\r
+ } while (!fileBlock.isLastBlock());\r
+\r
+ JmeConverter converter = new JmeConverter(dataRepository);\r
+ LoadingResults loadingResults = blenderKey.prepareLoadingResults();\r
+ WorldData worldData = null;//a set of data used in different scene aspects\r
+ for (FileBlockHeader block : blocks) {\r
+ switch (block.getCode()) {\r
+ case FileBlockHeader.BLOCK_OB00://Object\r
+ Object object = converter.toObject(block.getStructure(dataRepository));\r
+ if (object instanceof Node) {\r
+ if ((blenderKey.getFeaturesToLoad() & FeaturesToLoad.OBJECTS) != 0) {\r
+ LOGGER.log(Level.INFO, "{0}: {1}--> {2}", new Object[]{((Node) object).getName(), ((Node) object).getLocalTranslation().toString(), ((Node) object).getParent() == null ? "null" : ((Node) object).getParent().getName()});\r
+ if (((Node) object).getParent() == null) {\r
+ loadingResults.addObject((Node) object);\r
+ }\r
+ }\r
+ } else if (object instanceof Camera) {\r
+ if ((blenderKey.getFeaturesToLoad() & FeaturesToLoad.CAMERAS) != 0) {\r
+ loadingResults.addCamera((Camera) object);\r
+ }\r
+ } else if (object instanceof Light) {\r
+ if ((blenderKey.getFeaturesToLoad() & FeaturesToLoad.LIGHTS) != 0) {\r
+ loadingResults.addLight((Light) object);\r
+ }\r
+ }\r
+ break;\r
+ case FileBlockHeader.BLOCK_MA00://Material\r
+ if ((blenderKey.getFeaturesToLoad() & FeaturesToLoad.MATERIALS) != 0) {\r
+ loadingResults.addMaterial(converter.toMaterial(block.getStructure(dataRepository)));\r
+ }\r
+ break;\r
+ case FileBlockHeader.BLOCK_SC00://Scene\r
+ if ((blenderKey.getFeaturesToLoad() & FeaturesToLoad.SCENES) != 0) {\r
+ loadingResults.addScene(converter.toScene(block.getStructure(dataRepository)));\r
+ }\r
+ break;\r
+ case FileBlockHeader.BLOCK_WO00://World\r
+ if (worldData == null) {//onlu one world data is used\r
+ Structure worldStructure = block.getStructure(dataRepository);\r
+ String worldName = worldStructure.getName();\r
+ if (blenderKey.getUsedWorld() == null || blenderKey.getUsedWorld().equals(worldName)) {\r
+ worldData = converter.toWorldData(worldStructure);\r
+ if ((blenderKey.getFeaturesToLoad() & FeaturesToLoad.LIGHTS) != 0) {\r
+ loadingResults.addLight(worldData.getAmbientLight());\r
+ }\r
+ }\r
+ }\r
+ break;\r
+ }\r
+ }\r
+ try {\r
+ inputStream.close();\r
+ } catch (IOException e) {\r
+ LOGGER.log(Level.SEVERE, e.getMessage(), e);\r
+ }\r
+ return loadingResults;\r
+ } catch (BlenderFileException e) {\r
+ LOGGER.log(Level.SEVERE, e.getMessage(), e);\r
+ }\r
+ return null;\r
+ }\r
}\r
* @author Marcin Roguski
*/
public class BlenderModelLoader implements AssetLoader {
- private static final Logger LOGGER = Logger.getLogger(BlenderModelLoader.class.getName());
- @Override
- public Spatial load(AssetInfo assetInfo) throws IOException {
- try {
- //registering loaders
- ModelKey modelKey = (ModelKey)assetInfo.getKey();
- BlenderKey blenderKey;
- if(modelKey instanceof BlenderKey) {
- blenderKey = (BlenderKey)modelKey;
- } else {
- blenderKey = new BlenderKey(modelKey.getName());
- blenderKey.setAssetRootPath(modelKey.getFolder());
- }
+ private static final Logger LOGGER = Logger.getLogger(BlenderModelLoader.class.getName());
- //opening stream
- BlenderInputStream inputStream = new BlenderInputStream(assetInfo.openStream(), assetInfo.getManager());
- List<FileBlockHeader> blocks = new ArrayList<FileBlockHeader>();
- FileBlockHeader fileBlock;
- DataRepository dataRepository = new DataRepository();
- dataRepository.setAssetManager(assetInfo.getManager());
- dataRepository.setInputStream(inputStream);
- dataRepository.setBlenderKey(blenderKey);
- dataRepository.putHelper(ArmatureHelper.class, new ArmatureHelper(inputStream.getVersionNumber()));
- dataRepository.putHelper(TextureHelper.class, new TextureHelper(inputStream.getVersionNumber()));
- dataRepository.putHelper(MeshHelper.class, new MeshHelper(inputStream.getVersionNumber()));
- dataRepository.putHelper(ObjectHelper.class, new ObjectHelper(inputStream.getVersionNumber()));
- dataRepository.putHelper(CurvesHelper.class, new CurvesHelper(inputStream.getVersionNumber()));
- dataRepository.putHelper(LightHelper.class, new LightHelper(inputStream.getVersionNumber()));
- dataRepository.putHelper(CameraHelper.class, new CameraHelper(inputStream.getVersionNumber()));
- dataRepository.putHelper(ModifierHelper.class, new ModifierHelper(inputStream.getVersionNumber()));
- dataRepository.putHelper(MaterialHelper.class, new MaterialHelper(inputStream.getVersionNumber()));
- dataRepository.putHelper(ConstraintHelper.class, new ConstraintHelper(inputStream.getVersionNumber(), dataRepository));
- dataRepository.putHelper(IpoHelper.class, new IpoHelper(inputStream.getVersionNumber()));
- dataRepository.putHelper(NoiseHelper.class, new NoiseHelper(inputStream.getVersionNumber()));
- dataRepository.putHelper(ParticlesHelper.class, new ParticlesHelper(inputStream.getVersionNumber()));
-
- //setting additional data to helpers
- if(blenderKey.isFixUpAxis()) {
- ObjectHelper objectHelper = dataRepository.getHelper(ObjectHelper.class);
- objectHelper.setyIsUpAxis(true);
- CurvesHelper curvesHelper = dataRepository.getHelper(CurvesHelper.class);
- curvesHelper.setyIsUpAxis(true);
- }
- MaterialHelper materialHelper = dataRepository.getHelper(MaterialHelper.class);
- materialHelper.setFaceCullMode(blenderKey.getFaceCullMode());
+ @Override
+ public Spatial load(AssetInfo assetInfo) throws IOException {
+ try {
+ //registering loaders
+ ModelKey modelKey = (ModelKey) assetInfo.getKey();
+ BlenderKey blenderKey;
+ if (modelKey instanceof BlenderKey) {
+ blenderKey = (BlenderKey) modelKey;
+ } else {
+ blenderKey = new BlenderKey(modelKey.getName());
+ blenderKey.setAssetRootPath(modelKey.getFolder());
+ }
- //reading the blocks (dna block is automatically saved in the data repository when found)//TODO: zmienić to
- do {
- fileBlock = new FileBlockHeader(inputStream, dataRepository);
- if(!fileBlock.isDnaBlock()) {
- blocks.add(fileBlock);
- }
- } while(!fileBlock.isLastBlock());
+ //opening stream
+ BlenderInputStream inputStream = new BlenderInputStream(assetInfo.openStream(), assetInfo.getManager());
+ List<FileBlockHeader> blocks = new ArrayList<FileBlockHeader>();
+ FileBlockHeader fileBlock;
+ DataRepository dataRepository = new DataRepository();
+ dataRepository.setAssetManager(assetInfo.getManager());
+ dataRepository.setInputStream(inputStream);
+ dataRepository.setBlenderKey(blenderKey);
+ dataRepository.putHelper(ArmatureHelper.class, new ArmatureHelper(inputStream.getVersionNumber()));
+ dataRepository.putHelper(TextureHelper.class, new TextureHelper(inputStream.getVersionNumber()));
+ dataRepository.putHelper(MeshHelper.class, new MeshHelper(inputStream.getVersionNumber()));
+ dataRepository.putHelper(ObjectHelper.class, new ObjectHelper(inputStream.getVersionNumber()));
+ dataRepository.putHelper(CurvesHelper.class, new CurvesHelper(inputStream.getVersionNumber()));
+ dataRepository.putHelper(LightHelper.class, new LightHelper(inputStream.getVersionNumber()));
+ dataRepository.putHelper(CameraHelper.class, new CameraHelper(inputStream.getVersionNumber()));
+ dataRepository.putHelper(ModifierHelper.class, new ModifierHelper(inputStream.getVersionNumber()));
+ dataRepository.putHelper(MaterialHelper.class, new MaterialHelper(inputStream.getVersionNumber()));
+ dataRepository.putHelper(ConstraintHelper.class, new ConstraintHelper(inputStream.getVersionNumber(), dataRepository));
+ dataRepository.putHelper(IpoHelper.class, new IpoHelper(inputStream.getVersionNumber()));
+ dataRepository.putHelper(NoiseHelper.class, new NoiseHelper(inputStream.getVersionNumber()));
+ dataRepository.putHelper(ParticlesHelper.class, new ParticlesHelper(inputStream.getVersionNumber()));
- JmeConverter converter = new JmeConverter(dataRepository);
- LoadingResults loadingResults = blenderKey.prepareLoadingResults();
- for(FileBlockHeader block : blocks) {
- if(block.getCode() == FileBlockHeader.BLOCK_OB00) {
- Object object = converter.toObject(block.getStructure(dataRepository));
- if(object instanceof Node) {
- LOGGER.log(Level.INFO, ((Node)object).getName() + ": " + ((Node)object).getLocalTranslation().toString() + "--> " + (((Node)object).getParent() == null ? "null" : ((Node)object).getParent().getName()));
- if(((Node)object).getParent() == null) {
- loadingResults.addObject((Node)object);
- }
- }
- }
- }
- inputStream.close();
- List<Node> objects = loadingResults.getObjects();
- if(objects.size() > 0) {
- Node modelNode = new Node(blenderKey.getName());
- for(Iterator<Node> it = objects.iterator(); it.hasNext();) {
- Node node = it.next();
- modelNode.attachChild(node);
- }
- return modelNode;
- } else if(objects.size() == 1) {
- return objects.get(0);
- }
- } catch(BlenderFileException e) {
- LOGGER.log(Level.SEVERE, e.getMessage(), e);
- }
- return null;
- }
+ //setting additional data to helpers
+ if (blenderKey.isFixUpAxis()) {
+ ObjectHelper objectHelper = dataRepository.getHelper(ObjectHelper.class);
+ objectHelper.setyIsUpAxis(true);
+ CurvesHelper curvesHelper = dataRepository.getHelper(CurvesHelper.class);
+ curvesHelper.setyIsUpAxis(true);
+ }
+ MaterialHelper materialHelper = dataRepository.getHelper(MaterialHelper.class);
+ materialHelper.setFaceCullMode(blenderKey.getFaceCullMode());
+
+ //reading the blocks (dna block is automatically saved in the data repository when found)//TODO: zmienić to
+ do {
+ fileBlock = new FileBlockHeader(inputStream, dataRepository);
+ if (!fileBlock.isDnaBlock()) {
+ blocks.add(fileBlock);
+ }
+ } while (!fileBlock.isLastBlock());
+
+ JmeConverter converter = new JmeConverter(dataRepository);
+ LoadingResults loadingResults = blenderKey.prepareLoadingResults();
+ for (FileBlockHeader block : blocks) {
+ if (block.getCode() == FileBlockHeader.BLOCK_OB00) {
+ Object object = converter.toObject(block.getStructure(dataRepository));
+ if (object instanceof Node) {
+ LOGGER.log(Level.INFO, "{0}: {1}--> {2}", new Object[]{((Node) object).getName(), ((Node) object).getLocalTranslation().toString(), ((Node) object).getParent() == null ? "null" : ((Node) object).getParent().getName()});
+ if (((Node) object).getParent() == null) {
+ loadingResults.addObject((Node) object);
+ }
+ }
+ }
+ }
+ inputStream.close();
+ List<Node> objects = loadingResults.getObjects();
+ if (objects.size() > 0) {
+ Node modelNode = new Node(blenderKey.getName());
+ for (Iterator<Node> it = objects.iterator(); it.hasNext();) {
+ Node node = it.next();
+ modelNode.attachChild(node);
+ }
+ return modelNode;
+ } else if (objects.size() == 1) {
+ return objects.get(0);
+ }
+ } catch (BlenderFileException e) {
+ LOGGER.log(Level.SEVERE, e.getMessage(), e);
+ }
+ return null;
+ }
}
* @author Marcin Roguski\r
*/\r
public class DnaBlockData {\r
- private static final int SDNA_ID = 'S' << 24 | 'D' << 16 | 'N' << 8 | 'A'; //SDNA\r
- private static final int NAME_ID = 'N' << 24 | 'A' << 16 | 'M' << 8 | 'E'; //NAME\r
- private static final int TYPE_ID = 'T' << 24 | 'Y' << 16 | 'P' << 8 | 'E'; //TYPE\r
- private static final int TLEN_ID = 'T' << 24 | 'L' << 16 | 'E' << 8 | 'N'; //TLEN\r
- private static final int STRC_ID = 'S' << 24 | 'T' << 16 | 'R' << 8 | 'C'; //STRC\r
-\r
- /** Structures available inside the file. */\r
- private final Structure[] structures;\r
- /** A map that helps finding a structure by type. */\r
- private final Map<String, Structure> structuresMap;\r
-\r
- /**\r
- * Constructor. Loads the block from the given stream during instance creation.\r
- * @param inputStream\r
- * the stream we read the block from\r
- * @param dataRepository\r
- * the data repository\r
- * @throws BlenderFileException\r
- * this exception is throw if the blend file is invalid or somehow corrupted\r
- */\r
- public DnaBlockData(BlenderInputStream inputStream, DataRepository dataRepository) throws BlenderFileException {\r
- int identifier;\r
-\r
- //reading 'SDNA' identifier\r
- identifier = inputStream.readByte() << 24 | inputStream.readByte() << 16 |\r
- inputStream.readByte() << 8 | inputStream.readByte();\r
-\r
- if(identifier != SDNA_ID) {\r
- throw new BlenderFileException("Invalid identifier! '" + this.toString(SDNA_ID) + "' expected and found: " + this.toString(identifier));\r
- }\r
-\r
- //reading names\r
- identifier = inputStream.readByte() << 24 | inputStream.readByte() << 16 |\r
- inputStream.readByte() << 8 | inputStream.readByte();\r
- if(identifier != NAME_ID) {\r
- throw new BlenderFileException("Invalid identifier! '" + this.toString(NAME_ID) + "' expected and found: " + this.toString(identifier));\r
- }\r
- int amount = inputStream.readInt();\r
- if(amount <= 0) {\r
- throw new BlenderFileException("The names amount number should be positive!");\r
- }\r
- String[] names = new String[amount];\r
- for(int i = 0; i < amount; ++i) {\r
- names[i] = inputStream.readString();\r
- }\r
-\r
- //reding types\r
- inputStream.alignPosition(4);\r
- identifier = inputStream.readByte() << 24 | inputStream.readByte() << 16 |\r
- inputStream.readByte() << 8 | inputStream.readByte();\r
- if(identifier != TYPE_ID) {\r
- throw new BlenderFileException("Invalid identifier! '" + this.toString(TYPE_ID) + "' expected and found: " + this.toString(identifier));\r
- }\r
- amount = inputStream.readInt();\r
- if(amount <= 0) {\r
- throw new BlenderFileException("The types amount number should be positive!");\r
- }\r
- String[] types = new String[amount];\r
- for(int i = 0; i < amount; ++i) {\r
- types[i] = inputStream.readString();\r
- }\r
-\r
- //reading lengths\r
- inputStream.alignPosition(4);\r
- identifier = inputStream.readByte() << 24 | inputStream.readByte() << 16 |\r
- inputStream.readByte() << 8 | inputStream.readByte();\r
- if(identifier != TLEN_ID) {\r
- throw new BlenderFileException("Invalid identifier! '" + this.toString(TLEN_ID) + "' expected and found: " + this.toString(identifier));\r
- }\r
- int[] lengths = new int[amount];//theamount is the same as int types\r
- for(int i = 0; i < amount; ++i) {\r
- lengths[i] = inputStream.readShort();\r
- }\r
-\r
- //reading structures\r
- inputStream.alignPosition(4);\r
- identifier = inputStream.readByte() << 24 | inputStream.readByte() << 16 |\r
- inputStream.readByte() << 8 | inputStream.readByte();\r
- if(identifier != STRC_ID) {\r
- throw new BlenderFileException("Invalid identifier! '" + this.toString(STRC_ID) + "' expected and found: " + this.toString(identifier));\r
- }\r
- amount = inputStream.readInt();\r
- if(amount <= 0) {\r
- throw new BlenderFileException("The structures amount number should be positive!");\r
- }\r
- structures = new Structure[amount];\r
- structuresMap = new HashMap<String, Structure>(amount);\r
- for(int i = 0; i < amount; ++i) {\r
- structures[i] = new Structure(inputStream, names, types, dataRepository);\r
- if(structuresMap.containsKey(structures[i].getType())) {\r
- throw new BlenderFileException("Blend file seems to be corrupted! The type " + structures[i].getType() + " is defined twice!");\r
- }\r
- structuresMap.put(structures[i].getType(), structures[i]);\r
- }\r
- }\r
-\r
- /**\r
- * This method returns the amount of the structures.\r
- * @return the amount of the structures\r
- */\r
- public int getStructuresCount() {\r
- return structures.length;\r
- }\r
-\r
- /**\r
- * This method returns the structure of the given index.\r
- * @param index\r
- * the index of the structure\r
- * @return the structure of the given index\r
- */\r
- public Structure getStructure(int index) {\r
- try {\r
- return (Structure)structures[index].clone();\r
- } catch(CloneNotSupportedException e) {\r
- throw new IllegalStateException("Structure should be clonable!!!", e);\r
- }\r
- }\r
-\r
- /**\r
- * This method returns a structure of the given name. If the name does not exists then null is returned.\r
- * @param name\r
- * the name of the structure\r
- * @return the required structure or null if the given name is inapropriate\r
- */\r
- public Structure getStructure(String name) {\r
- try {\r
- return (Structure)structuresMap.get(name).clone();\r
- } catch(CloneNotSupportedException e) {\r
- throw new IllegalStateException(e.getMessage(), e);\r
- }\r
- }\r
-\r
- /**\r
- * This method indicates if the structure of the given name exists.\r
- * @param name\r
- * the name of the structure\r
- * @return true if the structure exists and false otherwise\r
- */\r
- public boolean hasStructure(String name) {\r
- return structuresMap.containsKey(name);\r
- }\r
-\r
- /**\r
- * This method converts the given identifier code to string.\r
- * @param code\r
- * the code taht is to be converted\r
- * @return the string value of the identifier\r
- */\r
- private String toString(int code) {\r
- char c1 = (char)((code & 0xFF000000) >> 24);\r
- char c2 = (char)((code & 0xFF0000) >> 16);\r
- char c3 = (char)((code & 0xFF00) >> 8);\r
- char c4 = (char)(code & 0xFF);\r
- return String.valueOf(c1) + c2 + c3 + c4;\r
- }\r
-\r
- @Override\r
- public String toString() {\r
- StringBuilder stringBuilder = new StringBuilder("=============== ").append(SDNA_ID).append('\n');\r
- for(Structure structure : structures) {\r
- stringBuilder.append(structure.toString()).append('\n');\r
- }\r
- return stringBuilder.append("===============").toString();\r
- }\r
+\r
+ private static final int SDNA_ID = 'S' << 24 | 'D' << 16 | 'N' << 8 | 'A'; //SDNA\r
+ private static final int NAME_ID = 'N' << 24 | 'A' << 16 | 'M' << 8 | 'E'; //NAME\r
+ private static final int TYPE_ID = 'T' << 24 | 'Y' << 16 | 'P' << 8 | 'E'; //TYPE\r
+ private static final int TLEN_ID = 'T' << 24 | 'L' << 16 | 'E' << 8 | 'N'; //TLEN\r
+ private static final int STRC_ID = 'S' << 24 | 'T' << 16 | 'R' << 8 | 'C'; //STRC\r
+ /** Structures available inside the file. */\r
+ private final Structure[] structures;\r
+ /** A map that helps finding a structure by type. */\r
+ private final Map<String, Structure> structuresMap;\r
+\r
+ /**\r
+ * Constructor. Loads the block from the given stream during instance creation.\r
+ * @param inputStream\r
+ * the stream we read the block from\r
+ * @param dataRepository\r
+ * the data repository\r
+ * @throws BlenderFileException\r
+ * this exception is throw if the blend file is invalid or somehow corrupted\r
+ */\r
+ public DnaBlockData(BlenderInputStream inputStream, DataRepository dataRepository) throws BlenderFileException {\r
+ int identifier;\r
+\r
+ //reading 'SDNA' identifier\r
+ identifier = inputStream.readByte() << 24 | inputStream.readByte() << 16\r
+ | inputStream.readByte() << 8 | inputStream.readByte();\r
+\r
+ if (identifier != SDNA_ID) {\r
+ throw new BlenderFileException("Invalid identifier! '" + this.toString(SDNA_ID) + "' expected and found: " + this.toString(identifier));\r
+ }\r
+\r
+ //reading names\r
+ identifier = inputStream.readByte() << 24 | inputStream.readByte() << 16\r
+ | inputStream.readByte() << 8 | inputStream.readByte();\r
+ if (identifier != NAME_ID) {\r
+ throw new BlenderFileException("Invalid identifier! '" + this.toString(NAME_ID) + "' expected and found: " + this.toString(identifier));\r
+ }\r
+ int amount = inputStream.readInt();\r
+ if (amount <= 0) {\r
+ throw new BlenderFileException("The names amount number should be positive!");\r
+ }\r
+ String[] names = new String[amount];\r
+ for (int i = 0; i < amount; ++i) {\r
+ names[i] = inputStream.readString();\r
+ }\r
+\r
+ //reding types\r
+ inputStream.alignPosition(4);\r
+ identifier = inputStream.readByte() << 24 | inputStream.readByte() << 16\r
+ | inputStream.readByte() << 8 | inputStream.readByte();\r
+ if (identifier != TYPE_ID) {\r
+ throw new BlenderFileException("Invalid identifier! '" + this.toString(TYPE_ID) + "' expected and found: " + this.toString(identifier));\r
+ }\r
+ amount = inputStream.readInt();\r
+ if (amount <= 0) {\r
+ throw new BlenderFileException("The types amount number should be positive!");\r
+ }\r
+ String[] types = new String[amount];\r
+ for (int i = 0; i < amount; ++i) {\r
+ types[i] = inputStream.readString();\r
+ }\r
+\r
+ //reading lengths\r
+ inputStream.alignPosition(4);\r
+ identifier = inputStream.readByte() << 24 | inputStream.readByte() << 16\r
+ | inputStream.readByte() << 8 | inputStream.readByte();\r
+ if (identifier != TLEN_ID) {\r
+ throw new BlenderFileException("Invalid identifier! '" + this.toString(TLEN_ID) + "' expected and found: " + this.toString(identifier));\r
+ }\r
+ int[] lengths = new int[amount];//theamount is the same as int types\r
+ for (int i = 0; i < amount; ++i) {\r
+ lengths[i] = inputStream.readShort();\r
+ }\r
+\r
+ //reading structures\r
+ inputStream.alignPosition(4);\r
+ identifier = inputStream.readByte() << 24 | inputStream.readByte() << 16\r
+ | inputStream.readByte() << 8 | inputStream.readByte();\r
+ if (identifier != STRC_ID) {\r
+ throw new BlenderFileException("Invalid identifier! '" + this.toString(STRC_ID) + "' expected and found: " + this.toString(identifier));\r
+ }\r
+ amount = inputStream.readInt();\r
+ if (amount <= 0) {\r
+ throw new BlenderFileException("The structures amount number should be positive!");\r
+ }\r
+ structures = new Structure[amount];\r
+ structuresMap = new HashMap<String, Structure>(amount);\r
+ for (int i = 0; i < amount; ++i) {\r
+ structures[i] = new Structure(inputStream, names, types, dataRepository);\r
+ if (structuresMap.containsKey(structures[i].getType())) {\r
+ throw new BlenderFileException("Blend file seems to be corrupted! The type " + structures[i].getType() + " is defined twice!");\r
+ }\r
+ structuresMap.put(structures[i].getType(), structures[i]);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * This method returns the amount of the structures.\r
+ * @return the amount of the structures\r
+ */\r
+ public int getStructuresCount() {\r
+ return structures.length;\r
+ }\r
+\r
+ /**\r
+ * This method returns the structure of the given index.\r
+ * @param index\r
+ * the index of the structure\r
+ * @return the structure of the given index\r
+ */\r
+ public Structure getStructure(int index) {\r
+ try {\r
+ return (Structure) structures[index].clone();\r
+ } catch (CloneNotSupportedException e) {\r
+ throw new IllegalStateException("Structure should be clonable!!!", e);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * This method returns a structure of the given name. If the name does not exists then null is returned.\r
+ * @param name\r
+ * the name of the structure\r
+ * @return the required structure or null if the given name is inapropriate\r
+ */\r
+ public Structure getStructure(String name) {\r
+ try {\r
+ return (Structure) structuresMap.get(name).clone();\r
+ } catch (CloneNotSupportedException e) {\r
+ throw new IllegalStateException(e.getMessage(), e);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * This method indicates if the structure of the given name exists.\r
+ * @param name\r
+ * the name of the structure\r
+ * @return true if the structure exists and false otherwise\r
+ */\r
+ public boolean hasStructure(String name) {\r
+ return structuresMap.containsKey(name);\r
+ }\r
+\r
+ /**\r
+ * This method converts the given identifier code to string.\r
+ * @param code\r
+ * the code taht is to be converted\r
+ * @return the string value of the identifier\r
+ */\r
+ private String toString(int code) {\r
+ char c1 = (char) ((code & 0xFF000000) >> 24);\r
+ char c2 = (char) ((code & 0xFF0000) >> 16);\r
+ char c3 = (char) ((code & 0xFF00) >> 8);\r
+ char c4 = (char) (code & 0xFF);\r
+ return String.valueOf(c1) + c2 + c3 + c4;\r
+ }\r
+\r
+ @Override\r
+ public String toString() {\r
+ StringBuilder stringBuilder = new StringBuilder("=============== ").append(SDNA_ID).append('\n');\r
+ for (Structure structure : structures) {\r
+ stringBuilder.append(structure.toString()).append('\n');\r
+ }\r
+ return stringBuilder.append("===============").toString();\r
+ }\r
}\r
* another structure.\r
* @author Marcin Roguski\r
*/\r
-/*package*/class Field implements Cloneable {\r
- private static final int NAME_LENGTH = 24;\r
- private static final int TYPE_LENGTH = 16;\r
+/*package*/\r
+class Field implements Cloneable {\r
\r
- /** The data repository. */\r
- public DataRepository dataRepository;\r
- /** The type of the field. */\r
- public String type;\r
- /** The name of the field. */\r
- public String name;\r
- /** The value of the field. Filled during data reading. */\r
- public Object value;\r
- /** This variable indicates the level of the pointer. */\r
- public int pointerLevel;\r
- /**\r
- * This variable determines the sizes of the array. If the value is null the n the field is not an array.\r
- */\r
- public int[] tableSizes;\r
- /** This variable indicates if the field is a function pointer. */\r
- public boolean function;\r
+ private static final int NAME_LENGTH = 24;\r
+ private static final int TYPE_LENGTH = 16;\r
+ /** The data repository. */\r
+ public DataRepository dataRepository;\r
+ /** The type of the field. */\r
+ public String type;\r
+ /** The name of the field. */\r
+ public String name;\r
+ /** The value of the field. Filled during data reading. */\r
+ public Object value;\r
+ /** This variable indicates the level of the pointer. */\r
+ public int pointerLevel;\r
+ /**\r
+ * This variable determines the sizes of the array. If the value is null the n the field is not an array.\r
+ */\r
+ public int[] tableSizes;\r
+ /** This variable indicates if the field is a function pointer. */\r
+ public boolean function;\r
\r
- /**\r
- * Constructor. Saves the field data and parses its name.\r
- * @param name\r
- * the name of the field\r
- * @param type\r
- * the type of the field\r
- * @param dataRepository\r
- * the data repository\r
- * @throws BlenderFileException\r
- * this exception is thrown if the names contain errors\r
- */\r
- public Field(String name, String type, DataRepository dataRepository) throws BlenderFileException {\r
- this.type = type;\r
- this.dataRepository = dataRepository;\r
- this.parseField(new StringBuilder(name));\r
- }\r
+ /**\r
+ * Constructor. Saves the field data and parses its name.\r
+ * @param name\r
+ * the name of the field\r
+ * @param type\r
+ * the type of the field\r
+ * @param dataRepository\r
+ * the data repository\r
+ * @throws BlenderFileException\r
+ * this exception is thrown if the names contain errors\r
+ */\r
+ public Field(String name, String type, DataRepository dataRepository) throws BlenderFileException {\r
+ this.type = type;\r
+ this.dataRepository = dataRepository;\r
+ this.parseField(new StringBuilder(name));\r
+ }\r
\r
- /**\r
- * Copy constructor. Used in clone method. Copying is not full. The value in the new object is not set so that we\r
- * have a clead empty copy of the filed to fill with data.\r
- * @param field\r
- * the object that we copy\r
- */\r
- private Field(Field field) {\r
- type = field.type;\r
- name = field.name;\r
- dataRepository = field.dataRepository;\r
- pointerLevel = field.pointerLevel;\r
- if(field.tableSizes != null) {\r
- tableSizes = field.tableSizes.clone();\r
- }\r
- function = field.function;\r
- }\r
+ /**\r
+ * Copy constructor. Used in clone method. Copying is not full. The value in the new object is not set so that we\r
+ * have a clead empty copy of the filed to fill with data.\r
+ * @param field\r
+ * the object that we copy\r
+ */\r
+ private Field(Field field) {\r
+ type = field.type;\r
+ name = field.name;\r
+ dataRepository = field.dataRepository;\r
+ pointerLevel = field.pointerLevel;\r
+ if (field.tableSizes != null) {\r
+ tableSizes = field.tableSizes.clone();\r
+ }\r
+ function = field.function;\r
+ }\r
\r
- @Override\r
- public Object clone() throws CloneNotSupportedException {\r
- return new Field(this);\r
- }\r
+ @Override\r
+ public Object clone() throws CloneNotSupportedException {\r
+ return new Field(this);\r
+ }\r
\r
- /**\r
- * This method fills the field wth data read from the input stream.\r
- * @param blenderInputStream\r
- * the stream we read data from\r
- * @throws BlenderFileException\r
- * an exception is thrown when the blend file is somehow invalid or corrupted\r
- */\r
- public void fill(BlenderInputStream blenderInputStream) throws BlenderFileException {\r
- int dataToRead = 1;\r
- if(tableSizes != null && tableSizes.length > 0) {\r
- for(int size : tableSizes) {\r
- if(size <= 0) {\r
- throw new BlenderFileException("The field " + name + " has invalid table size: " + size);\r
- }\r
- dataToRead *= size;\r
- }\r
- }\r
- DataType dataType = pointerLevel == 0 ? DataType.getDataType(type, dataRepository) : DataType.POINTER;\r
- switch(dataType) {\r
- case POINTER:\r
- if(dataToRead == 1) {\r
- Pointer pointer = new Pointer(pointerLevel, function, dataRepository);\r
- pointer.fill(blenderInputStream);\r
- value = pointer;\r
- } else {\r
- Pointer[] data = new Pointer[dataToRead];\r
- for(int i = 0; i < dataToRead; ++i) {\r
- Pointer pointer = new Pointer(pointerLevel, function, dataRepository);\r
- pointer.fill(blenderInputStream);\r
- data[i] = pointer;\r
- }\r
- value = new DynamicArray<Pointer>(tableSizes, data);\r
- }\r
- break;\r
- case CHARACTER:\r
- //character is also stored as a number, because sometimes the new blender version uses\r
- //other number type instead of character as a field type\r
- //and characters are very often used as byte number stores instead of real chars\r
- if(dataToRead == 1) {\r
- value = Byte.valueOf((byte)blenderInputStream.readByte());\r
- } else {\r
- Character[] data = new Character[dataToRead];\r
- for(int i = 0; i < dataToRead; ++i) {\r
- data[i] = Character.valueOf((char)blenderInputStream.readByte());\r
- }\r
- value = new DynamicArray<Character>(tableSizes, data);\r
- }\r
- break;\r
- case SHORT:\r
- if(dataToRead == 1) {\r
- value = Integer.valueOf(blenderInputStream.readShort());\r
- } else {\r
- Number[] data = new Number[dataToRead];\r
- for(int i = 0; i < dataToRead; ++i) {\r
- data[i] = Integer.valueOf(blenderInputStream.readShort());\r
- }\r
- value = new DynamicArray<Number>(tableSizes, data);\r
- }\r
- break;\r
- case INTEGER:\r
- if(dataToRead == 1) {\r
- value = Integer.valueOf(blenderInputStream.readInt());\r
- } else {\r
- Number[] data = new Number[dataToRead];\r
- for(int i = 0; i < dataToRead; ++i) {\r
- data[i] = Integer.valueOf(blenderInputStream.readInt());\r
- }\r
- value = new DynamicArray<Number>(tableSizes, data);\r
- }\r
- break;\r
- case LONG:\r
- if(dataToRead == 1) {\r
- value = Long.valueOf(blenderInputStream.readLong());\r
- } else {\r
- Number[] data = new Number[dataToRead];\r
- for(int i = 0; i < dataToRead; ++i) {\r
- data[i] = Long.valueOf(blenderInputStream.readLong());\r
- }\r
- value = new DynamicArray<Number>(tableSizes, data);\r
- }\r
- break;\r
- case FLOAT:\r
- if(dataToRead == 1) {\r
- value = Float.valueOf(blenderInputStream.readFloat());\r
- } else {\r
- Number[] data = new Number[dataToRead];\r
- for(int i = 0; i < dataToRead; ++i) {\r
- data[i] = Float.valueOf(blenderInputStream.readFloat());\r
- }\r
- value = new DynamicArray<Number>(tableSizes, data);\r
- }\r
- break;\r
- case DOUBLE:\r
- if(dataToRead == 1) {\r
- value = Double.valueOf(blenderInputStream.readDouble());\r
- } else {\r
- Number[] data = new Number[dataToRead];\r
- for(int i = 0; i < dataToRead; ++i) {\r
- data[i] = Double.valueOf(blenderInputStream.readDouble());\r
- }\r
- value = new DynamicArray<Number>(tableSizes, data);\r
- }\r
- break;\r
- case VOID:\r
- break;\r
- case STRUCTURE:\r
- if(dataToRead == 1) {\r
- Structure structure = dataRepository.getDnaBlockData().getStructure(type);\r
- structure.fill(blenderInputStream);\r
- value = structure;\r
- } else {\r
- Structure[] data = new Structure[dataToRead];\r
- for(int i = 0; i < dataToRead; ++i) {\r
- Structure structure = dataRepository.getDnaBlockData().getStructure(type);\r
- structure.fill(blenderInputStream);\r
- data[i] = structure;\r
- }\r
- value = new DynamicArray<Structure>(tableSizes, data);\r
- }\r
- break;\r
- default:\r
- throw new IllegalStateException("Unimplemented filling of type: " + type);\r
- }\r
- }\r
+ /**\r
+ * This method fills the field wth data read from the input stream.\r
+ * @param blenderInputStream\r
+ * the stream we read data from\r
+ * @throws BlenderFileException\r
+ * an exception is thrown when the blend file is somehow invalid or corrupted\r
+ */\r
+ public void fill(BlenderInputStream blenderInputStream) throws BlenderFileException {\r
+ int dataToRead = 1;\r
+ if (tableSizes != null && tableSizes.length > 0) {\r
+ for (int size : tableSizes) {\r
+ if (size <= 0) {\r
+ throw new BlenderFileException("The field " + name + " has invalid table size: " + size);\r
+ }\r
+ dataToRead *= size;\r
+ }\r
+ }\r
+ DataType dataType = pointerLevel == 0 ? DataType.getDataType(type, dataRepository) : DataType.POINTER;\r
+ switch (dataType) {\r
+ case POINTER:\r
+ if (dataToRead == 1) {\r
+ Pointer pointer = new Pointer(pointerLevel, function, dataRepository);\r
+ pointer.fill(blenderInputStream);\r
+ value = pointer;\r
+ } else {\r
+ Pointer[] data = new Pointer[dataToRead];\r
+ for (int i = 0; i < dataToRead; ++i) {\r
+ Pointer pointer = new Pointer(pointerLevel, function, dataRepository);\r
+ pointer.fill(blenderInputStream);\r
+ data[i] = pointer;\r
+ }\r
+ value = new DynamicArray<Pointer>(tableSizes, data);\r
+ }\r
+ break;\r
+ case CHARACTER:\r
+ //character is also stored as a number, because sometimes the new blender version uses\r
+ //other number type instead of character as a field type\r
+ //and characters are very often used as byte number stores instead of real chars\r
+ if (dataToRead == 1) {\r
+ value = Byte.valueOf((byte) blenderInputStream.readByte());\r
+ } else {\r
+ Character[] data = new Character[dataToRead];\r
+ for (int i = 0; i < dataToRead; ++i) {\r
+ data[i] = Character.valueOf((char) blenderInputStream.readByte());\r
+ }\r
+ value = new DynamicArray<Character>(tableSizes, data);\r
+ }\r
+ break;\r
+ case SHORT:\r
+ if (dataToRead == 1) {\r
+ value = Integer.valueOf(blenderInputStream.readShort());\r
+ } else {\r
+ Number[] data = new Number[dataToRead];\r
+ for (int i = 0; i < dataToRead; ++i) {\r
+ data[i] = Integer.valueOf(blenderInputStream.readShort());\r
+ }\r
+ value = new DynamicArray<Number>(tableSizes, data);\r
+ }\r
+ break;\r
+ case INTEGER:\r
+ if (dataToRead == 1) {\r
+ value = Integer.valueOf(blenderInputStream.readInt());\r
+ } else {\r
+ Number[] data = new Number[dataToRead];\r
+ for (int i = 0; i < dataToRead; ++i) {\r
+ data[i] = Integer.valueOf(blenderInputStream.readInt());\r
+ }\r
+ value = new DynamicArray<Number>(tableSizes, data);\r
+ }\r
+ break;\r
+ case LONG:\r
+ if (dataToRead == 1) {\r
+ value = Long.valueOf(blenderInputStream.readLong());\r
+ } else {\r
+ Number[] data = new Number[dataToRead];\r
+ for (int i = 0; i < dataToRead; ++i) {\r
+ data[i] = Long.valueOf(blenderInputStream.readLong());\r
+ }\r
+ value = new DynamicArray<Number>(tableSizes, data);\r
+ }\r
+ break;\r
+ case FLOAT:\r
+ if (dataToRead == 1) {\r
+ value = Float.valueOf(blenderInputStream.readFloat());\r
+ } else {\r
+ Number[] data = new Number[dataToRead];\r
+ for (int i = 0; i < dataToRead; ++i) {\r
+ data[i] = Float.valueOf(blenderInputStream.readFloat());\r
+ }\r
+ value = new DynamicArray<Number>(tableSizes, data);\r
+ }\r
+ break;\r
+ case DOUBLE:\r
+ if (dataToRead == 1) {\r
+ value = Double.valueOf(blenderInputStream.readDouble());\r
+ } else {\r
+ Number[] data = new Number[dataToRead];\r
+ for (int i = 0; i < dataToRead; ++i) {\r
+ data[i] = Double.valueOf(blenderInputStream.readDouble());\r
+ }\r
+ value = new DynamicArray<Number>(tableSizes, data);\r
+ }\r
+ break;\r
+ case VOID:\r
+ break;\r
+ case STRUCTURE:\r
+ if (dataToRead == 1) {\r
+ Structure structure = dataRepository.getDnaBlockData().getStructure(type);\r
+ structure.fill(blenderInputStream);\r
+ value = structure;\r
+ } else {\r
+ Structure[] data = new Structure[dataToRead];\r
+ for (int i = 0; i < dataToRead; ++i) {\r
+ Structure structure = dataRepository.getDnaBlockData().getStructure(type);\r
+ structure.fill(blenderInputStream);\r
+ data[i] = structure;\r
+ }\r
+ value = new DynamicArray<Structure>(tableSizes, data);\r
+ }\r
+ break;\r
+ default:\r
+ throw new IllegalStateException("Unimplemented filling of type: " + type);\r
+ }\r
+ }\r
\r
- /**\r
- * This method parses the field name to determine how the field should be used.\r
- * @param nameBuilder\r
- * the name of the field (given as StringBuilder)\r
- * @throws BlenderFileException\r
- * this exception is thrown if the names contain errors\r
- */\r
- private void parseField(StringBuilder nameBuilder) throws BlenderFileException {\r
- this.removeWhitespaces(nameBuilder);\r
- //veryfying if the name is a pointer\r
- int pointerIndex = nameBuilder.indexOf("*");\r
- while(pointerIndex >= 0) {\r
- ++pointerLevel;\r
- nameBuilder.deleteCharAt(pointerIndex);\r
- pointerIndex = nameBuilder.indexOf("*");\r
- }\r
- //veryfying if the name is a function pointer\r
- if(nameBuilder.indexOf("(") >= 0) {\r
- function = true;\r
- this.removeCharacter(nameBuilder, '(');\r
- this.removeCharacter(nameBuilder, ')');\r
- } else {\r
- //veryfying if the name is a table\r
- int tableStartIndex = 0;\r
- List<Integer> lengths = new ArrayList<Integer>(3);//3 dimensions will be enough in most cases\r
- do {\r
- tableStartIndex = nameBuilder.indexOf("[");\r
- if(tableStartIndex > 0) {\r
- int tableStopIndex = nameBuilder.indexOf("]");\r
- if(tableStopIndex < 0) {\r
- throw new BlenderFileException("Invalid structure name: " + name);\r
- }\r
- try {\r
- lengths.add(Integer.valueOf(nameBuilder.substring(tableStartIndex + 1, tableStopIndex)));\r
- } catch(NumberFormatException e) {\r
- throw new BlenderFileException("Invalid structure name caused by invalid table length: " + name, e);\r
- }\r
- nameBuilder.delete(tableStartIndex, tableStopIndex + 1);\r
- }\r
- } while(tableStartIndex > 0);\r
- if(!lengths.isEmpty()) {\r
- tableSizes = new int[lengths.size()];\r
- for(int i = 0; i < tableSizes.length; ++i) {\r
- tableSizes[i] = lengths.get(i).intValue();\r
- }\r
- }\r
- }\r
- name = nameBuilder.toString();\r
- }\r
+ /**\r
+ * This method parses the field name to determine how the field should be used.\r
+ * @param nameBuilder\r
+ * the name of the field (given as StringBuilder)\r
+ * @throws BlenderFileException\r
+ * this exception is thrown if the names contain errors\r
+ */\r
+ private void parseField(StringBuilder nameBuilder) throws BlenderFileException {\r
+ this.removeWhitespaces(nameBuilder);\r
+ //veryfying if the name is a pointer\r
+ int pointerIndex = nameBuilder.indexOf("*");\r
+ while (pointerIndex >= 0) {\r
+ ++pointerLevel;\r
+ nameBuilder.deleteCharAt(pointerIndex);\r
+ pointerIndex = nameBuilder.indexOf("*");\r
+ }\r
+ //veryfying if the name is a function pointer\r
+ if (nameBuilder.indexOf("(") >= 0) {\r
+ function = true;\r
+ this.removeCharacter(nameBuilder, '(');\r
+ this.removeCharacter(nameBuilder, ')');\r
+ } else {\r
+ //veryfying if the name is a table\r
+ int tableStartIndex = 0;\r
+ List<Integer> lengths = new ArrayList<Integer>(3);//3 dimensions will be enough in most cases\r
+ do {\r
+ tableStartIndex = nameBuilder.indexOf("[");\r
+ if (tableStartIndex > 0) {\r
+ int tableStopIndex = nameBuilder.indexOf("]");\r
+ if (tableStopIndex < 0) {\r
+ throw new BlenderFileException("Invalid structure name: " + name);\r
+ }\r
+ try {\r
+ lengths.add(Integer.valueOf(nameBuilder.substring(tableStartIndex + 1, tableStopIndex)));\r
+ } catch (NumberFormatException e) {\r
+ throw new BlenderFileException("Invalid structure name caused by invalid table length: " + name, e);\r
+ }\r
+ nameBuilder.delete(tableStartIndex, tableStopIndex + 1);\r
+ }\r
+ } while (tableStartIndex > 0);\r
+ if (!lengths.isEmpty()) {\r
+ tableSizes = new int[lengths.size()];\r
+ for (int i = 0; i < tableSizes.length; ++i) {\r
+ tableSizes[i] = lengths.get(i).intValue();\r
+ }\r
+ }\r
+ }\r
+ name = nameBuilder.toString();\r
+ }\r
\r
- /**\r
- * This method removes the required character from the text.\r
- * @param text\r
- * the text we remove characters from\r
- * @param toRemove\r
- * the character to be removed\r
- */\r
- private void removeCharacter(StringBuilder text, char toRemove) {\r
- for(int i = 0; i < text.length(); ++i) {\r
- if(text.charAt(i) == toRemove) {\r
- text.deleteCharAt(i);\r
- --i;\r
- }\r
- }\r
- }\r
+ /**\r
+ * This method removes the required character from the text.\r
+ * @param text\r
+ * the text we remove characters from\r
+ * @param toRemove\r
+ * the character to be removed\r
+ */\r
+ private void removeCharacter(StringBuilder text, char toRemove) {\r
+ for (int i = 0; i < text.length(); ++i) {\r
+ if (text.charAt(i) == toRemove) {\r
+ text.deleteCharAt(i);\r
+ --i;\r
+ }\r
+ }\r
+ }\r
\r
- /**\r
- * This method removes all whitespaces from the text.\r
- * @param text\r
- * the text we remove whitespaces from\r
- */\r
- private void removeWhitespaces(StringBuilder text) {\r
- for(int i = 0; i < text.length(); ++i) {\r
- if(Character.isWhitespace(text.charAt(i))) {\r
- text.deleteCharAt(i);\r
- --i;\r
- }\r
- }\r
- }\r
+ /**\r
+ * This method removes all whitespaces from the text.\r
+ * @param text\r
+ * the text we remove whitespaces from\r
+ */\r
+ private void removeWhitespaces(StringBuilder text) {\r
+ for (int i = 0; i < text.length(); ++i) {\r
+ if (Character.isWhitespace(text.charAt(i))) {\r
+ text.deleteCharAt(i);\r
+ --i;\r
+ }\r
+ }\r
+ }\r
\r
- @Override\r
- public String toString() {\r
- StringBuilder result = new StringBuilder();\r
- if(function) {\r
- result.append('(');\r
- }\r
- for(int i = 0; i < pointerLevel; ++i) {\r
- result.append('*');\r
- }\r
- result.append(name);\r
- if(tableSizes != null) {\r
- for(int i = 0; i < tableSizes.length; ++i) {\r
- result.append('[').append(tableSizes[i]).append(']');\r
- }\r
- }\r
- if(function) {\r
- result.append(")()");\r
- }\r
- //insert appropriate amount of spaces to format the output corrently\r
- int nameLength = result.length();\r
- result.append(' ');//at least one space is a must\r
- for(int i = 1; i < NAME_LENGTH - nameLength; ++i) {//we start from i=1 because one space is already added\r
- result.append(' ');\r
- }\r
- result.append(type);\r
- nameLength = result.length();\r
- for(int i = 0; i < NAME_LENGTH + TYPE_LENGTH - nameLength; ++i) {\r
- result.append(' ');\r
- }\r
- if(value instanceof Character) {\r
- result.append(" = ").append((int)((Character)value).charValue());\r
- } else {\r
- result.append(" = ").append(value != null ? value.toString() : "null");\r
- }\r
- return result.toString();\r
- }\r
+ @Override\r
+ public String toString() {\r
+ StringBuilder result = new StringBuilder();\r
+ if (function) {\r
+ result.append('(');\r
+ }\r
+ for (int i = 0; i < pointerLevel; ++i) {\r
+ result.append('*');\r
+ }\r
+ result.append(name);\r
+ if (tableSizes != null) {\r
+ for (int i = 0; i < tableSizes.length; ++i) {\r
+ result.append('[').append(tableSizes[i]).append(']');\r
+ }\r
+ }\r
+ if (function) {\r
+ result.append(")()");\r
+ }\r
+ //insert appropriate amount of spaces to format the output corrently\r
+ int nameLength = result.length();\r
+ result.append(' ');//at least one space is a must\r
+ for (int i = 1; i < NAME_LENGTH - nameLength; ++i) {//we start from i=1 because one space is already added\r
+ result.append(' ');\r
+ }\r
+ result.append(type);\r
+ nameLength = result.length();\r
+ for (int i = 0; i < NAME_LENGTH + TYPE_LENGTH - nameLength; ++i) {\r
+ result.append(' ');\r
+ }\r
+ if (value instanceof Character) {\r
+ result.append(" = ").append((int) ((Character) value).charValue());\r
+ } else {\r
+ result.append(" = ").append(value != null ? value.toString() : "null");\r
+ }\r
+ return result.toString();\r
+ }\r
}
\ No newline at end of file
* @author Marcin Roguski\r
*/\r
public class FileBlockHeader {\r
- public static final int BLOCK_TE00 = 'T' << 24 | 'E' << 16; //TE00\r
- public static final int BLOCK_ME00 = 'M' << 24 | 'E' << 16; //ME00\r
- public static final int BLOCK_SR00 = 'S' << 24 | 'R' << 16; //SR00\r
- public static final int BLOCK_CA00 = 'C' << 24 | 'A' << 16; //CA00\r
- public static final int BLOCK_LA00 = 'L' << 24 | 'A' << 16; //LA00\r
- public static final int BLOCK_OB00 = 'O' << 24 | 'B' << 16; //OB00\r
- public static final int BLOCK_MA00 = 'M' << 24 | 'A' << 16; //MA00\r
- public static final int BLOCK_SC00 = 'S' << 24 | 'C' << 16; //SC00\r
- public static final int BLOCK_WO00 = 'W' << 24 | 'O' << 16; //WO00\r
- public static final int BLOCK_TX00 = 'T' << 24 | 'X' << 16; //TX00\r
- public static final int BLOCK_IP00 = 'I' << 24 | 'P' << 16; //IP00\r
- public static final int BLOCK_AC00 = 'A' << 24 | 'C' << 16; //AC00\r
\r
- public static final int BLOCK_GLOB = 'G' << 24 | 'L' << 16 | 'O' << 8 | 'B'; //GLOB\r
- public static final int BLOCK_REND = 'R' << 24 | 'E' << 16 | 'N' << 8 | 'D'; //REND\r
- public static final int BLOCK_DATA = 'D' << 24 | 'A' << 16 | 'T' << 8 | 'A'; //DATA\r
- public static final int BLOCK_DNA1 = 'D' << 24 | 'N' << 16 | 'A' << 8 | '1'; //DNA1\r
- public static final int BLOCK_ENDB = 'E' << 24 | 'N' << 16 | 'D' << 8 | 'B'; //ENDB\r
-\r
- /** Identifier of the file-block [4 bytes]. */\r
- private int code;\r
- /** Total length of the data after the file-block-header [4 bytes]. */\r
- private int size;\r
- /**\r
- * Memory address the structure was located when written to disk [4 or 8 bytes (defined in file header as a pointer\r
- * size)].\r
- */\r
- private long oldMemoryAddress;\r
- /** Index of the SDNA structure [4 bytes]. */\r
- private int sdnaIndex;\r
- /** Number of structure located in this file-block [4 bytes]. */\r
- private int count;\r
- /** Start position of the block's data in the stream. */\r
- private int blockPosition;\r
-\r
- /**\r
- * Constructor. Loads the block header from the given stream during instance creation.\r
- * @param inputStream\r
- * the stream we read the block header from\r
- * @param dataRepository\r
- * the data repository\r
- * @throws BlenderFileException\r
- * this exception is thrown when the pointer size is neither 4 nor 8\r
- */\r
- public FileBlockHeader(BlenderInputStream inputStream, DataRepository dataRepository) throws BlenderFileException {\r
- inputStream.alignPosition(4);\r
- code = inputStream.readByte() << 24 | inputStream.readByte() << 16 |\r
- inputStream.readByte() << 8 | inputStream.readByte();\r
- size = inputStream.readInt();\r
- oldMemoryAddress = inputStream.readPointer();\r
- sdnaIndex = inputStream.readInt();\r
- count = inputStream.readInt();\r
- blockPosition = inputStream.getPosition();\r
- if(FileBlockHeader.BLOCK_DNA1 == code) {\r
- dataRepository.setBlockData(new DnaBlockData(inputStream, dataRepository));\r
- } else {\r
- inputStream.setPosition(blockPosition + size);\r
- dataRepository.addFileBlockHeader(Long.valueOf(oldMemoryAddress), this);\r
- }\r
- }\r
-\r
- /**\r
- * This method returns the structure described by the header filled with appropriate data.\r
- * @param dataRepository\r
- * the data repository\r
- * @return structure filled with data\r
- * @throws BlenderFileException\r
- */\r
- public Structure getStructure(DataRepository dataRepository) throws BlenderFileException {\r
- dataRepository.getInputStream().setPosition(blockPosition);\r
- Structure structure = dataRepository.getDnaBlockData().getStructure(sdnaIndex);\r
- structure.fill(dataRepository.getInputStream());\r
- return structure;\r
- }\r
-\r
- /**\r
- * This method returns the code of this data block.\r
- * @return the code of this data block\r
- */\r
- public int getCode() {\r
- return code;\r
- }\r
-\r
- /**\r
- * This method returns the size of the data stored in this block.\r
- * @return the size of the data stored in this block\r
- */\r
- public int getSize() {\r
- return size;\r
- }\r
-\r
- /**\r
- * This method returns the memory address.\r
- * @return the memory address\r
- */\r
- public long getOldMemoryAddress() {\r
- return oldMemoryAddress;\r
- }\r
-\r
- /**\r
- * This method returns the sdna index.\r
- * @return the sdna index\r
- */\r
- public int getSdnaIndex() {\r
- return sdnaIndex;\r
- }\r
-\r
- /**\r
- * This data returns the number of structure stored in the data block after this header.\r
- * @return the number of structure stored in the data block after this header\r
- */\r
- public int getCount() {\r
- return count;\r
- }\r
-\r
- /**\r
- * This method returns the start position of the data block in the blend file stream.\r
- * @return the start position of the data block\r
- */\r
- public int getBlockPosition() {\r
- return blockPosition;\r
- }\r
-\r
- /**\r
- * This method indicates if the block is the last block in the file.\r
- * @return true if this block is the last one in the file nad false otherwise\r
- */\r
- public boolean isLastBlock() {\r
- return FileBlockHeader.BLOCK_ENDB == code;\r
- }\r
-\r
- /**\r
- * This method indicates if the block is the SDNA block.\r
- * @return true if this block is the SDNA block and false otherwise\r
- */\r
- public boolean isDnaBlock() {\r
- return FileBlockHeader.BLOCK_DNA1 == code;\r
- }\r
-\r
- @Override\r
- public String toString() {\r
- return "FILE BLOCK HEADER [" + this.codeToString(code) + " : " + size + " : " + oldMemoryAddress + " : " + sdnaIndex + " : " + count + "]";\r
- }\r
-\r
- /**\r
- * This method transforms the coded bloch id into a string value.\r
- * @param code\r
- * the id of the block\r
- * @return the string value of the block id\r
- */\r
- protected String codeToString(int code) {\r
- char c1 = (char)((code & 0xFF000000) >> 24);\r
- char c2 = (char)((code & 0xFF0000) >> 16);\r
- char c3 = (char)((code & 0xFF00) >> 8);\r
- char c4 = (char)(code & 0xFF);\r
- return String.valueOf(c1) + c2 + c3 + c4;\r
- }\r
+ public static final int BLOCK_TE00 = 'T' << 24 | 'E' << 16; //TE00\r
+ public static final int BLOCK_ME00 = 'M' << 24 | 'E' << 16; //ME00\r
+ public static final int BLOCK_SR00 = 'S' << 24 | 'R' << 16; //SR00\r
+ public static final int BLOCK_CA00 = 'C' << 24 | 'A' << 16; //CA00\r
+ public static final int BLOCK_LA00 = 'L' << 24 | 'A' << 16; //LA00\r
+ public static final int BLOCK_OB00 = 'O' << 24 | 'B' << 16; //OB00\r
+ public static final int BLOCK_MA00 = 'M' << 24 | 'A' << 16; //MA00\r
+ public static final int BLOCK_SC00 = 'S' << 24 | 'C' << 16; //SC00\r
+ public static final int BLOCK_WO00 = 'W' << 24 | 'O' << 16; //WO00\r
+ public static final int BLOCK_TX00 = 'T' << 24 | 'X' << 16; //TX00\r
+ public static final int BLOCK_IP00 = 'I' << 24 | 'P' << 16; //IP00\r
+ public static final int BLOCK_AC00 = 'A' << 24 | 'C' << 16; //AC00\r
+ public static final int BLOCK_GLOB = 'G' << 24 | 'L' << 16 | 'O' << 8 | 'B'; //GLOB\r
+ public static final int BLOCK_REND = 'R' << 24 | 'E' << 16 | 'N' << 8 | 'D'; //REND\r
+ public static final int BLOCK_DATA = 'D' << 24 | 'A' << 16 | 'T' << 8 | 'A'; //DATA\r
+ public static final int BLOCK_DNA1 = 'D' << 24 | 'N' << 16 | 'A' << 8 | '1'; //DNA1\r
+ public static final int BLOCK_ENDB = 'E' << 24 | 'N' << 16 | 'D' << 8 | 'B'; //ENDB\r
+ /** Identifier of the file-block [4 bytes]. */\r
+ private int code;\r
+ /** Total length of the data after the file-block-header [4 bytes]. */\r
+ private int size;\r
+ /**\r
+ * Memory address the structure was located when written to disk [4 or 8 bytes (defined in file header as a pointer\r
+ * size)].\r
+ */\r
+ private long oldMemoryAddress;\r
+ /** Index of the SDNA structure [4 bytes]. */\r
+ private int sdnaIndex;\r
+ /** Number of structure located in this file-block [4 bytes]. */\r
+ private int count;\r
+ /** Start position of the block's data in the stream. */\r
+ private int blockPosition;\r
+\r
+ /**\r
+ * Constructor. Loads the block header from the given stream during instance creation.\r
+ * @param inputStream\r
+ * the stream we read the block header from\r
+ * @param dataRepository\r
+ * the data repository\r
+ * @throws BlenderFileException\r
+ * this exception is thrown when the pointer size is neither 4 nor 8\r
+ */\r
+ public FileBlockHeader(BlenderInputStream inputStream, DataRepository dataRepository) throws BlenderFileException {\r
+ inputStream.alignPosition(4);\r
+ code = inputStream.readByte() << 24 | inputStream.readByte() << 16\r
+ | inputStream.readByte() << 8 | inputStream.readByte();\r
+ size = inputStream.readInt();\r
+ oldMemoryAddress = inputStream.readPointer();\r
+ sdnaIndex = inputStream.readInt();\r
+ count = inputStream.readInt();\r
+ blockPosition = inputStream.getPosition();\r
+ if (FileBlockHeader.BLOCK_DNA1 == code) {\r
+ dataRepository.setBlockData(new DnaBlockData(inputStream, dataRepository));\r
+ } else {\r
+ inputStream.setPosition(blockPosition + size);\r
+ dataRepository.addFileBlockHeader(Long.valueOf(oldMemoryAddress), this);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * This method returns the structure described by the header filled with appropriate data.\r
+ * @param dataRepository\r
+ * the data repository\r
+ * @return structure filled with data\r
+ * @throws BlenderFileException\r
+ */\r
+ public Structure getStructure(DataRepository dataRepository) throws BlenderFileException {\r
+ dataRepository.getInputStream().setPosition(blockPosition);\r
+ Structure structure = dataRepository.getDnaBlockData().getStructure(sdnaIndex);\r
+ structure.fill(dataRepository.getInputStream());\r
+ return structure;\r
+ }\r
+\r
+ /**\r
+ * This method returns the code of this data block.\r
+ * @return the code of this data block\r
+ */\r
+ public int getCode() {\r
+ return code;\r
+ }\r
+\r
+ /**\r
+ * This method returns the size of the data stored in this block.\r
+ * @return the size of the data stored in this block\r
+ */\r
+ public int getSize() {\r
+ return size;\r
+ }\r
+\r
+ /**\r
+ * This method returns the memory address.\r
+ * @return the memory address\r
+ */\r
+ public long getOldMemoryAddress() {\r
+ return oldMemoryAddress;\r
+ }\r
+\r
+ /**\r
+ * This method returns the sdna index.\r
+ * @return the sdna index\r
+ */\r
+ public int getSdnaIndex() {\r
+ return sdnaIndex;\r
+ }\r
+\r
+ /**\r
+ * This data returns the number of structure stored in the data block after this header.\r
+ * @return the number of structure stored in the data block after this header\r
+ */\r
+ public int getCount() {\r
+ return count;\r
+ }\r
+\r
+ /**\r
+ * This method returns the start position of the data block in the blend file stream.\r
+ * @return the start position of the data block\r
+ */\r
+ public int getBlockPosition() {\r
+ return blockPosition;\r
+ }\r
+\r
+ /**\r
+ * This method indicates if the block is the last block in the file.\r
+ * @return true if this block is the last one in the file nad false otherwise\r
+ */\r
+ public boolean isLastBlock() {\r
+ return FileBlockHeader.BLOCK_ENDB == code;\r
+ }\r
+\r
+ /**\r
+ * This method indicates if the block is the SDNA block.\r
+ * @return true if this block is the SDNA block and false otherwise\r
+ */\r
+ public boolean isDnaBlock() {\r
+ return FileBlockHeader.BLOCK_DNA1 == code;\r
+ }\r
+\r
+ @Override\r
+ public String toString() {\r
+ return "FILE BLOCK HEADER [" + this.codeToString(code) + " : " + size + " : " + oldMemoryAddress + " : " + sdnaIndex + " : " + count + "]";\r
+ }\r
+\r
+ /**\r
+ * This method transforms the coded bloch id into a string value.\r
+ * @param code\r
+ * the id of the block\r
+ * @return the string value of the block id\r
+ */\r
+ protected String codeToString(int code) {\r
+ char c1 = (char) ((code & 0xFF000000) >> 24);\r
+ char c2 = (char) ((code & 0xFF0000) >> 16);\r
+ char c3 = (char) ((code & 0xFF00) >> 8);\r
+ char c4 = (char) (code & 0xFF);\r
+ return String.valueOf(c1) + c2 + c3 + c4;\r
+ }\r
}\r
* @author Marcin Roguski\r
*/\r
public class Structure implements Cloneable {\r
- /** The data repository. */\r
- private DataRepository dataRepository;\r
- /** The address of the block that fills the structure. */\r
- private transient Long oldMemoryAddress;\r
- /** The type of the structure. */\r
- private String type;\r
- /**\r
- * The fields of the structure. Each field consists of a pair: name-type.\r
- */\r
- private Field[] fields;\r
\r
- /**\r
- * Constructor that copies the data of the structure.\r
- * @param structure\r
- * the structure to copy.\r
- * @param dataRepository\r
- * the data repository of the structure\r
- * @throws CloneNotSupportedException\r
- * this exception should never be thrown\r
- */\r
- private Structure(Structure structure, DataRepository dataRepository) throws CloneNotSupportedException {\r
- type = structure.type;\r
- fields = new Field[structure.fields.length];\r
- for(int i = 0; i < fields.length; ++i) {\r
- fields[i] = (Field)structure.fields[i].clone();\r
- }\r
- this.dataRepository = dataRepository;\r
- this.oldMemoryAddress = structure.oldMemoryAddress;\r
- }\r
+ /** The data repository. */\r
+ private DataRepository dataRepository;\r
+ /** The address of the block that fills the structure. */\r
+ private transient Long oldMemoryAddress;\r
+ /** The type of the structure. */\r
+ private String type;\r
+ /**\r
+ * The fields of the structure. Each field consists of a pair: name-type.\r
+ */\r
+ private Field[] fields;\r
\r
- /**\r
- * Constructor. Loads the structure from the given stream during instance creation.\r
- * @param inputStream\r
- * the stream we read the structure from\r
- * @param names\r
- * the names from which the name of structure and its fields will be taken\r
- * @param types\r
- * the names of types for the structure\r
- * @param dataRepository\r
- * the data repository\r
- * @throws BlenderFileException\r
- * this exception occurs if the amount of fields, defined in the file, is negative\r
- */\r
- public Structure(BlenderInputStream inputStream, String[] names, String[] types, DataRepository dataRepository) throws BlenderFileException {\r
- int nameIndex = inputStream.readShort();\r
- type = types[nameIndex];\r
- this.dataRepository = dataRepository;\r
- int fieldsAmount = inputStream.readShort();\r
- if(fieldsAmount < 0) {\r
- throw new BlenderFileException("The amount of fields of " + this.type + " structure cannot be negative!");\r
- }\r
- if(fieldsAmount > 0) {\r
- fields = new Field[fieldsAmount];\r
- for(int i = 0; i < fieldsAmount; ++i) {\r
- int typeIndex = inputStream.readShort();\r
- nameIndex = inputStream.readShort();\r
- fields[i] = new Field(names[nameIndex], types[typeIndex], dataRepository);\r
- }\r
- }\r
- this.oldMemoryAddress = Long.valueOf(-1L);\r
- }\r
+ /**\r
+ * Constructor that copies the data of the structure.\r
+ * @param structure\r
+ * the structure to copy.\r
+ * @param dataRepository\r
+ * the data repository of the structure\r
+ * @throws CloneNotSupportedException\r
+ * this exception should never be thrown\r
+ */\r
+ private Structure(Structure structure, DataRepository dataRepository) throws CloneNotSupportedException {\r
+ type = structure.type;\r
+ fields = new Field[structure.fields.length];\r
+ for (int i = 0; i < fields.length; ++i) {\r
+ fields[i] = (Field) structure.fields[i].clone();\r
+ }\r
+ this.dataRepository = dataRepository;\r
+ this.oldMemoryAddress = structure.oldMemoryAddress;\r
+ }\r
\r
- /**\r
- * This method fills the structure with data.\r
- * @param inputStream\r
- * the stream we read data from, its read cursor should be placed at the start position of the data for the\r
- * structure\r
- * @throws BlenderFileException\r
- * an exception is thrown when the blend file is somehow invalid or corrupted\r
- */\r
- public void fill(BlenderInputStream inputStream) throws BlenderFileException {\r
- int position = inputStream.getPosition();\r
- inputStream.setPosition(position - 8 - inputStream.getPointerSize());\r
- this.oldMemoryAddress = Long.valueOf(inputStream.readPointer());\r
- inputStream.setPosition(position);\r
- for(Field field : fields) {\r
- field.fill(inputStream);\r
- }\r
- }\r
+ /**\r
+ * Constructor. Loads the structure from the given stream during instance creation.\r
+ * @param inputStream\r
+ * the stream we read the structure from\r
+ * @param names\r
+ * the names from which the name of structure and its fields will be taken\r
+ * @param types\r
+ * the names of types for the structure\r
+ * @param dataRepository\r
+ * the data repository\r
+ * @throws BlenderFileException\r
+ * this exception occurs if the amount of fields, defined in the file, is negative\r
+ */\r
+ public Structure(BlenderInputStream inputStream, String[] names, String[] types, DataRepository dataRepository) throws BlenderFileException {\r
+ int nameIndex = inputStream.readShort();\r
+ type = types[nameIndex];\r
+ this.dataRepository = dataRepository;\r
+ int fieldsAmount = inputStream.readShort();\r
+ if (fieldsAmount < 0) {\r
+ throw new BlenderFileException("The amount of fields of " + this.type + " structure cannot be negative!");\r
+ }\r
+ if (fieldsAmount > 0) {\r
+ fields = new Field[fieldsAmount];\r
+ for (int i = 0; i < fieldsAmount; ++i) {\r
+ int typeIndex = inputStream.readShort();\r
+ nameIndex = inputStream.readShort();\r
+ fields[i] = new Field(names[nameIndex], types[typeIndex], dataRepository);\r
+ }\r
+ }\r
+ this.oldMemoryAddress = Long.valueOf(-1L);\r
+ }\r
\r
- /**\r
- * This method returns the value of the filed with a given name.\r
- * @param fieldName\r
- * the name of the field\r
- * @return the value of the field or null if no field with a given name is found\r
- */\r
- public Object getFieldValue(String fieldName) {\r
- for(Field field : fields) {\r
- if(field.name.equalsIgnoreCase(fieldName)) {\r
- return field.value;\r
- }\r
- }\r
- return null;\r
- }\r
+ /**\r
+ * This method fills the structure with data.\r
+ * @param inputStream\r
+ * the stream we read data from, its read cursor should be placed at the start position of the data for the\r
+ * structure\r
+ * @throws BlenderFileException\r
+ * an exception is thrown when the blend file is somehow invalid or corrupted\r
+ */\r
+ public void fill(BlenderInputStream inputStream) throws BlenderFileException {\r
+ int position = inputStream.getPosition();\r
+ inputStream.setPosition(position - 8 - inputStream.getPointerSize());\r
+ this.oldMemoryAddress = Long.valueOf(inputStream.readPointer());\r
+ inputStream.setPosition(position);\r
+ for (Field field : fields) {\r
+ field.fill(inputStream);\r
+ }\r
+ }\r
\r
- /**\r
- * This method returns the value of the filed with a given name. The structure is considered to have flat fields\r
- * only (no substructures).\r
- * @param fieldName\r
- * the name of the field\r
- * @return the value of the field or null if no field with a given name is found\r
- */\r
- public Object getFlatFieldValue(String fieldName) {\r
- for(Field field : fields) {\r
- Object value = field.value;\r
- if(field.name.equalsIgnoreCase(fieldName)) {\r
- return value;\r
- } else if(value instanceof Structure) {\r
- value = ((Structure)value).getFlatFieldValue(fieldName);\r
- if(value != null) {//we can compare references here, since we use one static object as a NULL field value\r
- return value;\r
- }\r
- }\r
- }\r
- return null;\r
- }\r
+ /**\r
+ * This method returns the value of the filed with a given name.\r
+ * @param fieldName\r
+ * the name of the field\r
+ * @return the value of the field or null if no field with a given name is found\r
+ */\r
+ public Object getFieldValue(String fieldName) {\r
+ for (Field field : fields) {\r
+ if (field.name.equalsIgnoreCase(fieldName)) {\r
+ return field.value;\r
+ }\r
+ }\r
+ return null;\r
+ }\r
\r
- /**\r
- * This methos should be used on structures that are of a 'ListBase' type. It creates a List of structures that are\r
- * held by this structure within the blend file.\r
- * @param dataRepository\r
- * the data repository\r
- * @return a list of filled structures\r
- * @throws BlenderFileException\r
- * this exception is thrown when the blend file structure is somehow invalid or corrupted\r
- * @throws IllegalArgumentException\r
- * this exception is thrown if the type of the structure is not 'ListBase'\r
- */\r
- public List<Structure> evaluateListBase(DataRepository dataRepository) throws BlenderFileException {\r
- if(!"ListBase".equals(this.type)) {\r
- throw new IllegalStateException("This structure is not of type: 'ListBase'");\r
- }\r
- Pointer first = (Pointer)this.getFieldValue("first");\r
- Pointer last = (Pointer)this.getFieldValue("last");\r
- long currentAddress = 0;\r
- long lastAddress = last.getOldMemoryAddress();\r
- List<Structure> result = new LinkedList<Structure>();\r
- while(currentAddress != lastAddress) {\r
- currentAddress = first.getOldMemoryAddress();\r
- Structure structure = first.fetchData(dataRepository.getInputStream()).get(0);\r
- result.add(structure);\r
- first = (Pointer)structure.getFlatFieldValue("next");\r
- }\r
- return result;\r
- }\r
+ /**\r
+ * This method returns the value of the filed with a given name. The structure is considered to have flat fields\r
+ * only (no substructures).\r
+ * @param fieldName\r
+ * the name of the field\r
+ * @return the value of the field or null if no field with a given name is found\r
+ */\r
+ public Object getFlatFieldValue(String fieldName) {\r
+ for (Field field : fields) {\r
+ Object value = field.value;\r
+ if (field.name.equalsIgnoreCase(fieldName)) {\r
+ return value;\r
+ } else if (value instanceof Structure) {\r
+ value = ((Structure) value).getFlatFieldValue(fieldName);\r
+ if (value != null) {//we can compare references here, since we use one static object as a NULL field value\r
+ return value;\r
+ }\r
+ }\r
+ }\r
+ return null;\r
+ }\r
\r
- /**\r
- * This method returns the type of the structure.\r
- * @return the type of the structure\r
- */\r
- public String getType() {\r
- return type;\r
- }\r
+ /**\r
+ * This methos should be used on structures that are of a 'ListBase' type. It creates a List of structures that are\r
+ * held by this structure within the blend file.\r
+ * @param dataRepository\r
+ * the data repository\r
+ * @return a list of filled structures\r
+ * @throws BlenderFileException\r
+ * this exception is thrown when the blend file structure is somehow invalid or corrupted\r
+ * @throws IllegalArgumentException\r
+ * this exception is thrown if the type of the structure is not 'ListBase'\r
+ */\r
+ public List<Structure> evaluateListBase(DataRepository dataRepository) throws BlenderFileException {\r
+ if (!"ListBase".equals(this.type)) {\r
+ throw new IllegalStateException("This structure is not of type: 'ListBase'");\r
+ }\r
+ Pointer first = (Pointer) this.getFieldValue("first");\r
+ Pointer last = (Pointer) this.getFieldValue("last");\r
+ long currentAddress = 0;\r
+ long lastAddress = last.getOldMemoryAddress();\r
+ List<Structure> result = new LinkedList<Structure>();\r
+ while (currentAddress != lastAddress) {\r
+ currentAddress = first.getOldMemoryAddress();\r
+ Structure structure = first.fetchData(dataRepository.getInputStream()).get(0);\r
+ result.add(structure);\r
+ first = (Pointer) structure.getFlatFieldValue("next");\r
+ }\r
+ return result;\r
+ }\r
\r
- /**\r
- * This method returns the amount of fields for the current structure.\r
- * @return the amount of fields for the current structure\r
- */\r
- public int getFieldsAmount() {\r
- return fields.length;\r
- }\r
+ /**\r
+ * This method returns the type of the structure.\r
+ * @return the type of the structure\r
+ */\r
+ public String getType() {\r
+ return type;\r
+ }\r
\r
- /**\r
- * This method returns the field name of the given index.\r
- * @param fieldIndex\r
- * the index of the field\r
- * @return the field name of the given index\r
- */\r
- public String getFieldName(int fieldIndex) {\r
- return fields[fieldIndex].name;\r
- }\r
+ /**\r
+ * This method returns the amount of fields for the current structure.\r
+ * @return the amount of fields for the current structure\r
+ */\r
+ public int getFieldsAmount() {\r
+ return fields.length;\r
+ }\r
\r
- /**\r
- * This method returns the field type of the given index.\r
- * @param fieldIndex\r
- * the index of the field\r
- * @return the field type of the given index\r
- */\r
- public String getFieldType(int fieldIndex) {\r
- return fields[fieldIndex].type;\r
- }\r
+ /**\r
+ * This method returns the field name of the given index.\r
+ * @param fieldIndex\r
+ * the index of the field\r
+ * @return the field name of the given index\r
+ */\r
+ public String getFieldName(int fieldIndex) {\r
+ return fields[fieldIndex].name;\r
+ }\r
\r
- /**\r
- * This method returns the address of the structure. The strucutre should be filled with data otherwise an exception\r
- * is thrown.\r
- * @return the address of the feature stored in this structure\r
- */\r
- public Long getOldMemoryAddress() {\r
- if(oldMemoryAddress.longValue() == -1L) {\r
- throw new IllegalStateException("Call the 'fill' method and fill the structure with data first!");\r
- }\r
- return oldMemoryAddress;\r
- }\r
+ /**\r
+ * This method returns the field type of the given index.\r
+ * @param fieldIndex\r
+ * the index of the field\r
+ * @return the field type of the given index\r
+ */\r
+ public String getFieldType(int fieldIndex) {\r
+ return fields[fieldIndex].type;\r
+ }\r
\r
- /**\r
- * This method returns the name of the structure. If the structure has an ID field then the name is returned.\r
- * Otherwise the name does not exists and the method returns null.\r
- * @return the name of the structure read from the ID field or null\r
- */\r
- public String getName() {\r
- Structure id = (Structure)this.getFieldValue("ID");\r
- return id == null ? null : id.getFieldValue("name").toString().substring(2);//blender adds 2-charactes as a name prefix\r
- }\r
+ /**\r
+ * This method returns the address of the structure. The strucutre should be filled with data otherwise an exception\r
+ * is thrown.\r
+ * @return the address of the feature stored in this structure\r
+ */\r
+ public Long getOldMemoryAddress() {\r
+ if (oldMemoryAddress.longValue() == -1L) {\r
+ throw new IllegalStateException("Call the 'fill' method and fill the structure with data first!");\r
+ }\r
+ return oldMemoryAddress;\r
+ }\r
\r
- @Override\r
- public String toString() {\r
- StringBuilder result = new StringBuilder("struct ").append(type).append(" {\n");\r
- for(int i = 0; i < fields.length; ++i) {\r
- result.append(fields[i].toString()).append('\n');\r
- }\r
- return result.append('}').toString();\r
- }\r
+ /**\r
+ * This method returns the name of the structure. If the structure has an ID field then the name is returned.\r
+ * Otherwise the name does not exists and the method returns null.\r
+ * @return the name of the structure read from the ID field or null\r
+ */\r
+ public String getName() {\r
+ Structure id = (Structure) this.getFieldValue("ID");\r
+ return id == null ? null : id.getFieldValue("name").toString().substring(2);//blender adds 2-charactes as a name prefix\r
+ }\r
\r
- @Override\r
- public Object clone() throws CloneNotSupportedException {\r
- return new Structure(this, dataRepository);\r
- }\r
+ @Override\r
+ public String toString() {\r
+ StringBuilder result = new StringBuilder("struct ").append(type).append(" {\n");\r
+ for (int i = 0; i < fields.length; ++i) {\r
+ result.append(fields[i].toString()).append('\n');\r
+ }\r
+ return result.append('}').toString();\r
+ }\r
\r
- /**\r
- * This enum enumerates all known data types that can be found in the blend file.\r
- * @author Marcin Roguski\r
- */\r
- /*package*/static enum DataType {\r
- CHARACTER, SHORT, INTEGER, LONG, FLOAT, DOUBLE, VOID, STRUCTURE, POINTER;\r
+ @Override\r
+ public Object clone() throws CloneNotSupportedException {\r
+ return new Structure(this, dataRepository);\r
+ }\r
\r
- /** The map containing the known primary types. */\r
- private static final Map<String, DataType> PRIMARY_TYPES = new HashMap<String, DataType>(10);\r
- static {\r
- PRIMARY_TYPES.put("char", CHARACTER);\r
- PRIMARY_TYPES.put("uchar", CHARACTER);\r
- PRIMARY_TYPES.put("short", SHORT);\r
- PRIMARY_TYPES.put("ushort", SHORT);\r
- PRIMARY_TYPES.put("int", INTEGER);\r
- PRIMARY_TYPES.put("long", LONG);\r
- PRIMARY_TYPES.put("ulong", LONG);\r
- PRIMARY_TYPES.put("float", FLOAT);\r
- PRIMARY_TYPES.put("double", DOUBLE);\r
- PRIMARY_TYPES.put("void", VOID);\r
- }\r
+ /**\r
+ * This enum enumerates all known data types that can be found in the blend file.\r
+ * @author Marcin Roguski\r
+ */\r
+ /*package*/\r
+ static enum DataType {\r
\r
- /**\r
- * This method returns the data type that is appropriate to the given type name. WARNING! The type recognition\r
- * is case sensitive!\r
- * @param type\r
- * the type name of the data\r
- * @param dataRepository\r
- * the data repository\r
- * @return appropriate enum value to the given type name\r
- * @throws BlenderFileException\r
- * this exception is thrown if the given type name does not exist in the blend file\r
- */\r
- public static DataType getDataType(String type, DataRepository dataRepository) throws BlenderFileException {\r
- DataType result = PRIMARY_TYPES.get(type);\r
- if(result != null) {\r
- return result;\r
- }\r
- if(dataRepository.getDnaBlockData().hasStructure(type)) {\r
- return STRUCTURE;\r
- }\r
- throw new BlenderFileException("Unknown data type: " + type);\r
- }\r
- }\r
+ CHARACTER, SHORT, INTEGER, LONG, FLOAT, DOUBLE, VOID, STRUCTURE, POINTER;\r
+ /** The map containing the known primary types. */\r
+ private static final Map<String, DataType> PRIMARY_TYPES = new HashMap<String, DataType>(10);\r
+\r
+ static {\r
+ PRIMARY_TYPES.put("char", CHARACTER);\r
+ PRIMARY_TYPES.put("uchar", CHARACTER);\r
+ PRIMARY_TYPES.put("short", SHORT);\r
+ PRIMARY_TYPES.put("ushort", SHORT);\r
+ PRIMARY_TYPES.put("int", INTEGER);\r
+ PRIMARY_TYPES.put("long", LONG);\r
+ PRIMARY_TYPES.put("ulong", LONG);\r
+ PRIMARY_TYPES.put("float", FLOAT);\r
+ PRIMARY_TYPES.put("double", DOUBLE);\r
+ PRIMARY_TYPES.put("void", VOID);\r
+ }\r
+\r
+ /**\r
+ * This method returns the data type that is appropriate to the given type name. WARNING! The type recognition\r
+ * is case sensitive!\r
+ * @param type\r
+ * the type name of the data\r
+ * @param dataRepository\r
+ * the data repository\r
+ * @return appropriate enum value to the given type name\r
+ * @throws BlenderFileException\r
+ * this exception is thrown if the given type name does not exist in the blend file\r
+ */\r
+ public static DataType getDataType(String type, DataRepository dataRepository) throws BlenderFileException {\r
+ DataType result = PRIMARY_TYPES.get(type);\r
+ if (result != null) {\r
+ return result;\r
+ }\r
+ if (dataRepository.getDnaBlockData().hasStructure(type)) {\r
+ return STRUCTURE;\r
+ }\r
+ throw new BlenderFileException("Unknown data type: " + type);\r
+ }\r
+ }\r
}\r
* @author Marcin Roguski\r
*/\r
public class BlenderFileException extends Exception {\r
- private static final long serialVersionUID = 7573482836437866767L;\r
\r
- /**\r
- * Constructor. Creates an exception with no description.\r
- */\r
- public BlenderFileException() {}\r
+ private static final long serialVersionUID = 7573482836437866767L;\r
\r
- /**\r
- * Constructor. Creates an exception containing the given message.\r
- * @param message\r
- * the message describing the problem that occured\r
- */\r
- public BlenderFileException(String message) {\r
- super(message);\r
- }\r
+ /**\r
+ * Constructor. Creates an exception with no description.\r
+ */\r
+ public BlenderFileException() {\r
+ }\r
\r
- /**\r
- * Constructor. Creates an exception that is based upon other thrown object. It contains the whole stacktrace then.\r
- * @param throwable\r
- * an exception/error that occured\r
- */\r
- public BlenderFileException(Throwable throwable) {\r
- super(throwable);\r
- }\r
+ /**\r
+ * Constructor. Creates an exception containing the given message.\r
+ * @param message\r
+ * the message describing the problem that occured\r
+ */\r
+ public BlenderFileException(String message) {\r
+ super(message);\r
+ }\r
\r
- /**\r
- * Constructor. Creates an exception with both a message and stacktrace.\r
- * @param message\r
- * the message describing the problem that occured\r
- * @param throwable\r
- * an exception/error that occured\r
- */\r
- public BlenderFileException(String message, Throwable throwable) {\r
- super(message, throwable);\r
- }\r
+ /**\r
+ * Constructor. Creates an exception that is based upon other thrown object. It contains the whole stacktrace then.\r
+ * @param throwable\r
+ * an exception/error that occured\r
+ */\r
+ public BlenderFileException(Throwable throwable) {\r
+ super(throwable);\r
+ }\r
+\r
+ /**\r
+ * Constructor. Creates an exception with both a message and stacktrace.\r
+ * @param message\r
+ * the message describing the problem that occured\r
+ * @param throwable\r
+ * an exception/error that occured\r
+ */\r
+ public BlenderFileException(String message, Throwable throwable) {\r
+ super(message, throwable);\r
+ }\r
}\r
import com.jme3.scene.plugins.blender.utils.DataRepository;\r
import com.jme3.scene.plugins.blender.utils.Pointer;\r
\r
-\r
/**\r
* This class defines the methods to calculate certain aspects of animation and armature functionalities.\r
* @author Marcin Roguski\r
*/\r
public class ArmatureHelper extends com.jme3.scene.plugins.blender.helpers.v249.ArmatureHelper {\r
- private static final Logger LOGGER = Logger.getLogger(ArmatureHelper.class.getName());\r
- \r
- /**\r
- * This constructor parses the given blender version and stores the result. Some functionalities may differ in\r
- * different blender versions.\r
- * @param blenderVersion\r
- * the version read from the blend file\r
- */\r
- public ArmatureHelper(String blenderVersion) {\r
- super(blenderVersion);\r
- }\r
- \r
- @Override\r
- public BoneTrack[] getTracks(Structure actionStructure, DataRepository dataRepository, String objectName, String animationName) throws BlenderFileException {\r
- if(blenderVersion<250) {\r
- return super.getTracks(actionStructure, dataRepository, objectName, animationName);\r
- }\r
- LOGGER.log(Level.INFO, "Getting tracks!");\r
- int fps = dataRepository.getBlenderKey().getFps();\r
- int[] animationFrames = dataRepository.getBlenderKey().getAnimationFrames(objectName, animationName);\r
- Structure groups = (Structure)actionStructure.getFieldValue("groups");\r
- List<Structure> actionGroups = groups.evaluateListBase(dataRepository);//bActionGroup\r
- if(actionGroups != null && actionGroups.size() > 0 && (bonesMap == null || bonesMap.size() == 0)) {\r
- throw new IllegalStateException("No bones found! Cannot proceed to calculating tracks!");\r
- }\r
- \r
- List<BoneTrack> tracks = new ArrayList<BoneTrack>();\r
- for(Structure actionGroup : actionGroups) {\r
- String name = actionGroup.getFieldValue("name").toString();\r
- Integer boneIndex = bonesMap.get(name);\r
- if(boneIndex != null) {\r
- List<Structure> channels = ((Structure)actionGroup.getFieldValue("channels")).evaluateListBase(dataRepository);\r
- BezierCurve[] bezierCurves = new BezierCurve[channels.size()];\r
- int channelCounter = 0;\r
- for(Structure c : channels) {\r
- //reading rna path first\r
- BlenderInputStream bis = dataRepository.getInputStream();\r
- int currentPosition = bis.getPosition();\r
- Pointer pRnaPath = (Pointer) c.getFieldValue("rna_path");\r
- FileBlockHeader dataFileBlock = dataRepository.getFileBlock(pRnaPath.getOldMemoryAddress());\r
- bis.setPosition(dataFileBlock.getBlockPosition());\r
- String rnaPath = bis.readString();\r
- bis.setPosition(currentPosition);\r
- int arrayIndex = ((Number)c.getFieldValue("array_index")).intValue();\r
- int type = this.getCurveType(rnaPath, arrayIndex);\r
\r
- Pointer pBezTriple = (Pointer)c.getFieldValue("bezt");\r
- List<Structure> bezTriples = pBezTriple.fetchData(dataRepository.getInputStream());\r
- bezierCurves[channelCounter++] = new BezierCurve(type, bezTriples, 2);\r
- }\r
+ private static final Logger LOGGER = Logger.getLogger(ArmatureHelper.class.getName());\r
+\r
+ /**\r
+ * This constructor parses the given blender version and stores the result. Some functionalities may differ in\r
+ * different blender versions.\r
+ * @param blenderVersion\r
+ * the version read from the blend file\r
+ */\r
+ public ArmatureHelper(String blenderVersion) {\r
+ super(blenderVersion);\r
+ }\r
+\r
+ @Override\r
+ public BoneTrack[] getTracks(Structure actionStructure, DataRepository dataRepository, String objectName, String animationName) throws BlenderFileException {\r
+ if (blenderVersion < 250) {\r
+ return super.getTracks(actionStructure, dataRepository, objectName, animationName);\r
+ }\r
+ LOGGER.log(Level.INFO, "Getting tracks!");\r
+ int fps = dataRepository.getBlenderKey().getFps();\r
+ int[] animationFrames = dataRepository.getBlenderKey().getAnimationFrames(objectName, animationName);\r
+ Structure groups = (Structure) actionStructure.getFieldValue("groups");\r
+ List<Structure> actionGroups = groups.evaluateListBase(dataRepository);//bActionGroup\r
+ if (actionGroups != null && actionGroups.size() > 0 && (bonesMap == null || bonesMap.size() == 0)) {\r
+ throw new IllegalStateException("No bones found! Cannot proceed to calculating tracks!");\r
+ }\r
+\r
+ List<BoneTrack> tracks = new ArrayList<BoneTrack>();\r
+ for (Structure actionGroup : actionGroups) {\r
+ String name = actionGroup.getFieldValue("name").toString();\r
+ Integer boneIndex = bonesMap.get(name);\r
+ if (boneIndex != null) {\r
+ List<Structure> channels = ((Structure) actionGroup.getFieldValue("channels")).evaluateListBase(dataRepository);\r
+ BezierCurve[] bezierCurves = new BezierCurve[channels.size()];\r
+ int channelCounter = 0;\r
+ for (Structure c : channels) {\r
+ //reading rna path first\r
+ BlenderInputStream bis = dataRepository.getInputStream();\r
+ int currentPosition = bis.getPosition();\r
+ Pointer pRnaPath = (Pointer) c.getFieldValue("rna_path");\r
+ FileBlockHeader dataFileBlock = dataRepository.getFileBlock(pRnaPath.getOldMemoryAddress());\r
+ bis.setPosition(dataFileBlock.getBlockPosition());\r
+ String rnaPath = bis.readString();\r
+ bis.setPosition(currentPosition);\r
+ int arrayIndex = ((Number) c.getFieldValue("array_index")).intValue();\r
+ int type = this.getCurveType(rnaPath, arrayIndex);\r
+\r
+ Pointer pBezTriple = (Pointer) c.getFieldValue("bezt");\r
+ List<Structure> bezTriples = pBezTriple.fetchData(dataRepository.getInputStream());\r
+ bezierCurves[channelCounter++] = new BezierCurve(type, bezTriples, 2);\r
+ }\r
+\r
+ Ipo ipo = new Ipo(bezierCurves);\r
+ tracks.add(ipo.calculateTrack(boneIndex.intValue(), animationFrames[0], animationFrames[1], fps));\r
+ }\r
+ }\r
+ return tracks.toArray(new BoneTrack[tracks.size()]);\r
+ }\r
\r
- Ipo ipo = new Ipo(bezierCurves);\r
- tracks.add(ipo.calculateTrack(boneIndex.intValue(), animationFrames[0], animationFrames[1], fps));\r
- }\r
- }\r
- return tracks.toArray(new BoneTrack[tracks.size()]);\r
- }\r
- \r
- /**\r
- * This method parses the information stored inside the curve rna path and returns the proper type\r
- * of the curve.\r
- * @param rnaPath the curve's rna path\r
- * @param arrayIndex the array index of the stored data\r
- * @return the type of the curve\r
- */\r
- protected int getCurveType(String rnaPath, int arrayIndex) {\r
- if(rnaPath.endsWith(".location")) {\r
- return Ipo.AC_LOC_X + arrayIndex;\r
- }\r
- if(rnaPath.endsWith(".rotation_quaternion")) {\r
- return Ipo.AC_QUAT_W + arrayIndex;\r
- }\r
- if(rnaPath.endsWith(".scale")) {\r
- return Ipo.AC_SIZE_X + arrayIndex;\r
- }\r
- throw new IllegalStateException("Unknown curve rna path: " + rnaPath);\r
- }\r
+ /**\r
+ * This method parses the information stored inside the curve rna path and returns the proper type\r
+ * of the curve.\r
+ * @param rnaPath the curve's rna path\r
+ * @param arrayIndex the array index of the stored data\r
+ * @return the type of the curve\r
+ */\r
+ protected int getCurveType(String rnaPath, int arrayIndex) {\r
+ if (rnaPath.endsWith(".location")) {\r
+ return Ipo.AC_LOC_X + arrayIndex;\r
+ }\r
+ if (rnaPath.endsWith(".rotation_quaternion")) {\r
+ return Ipo.AC_QUAT_W + arrayIndex;\r
+ }\r
+ if (rnaPath.endsWith(".scale")) {\r
+ return Ipo.AC_SIZE_X + arrayIndex;\r
+ }\r
+ throw new IllegalStateException("Unknown curve rna path: " + rnaPath);\r
+ }\r
}\r
* @author Marcin Roguski\r
*/\r
public class CameraHelper extends com.jme3.scene.plugins.blender.helpers.v249.CameraHelper {\r
- private static final Logger LOGGER = Logger.getLogger(CameraHelper.class.getName());\r
- \r
- /**\r
- * This constructor parses the given blender version and stores the result. Some functionalities may differ in\r
- * different blender versions.\r
- * @param blenderVersion\r
- * the version read from the blend file\r
- */\r
- public CameraHelper(String blenderVersion) {\r
- super(blenderVersion);\r
- }\r
- \r
- @Override\r
- public Camera toCamera(Structure structure) throws BlenderFileException {\r
- if(blenderVersion<250) {\r
- return super.toCamera(structure);\r
- }\r
- Camera result = new Camera(DEFAULT_CAM_WIDTH, DEFAULT_CAM_HEIGHT);\r
- int type = ((Number)structure.getFieldValue("type")).intValue();\r
- if(type != 0 && type != 1) {\r
- LOGGER.log(Level.WARNING, "Unknown camera type: " + type + ". Perspective camera is being used!");\r
- type = 0;\r
- }\r
- //type==0 - perspective; type==1 - orthographic; perspective is used as default\r
- result.setParallelProjection(type == 1);\r
- float aspect = 0;\r
- float clipsta = ((Number)structure.getFieldValue("clipsta")).floatValue();\r
- float clipend = ((Number)structure.getFieldValue("clipend")).floatValue();\r
- if(type == 0) {\r
- aspect = ((Number)structure.getFieldValue("lens")).floatValue();\r
- } else {\r
- aspect = ((Number)structure.getFieldValue("ortho_scale")).floatValue();\r
- }\r
- result.setFrustumPerspective(45, aspect, clipsta, clipend);\r
- return result;\r
- }\r
+\r
+ private static final Logger LOGGER = Logger.getLogger(CameraHelper.class.getName());\r
+\r
+ /**\r
+ * This constructor parses the given blender version and stores the result. Some functionalities may differ in\r
+ * different blender versions.\r
+ * @param blenderVersion\r
+ * the version read from the blend file\r
+ */\r
+ public CameraHelper(String blenderVersion) {\r
+ super(blenderVersion);\r
+ }\r
+\r
+ @Override\r
+ public Camera toCamera(Structure structure) throws BlenderFileException {\r
+ if (blenderVersion < 250) {\r
+ return super.toCamera(structure);\r
+ }\r
+ Camera result = new Camera(DEFAULT_CAM_WIDTH, DEFAULT_CAM_HEIGHT);\r
+ int type = ((Number) structure.getFieldValue("type")).intValue();\r
+ if (type != 0 && type != 1) {\r
+ LOGGER.log(Level.WARNING, "Unknown camera type: {0}. Perspective camera is being used!", type);\r
+ type = 0;\r
+ }\r
+ //type==0 - perspective; type==1 - orthographic; perspective is used as default\r
+ result.setParallelProjection(type == 1);\r
+ float aspect = 0;\r
+ float clipsta = ((Number) structure.getFieldValue("clipsta")).floatValue();\r
+ float clipend = ((Number) structure.getFieldValue("clipend")).floatValue();\r
+ if (type == 0) {\r
+ aspect = ((Number) structure.getFieldValue("lens")).floatValue();\r
+ } else {\r
+ aspect = ((Number) structure.getFieldValue("ortho_scale")).floatValue();\r
+ }\r
+ result.setFrustumPerspective(45, aspect, clipsta, clipend);\r
+ return result;\r
+ }\r
}\r
* @author Marcin Roguski\r
*/\r
public class ConstraintHelper extends com.jme3.scene.plugins.blender.helpers.v249.ConstraintHelper {\r
- private static final Logger LOGGER = Logger.getLogger(ConstraintHelper.class.getName());\r
- \r
- /**\r
- * Helper constructor. It's main task is to generate the affection functions. These functions are common to all\r
- * ConstraintHelper instances. Unfortunately this constructor might grow large. If it becomes too large - I shall\r
- * consider refactoring. The constructor parses the given blender version and stores the result. Some\r
- * functionalities may differ in different blender versions.\r
- * @param blenderVersion\r
- * the version read from the blend file\r
- */\r
- public ConstraintHelper(String blenderVersion, DataRepository dataRepository) {\r
- super(blenderVersion, dataRepository);\r
- }\r
- \r
- @Override\r
- public void loadConstraints(Structure objectStructure, DataRepository dataRepository) throws BlenderFileException {\r
- if(blenderVersion<250) {\r
- super.loadConstraints(objectStructure, dataRepository);\r
- } else {\r
- LOGGER.warning("Loading of constraints not yet implemented for version 2.5x !");\r
- //TODO: to implement\r
- }\r
- }\r
+\r
+ private static final Logger LOGGER = Logger.getLogger(ConstraintHelper.class.getName());\r
+\r
+ /**\r
+ * Helper constructor. It's main task is to generate the affection functions. These functions are common to all\r
+ * ConstraintHelper instances. Unfortunately this constructor might grow large. If it becomes too large - I shall\r
+ * consider refactoring. The constructor parses the given blender version and stores the result. Some\r
+ * functionalities may differ in different blender versions.\r
+ * @param blenderVersion\r
+ * the version read from the blend file\r
+ */\r
+ public ConstraintHelper(String blenderVersion, DataRepository dataRepository) {\r
+ super(blenderVersion, dataRepository);\r
+ }\r
+\r
+ @Override\r
+ public void loadConstraints(Structure objectStructure, DataRepository dataRepository) throws BlenderFileException {\r
+ if (blenderVersion < 250) {\r
+ super.loadConstraints(objectStructure, dataRepository);\r
+ } else {\r
+ LOGGER.warning("Loading of constraints not yet implemented for version 2.5x !");\r
+ //TODO: to implement\r
+ }\r
+ }\r
}\r
* @author Marcin Roguski\r
*/\r
public class CurvesHelper extends com.jme3.scene.plugins.blender.helpers.v249.CurvesHelper {\r
- /**\r
- * This constructor parses the given blender version and stores the result. Some functionalities may differ in\r
- * different blender versions.\r
- * @param blenderVersion\r
- * the version read from the blend file\r
- */\r
- public CurvesHelper(String blenderVersion) {\r
- super(blenderVersion);\r
- }\r
+\r
+ /**\r
+ * This constructor parses the given blender version and stores the result. Some functionalities may differ in\r
+ * different blender versions.\r
+ * @param blenderVersion\r
+ * the version read from the blend file\r
+ */\r
+ public CurvesHelper(String blenderVersion) {\r
+ super(blenderVersion);\r
+ }\r
}\r
* @author Marcin Roguski\r
*/\r
public class IpoHelper extends com.jme3.scene.plugins.blender.helpers.v249.IpoHelper {\r
- /**\r
- * This constructor parses the given blender version and stores the result. Some functionalities may differ in\r
- * different blender versions.\r
- * @param blenderVersion\r
- * the version read from the blend file\r
- */\r
- public IpoHelper(String blenderVersion) {\r
- super(blenderVersion);\r
- }\r
+\r
+ /**\r
+ * This constructor parses the given blender version and stores the result. Some functionalities may differ in\r
+ * different blender versions.\r
+ * @param blenderVersion\r
+ * the version read from the blend file\r
+ */\r
+ public IpoHelper(String blenderVersion) {\r
+ super(blenderVersion);\r
+ }\r
}\r
* @author Marcin Roguski\r
*/\r
public class LightHelper extends com.jme3.scene.plugins.blender.helpers.v249.LightHelper {\r
- /**\r
- * This constructor parses the given blender version and stores the result. Some functionalities may differ in\r
- * different blender versions.\r
- * @param blenderVersion\r
- * the version read from the blend file\r
- */\r
- public LightHelper(String blenderVersion) {\r
- super(blenderVersion);\r
- }\r
+\r
+ /**\r
+ * This constructor parses the given blender version and stores the result. Some functionalities may differ in\r
+ * different blender versions.\r
+ * @param blenderVersion\r
+ * the version read from the blend file\r
+ */\r
+ public LightHelper(String blenderVersion) {\r
+ super(blenderVersion);\r
+ }\r
}\r
package com.jme3.scene.plugins.blender.helpers;\r
\r
public class MaterialHelper extends com.jme3.scene.plugins.blender.helpers.v249.MaterialHelper {\r
- /**\r
- * This constructor parses the given blender version and stores the result. Some functionalities may differ in\r
- * different blender versions.\r
- * @param blenderVersion\r
- * the version read from the blend file\r
- */\r
- public MaterialHelper(String blenderVersion) {\r
- super(blenderVersion);\r
- }\r
+\r
+ /**\r
+ * This constructor parses the given blender version and stores the result. Some functionalities may differ in\r
+ * different blender versions.\r
+ * @param blenderVersion\r
+ * the version read from the blend file\r
+ */\r
+ public MaterialHelper(String blenderVersion) {\r
+ super(blenderVersion);\r
+ }\r
}\r
* @author Marcin Roguski\r
*/\r
public class MeshHelper extends com.jme3.scene.plugins.blender.helpers.v249.MeshHelper {\r
- /**\r
- * This constructor parses the given blender version and stores the result. Some functionalities may differ in\r
- * different blender versions.\r
- * @param blenderVersion\r
- * the version read from the blend file\r
- */\r
- public MeshHelper(String blenderVersion) {\r
- super(blenderVersion);\r
- }\r
+\r
+ /**\r
+ * This constructor parses the given blender version and stores the result. Some functionalities may differ in\r
+ * different blender versions.\r
+ * @param blenderVersion\r
+ * the version read from the blend file\r
+ */\r
+ public MeshHelper(String blenderVersion) {\r
+ super(blenderVersion);\r
+ }\r
}\r
* @author Marcin Roguski\r
*/\r
public class ModifierHelper extends com.jme3.scene.plugins.blender.helpers.v249.ModifierHelper {\r
- /**\r
- * This constructor parses the given blender version and stores the result. Some functionalities may differ in\r
- * different blender versions.\r
- * @param blenderVersion\r
- * the version read from the blend file\r
- */\r
- public ModifierHelper(String blenderVersion) {\r
- super(blenderVersion);\r
- }\r
+\r
+ /**\r
+ * This constructor parses the given blender version and stores the result. Some functionalities may differ in\r
+ * different blender versions.\r
+ * @param blenderVersion\r
+ * the version read from the blend file\r
+ */\r
+ public ModifierHelper(String blenderVersion) {\r
+ super(blenderVersion);\r
+ }\r
}\r
* @author Marcin Roguski (Kaelthas)\r
*/\r
public class NoiseHelper extends com.jme3.scene.plugins.blender.helpers.v249.NoiseHelper {\r
- /**\r
- * Constructor. Stores the blender version number and loads the constants needed for computations.\r
- * \r
- * @param blenderVersion\r
- * the number of blender version\r
- */\r
- public NoiseHelper(String blenderVersion) {\r
- super(blenderVersion);\r
- }\r
+\r
+ /**\r
+ * Constructor. Stores the blender version number and loads the constants needed for computations.\r
+ * \r
+ * @param blenderVersion\r
+ * the number of blender version\r
+ */\r
+ public NoiseHelper(String blenderVersion) {\r
+ super(blenderVersion);\r
+ }\r
}\r
* @author Marcin Roguski\r
*/\r
public class ObjectHelper extends com.jme3.scene.plugins.blender.helpers.v249.ObjectHelper {\r
- /**\r
- * This constructor parses the given blender version and stores the result. Some functionalities may differ in\r
- * different blender versions.\r
- * @param blenderVersion\r
- * the version read from the blend file\r
- */\r
- public ObjectHelper(String blenderVersion) {\r
- super(blenderVersion);\r
- }\r
+\r
+ /**\r
+ * This constructor parses the given blender version and stores the result. Some functionalities may differ in\r
+ * different blender versions.\r
+ * @param blenderVersion\r
+ * the version read from the blend file\r
+ */\r
+ public ObjectHelper(String blenderVersion) {\r
+ super(blenderVersion);\r
+ }\r
}\r
* @author Marcin Roguski (Kaelthas)\r
*/\r
public class ParticlesHelper extends com.jme3.scene.plugins.blender.helpers.v249.ParticlesHelper {\r
- /**\r
- * This constructor parses the given blender version and stores the result. Some functionalities may differ in\r
- * different blender versions.\r
- * @param blenderVersion\r
- * the version read from the blend file\r
- */\r
- public ParticlesHelper(String blenderVersion) {\r
- super(blenderVersion);\r
- }\r
+\r
+ /**\r
+ * This constructor parses the given blender version and stores the result. Some functionalities may differ in\r
+ * different blender versions.\r
+ * @param blenderVersion\r
+ * the version read from the blend file\r
+ */\r
+ public ParticlesHelper(String blenderVersion) {\r
+ super(blenderVersion);\r
+ }\r
}\r
* @author Marcin Roguski\r
*/\r
public class TextureHelper extends com.jme3.scene.plugins.blender.helpers.v249.TextureHelper {\r
- private static final Logger LOGGER = Logger.getLogger(TextureHelper.class.getName());\r
- public static final int TEX_POINTDENSITY = 14;\r
- public static final int TEX_VOXELDATA = 15;\r
- /**\r
- * This constructor parses the given blender version and stores the result. Some functionalities may differ in\r
- * different blender versions.\r
- * @param blenderVersion\r
- * the version read from the blend file\r
- */\r
- public TextureHelper(String blenderVersion) {\r
- super(blenderVersion);\r
- }\r
- \r
- @Override\r
- public Texture getTexture(Structure tex, DataRepository dataRepository) throws BlenderFileException {\r
- if(blenderVersion<250) {\r
- return super.getTexture(tex, dataRepository);\r
- }\r
- Texture result = (Texture) dataRepository.getLoadedFeature(tex.getOldMemoryAddress(), LoadedFeatureDataType.LOADED_FEATURE);\r
- if (result != null) {\r
- return result;\r
- }\r
- int type = ((Number)tex.getFieldValue("type")).intValue();\r
- switch(type) {\r
- case TEX_POINTDENSITY:\r
- LOGGER.warning("Point density texture loading currently not supported!");\r
- break;\r
- case TEX_VOXELDATA:\r
- LOGGER.warning("Voxel data texture loading currently not supported!");\r
- break;\r
- default:\r
- result = super.getTexture(tex, dataRepository);\r
- }\r
- return result;\r
- }\r
+\r
+ private static final Logger LOGGER = Logger.getLogger(TextureHelper.class.getName());\r
+ public static final int TEX_POINTDENSITY = 14;\r
+ public static final int TEX_VOXELDATA = 15;\r
+\r
+ /**\r
+ * This constructor parses the given blender version and stores the result. Some functionalities may differ in\r
+ * different blender versions.\r
+ * @param blenderVersion\r
+ * the version read from the blend file\r
+ */\r
+ public TextureHelper(String blenderVersion) {\r
+ super(blenderVersion);\r
+ }\r
+\r
+ @Override\r
+ public Texture getTexture(Structure tex, DataRepository dataRepository) throws BlenderFileException {\r
+ if (blenderVersion < 250) {\r
+ return super.getTexture(tex, dataRepository);\r
+ }\r
+ Texture result = (Texture) dataRepository.getLoadedFeature(tex.getOldMemoryAddress(), LoadedFeatureDataType.LOADED_FEATURE);\r
+ if (result != null) {\r
+ return result;\r
+ }\r
+ int type = ((Number) tex.getFieldValue("type")).intValue();\r
+ switch (type) {\r
+ case TEX_POINTDENSITY:\r
+ LOGGER.warning("Point density texture loading currently not supported!");\r
+ break;\r
+ case TEX_VOXELDATA:\r
+ LOGGER.warning("Voxel data texture loading currently not supported!");\r
+ break;\r
+ default:\r
+ result = super.getTexture(tex, dataRepository);\r
+ }\r
+ return result;\r
+ }\r
}\r
* @author Marcin Roguski\r
*/\r
public class ArmatureHelper extends AbstractBlenderHelper {\r
- private static final Logger LOGGER = Logger.getLogger(ArmatureHelper.class.getName());\r
\r
- /**\r
- * The map of the bones. Maps a bone name to its index in the armature. Should be cleared after the object had been\r
- * read. TODO: probably bones can have identical names in different armatures\r
- */\r
- protected Map<String, Integer> bonesMap = new HashMap<String, Integer>();\r
- /** A map of bones and their old memory addresses. */\r
- protected Map<Bone, Long> bonesOMAs = new HashMap<Bone, Long>();\r
- /** This list contains bones hierarchy and their matrices. It is later converted into jme bones. */\r
- protected List<BoneTransformationData> boneDataRoots = new ArrayList<BoneTransformationData>();\r
- \r
- /**\r
- * This constructor parses the given blender version and stores the result. Some functionalities may differ in\r
- * different blender versions.\r
- * @param blenderVersion\r
- * the version read from the blend file\r
- */\r
- public ArmatureHelper(String blenderVersion) {\r
- super(blenderVersion);\r
- }\r
+ private static final Logger LOGGER = Logger.getLogger(ArmatureHelper.class.getName());\r
+ /**\r
+ * The map of the bones. Maps a bone name to its index in the armature. Should be cleared after the object had been\r
+ * read. TODO: probably bones can have identical names in different armatures\r
+ */\r
+ protected Map<String, Integer> bonesMap = new HashMap<String, Integer>();\r
+ /** A map of bones and their old memory addresses. */\r
+ protected Map<Bone, Long> bonesOMAs = new HashMap<Bone, Long>();\r
+ /** This list contains bones hierarchy and their matrices. It is later converted into jme bones. */\r
+ protected List<BoneTransformationData> boneDataRoots = new ArrayList<BoneTransformationData>();\r
\r
- /**\r
- * This method returns the old memory address of a bone. If the bone does not exist in the blend file - zero is\r
- * returned.\r
- * @param bone\r
- * the bone whose old memory address we seek\r
- * @return the old memory address of the given bone\r
- */\r
- public Long getBoneOMA(Bone bone) {\r
- Long result = bonesOMAs.get(bone);\r
- if(result == null) {\r
- result = Long.valueOf(0);\r
- }\r
- return result;\r
- }\r
+ /**\r
+ * This constructor parses the given blender version and stores the result. Some functionalities may differ in\r
+ * different blender versions.\r
+ * @param blenderVersion\r
+ * the version read from the blend file\r
+ */\r
+ public ArmatureHelper(String blenderVersion) {\r
+ super(blenderVersion);\r
+ }\r
\r
- /**\r
- * This method reads the bones and returns an empty skeleton. Bones should be assigned later.\r
- * @param structure\r
- * armature structure\r
- * @param dataRepository\r
- * the data repository\r
- * @return an empty skeleton, bones are stored within the helper object\r
- * @throws BlenderFileException\r
- * this exception is thrown when the blender file is somehow corrupted\r
- */\r
- public Skeleton toArmature(Structure structure, DataRepository dataRepository) throws BlenderFileException {\r
- LOGGER.log(Level.INFO, "Converting structure to Armature!");\r
- Structure bonebase = (Structure)structure.getFieldValue("bonebase");\r
- List<Structure> bonesStructures = bonebase.evaluateListBase(dataRepository);\r
- for(Structure boneStructure : bonesStructures) {\r
- BoneTransformationData rootBoneTransformationData = this.readBoneAndItsChildren(boneStructure, null, dataRepository);\r
- boneDataRoots.add(rootBoneTransformationData);\r
- }\r
- return new Skeleton();//bones are assigned later\r
- }\r
+ /**\r
+ * This method returns the old memory address of a bone. If the bone does not exist in the blend file - zero is\r
+ * returned.\r
+ * @param bone\r
+ * the bone whose old memory address we seek\r
+ * @return the old memory address of the given bone\r
+ */\r
+ public Long getBoneOMA(Bone bone) {\r
+ Long result = bonesOMAs.get(bone);\r
+ if (result == null) {\r
+ result = Long.valueOf(0);\r
+ }\r
+ return result;\r
+ }\r
\r
- /**\r
- * 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\r
- * bone index in the armature.\r
- * @param poseStructure\r
- * a bPose structure of the object\r
- * @return bone group-to-index map\r
- * @throws BlenderFileException\r
- * this exception is thrown when the blender file is somehow corrupted\r
- */\r
- public Map<Integer, Integer> getGroupToBoneIndexMap(Structure defBaseStructure, DataRepository dataRepository) throws BlenderFileException {\r
- Map<Integer, Integer> result = null;\r
- if(bonesMap != null && bonesMap.size() != 0) {\r
- result = new HashMap<Integer, Integer>();\r
- List<Structure> deformGroups = defBaseStructure.evaluateListBase(dataRepository);//bDeformGroup\r
- int groupIndex = 0;//TODO: consider many armatures attached to one object in the future !!!\r
- for(Structure deformGroup : deformGroups) {\r
- String deformGroupName = deformGroup.getFieldValue("name").toString();\r
- Integer boneIndex = bonesMap.get(deformGroupName);\r
- if(boneIndex != null) {\r
- result.put(Integer.valueOf(groupIndex), boneIndex);\r
- }\r
- ++groupIndex;\r
- }\r
- }\r
- return result;\r
- }\r
+ /**\r
+ * This method reads the bones and returns an empty skeleton. Bones should be assigned later.\r
+ * @param structure\r
+ * armature structure\r
+ * @param dataRepository\r
+ * the data repository\r
+ * @return an empty skeleton, bones are stored within the helper object\r
+ * @throws BlenderFileException\r
+ * this exception is thrown when the blender file is somehow corrupted\r
+ */\r
+ public Skeleton toArmature(Structure structure, DataRepository dataRepository) throws BlenderFileException {\r
+ LOGGER.log(Level.INFO, "Converting structure to Armature!");\r
+ Structure bonebase = (Structure) structure.getFieldValue("bonebase");\r
+ List<Structure> bonesStructures = bonebase.evaluateListBase(dataRepository);\r
+ for (Structure boneStructure : bonesStructures) {\r
+ BoneTransformationData rootBoneTransformationData = this.readBoneAndItsChildren(boneStructure, null, dataRepository);\r
+ boneDataRoots.add(rootBoneTransformationData);\r
+ }\r
+ return new Skeleton();//bones are assigned later\r
+ }\r
\r
- /**\r
- * This method reads the tracks of the armature object.\r
- * @param actionStructure\r
- * @param dataRepository\r
- * the data repository\r
- * @param objectName\r
- * the name of animation owner\r
- * @param animationName\r
- * the name of the animation\r
- * @return a list of tracks for the armature\r
- * @throws BlenderFileException\r
- * this exception is thrown when the blender file is somehow corrupted\r
- */\r
- public BoneTrack[] getTracks(Structure actionStructure, DataRepository dataRepository, String objectName, String animationName) throws BlenderFileException {\r
- LOGGER.log(Level.INFO, "Getting tracks!");\r
- IpoHelper ipoHelper = dataRepository.getHelper(IpoHelper.class);\r
- int fps = dataRepository.getBlenderKey().getFps();\r
- int[] animationFrames = dataRepository.getBlenderKey().getAnimationFrames(objectName, animationName);\r
- Structure chanbase = (Structure)actionStructure.getFieldValue("chanbase");\r
- List<Structure> actionChannels = chanbase.evaluateListBase(dataRepository);//bActionChannel\r
- if(actionChannels != null && actionChannels.size() > 0 && (bonesMap == null || bonesMap.size() == 0)) {\r
- throw new IllegalStateException("No bones found! Cannot proceed to calculating tracks!");\r
- }\r
- List<BoneTrack> tracks = new ArrayList<BoneTrack>();\r
- for(Structure bActionChannel : actionChannels) {\r
- String name = bActionChannel.getFieldValue("name").toString();\r
- Integer boneIndex = bonesMap.get(name);\r
- if(boneIndex != null) {\r
- Pointer p = (Pointer)bActionChannel.getFieldValue("ipo");\r
- if(!p.isNull()) {\r
- Structure ipoStructure = p.fetchData(dataRepository.getInputStream()).get(0);\r
- Ipo ipo = ipoHelper.createIpo(ipoStructure, dataRepository);\r
- tracks.add(ipo.calculateTrack(boneIndex.intValue(), animationFrames[0], animationFrames[1], fps));\r
- }\r
- }\r
- }\r
- return tracks.toArray(new BoneTrack[tracks.size()]);\r
- }\r
+ /**\r
+ * 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\r
+ * bone index in the armature.\r
+ * @param poseStructure\r
+ * a bPose structure of the object\r
+ * @return bone group-to-index map\r
+ * @throws BlenderFileException\r
+ * this exception is thrown when the blender file is somehow corrupted\r
+ */\r
+ public Map<Integer, Integer> getGroupToBoneIndexMap(Structure defBaseStructure, DataRepository dataRepository) throws BlenderFileException {\r
+ Map<Integer, Integer> result = null;\r
+ if (bonesMap != null && bonesMap.size() != 0) {\r
+ result = new HashMap<Integer, Integer>();\r
+ List<Structure> deformGroups = defBaseStructure.evaluateListBase(dataRepository);//bDeformGroup\r
+ int groupIndex = 0;//TODO: consider many armatures attached to one object in the future !!!\r
+ for (Structure deformGroup : deformGroups) {\r
+ String deformGroupName = deformGroup.getFieldValue("name").toString();\r
+ Integer boneIndex = bonesMap.get(deformGroupName);\r
+ if (boneIndex != null) {\r
+ result.put(Integer.valueOf(groupIndex), boneIndex);\r
+ }\r
+ ++groupIndex;\r
+ }\r
+ }\r
+ return result;\r
+ }\r
\r
- /**\r
- * This bone returns transformation matrix of the bone that is relative to\r
- * its armature object.\r
- * @param boneStructure the bone's structure\r
- * @return bone's transformation matrix in armature space\r
- */\r
- @SuppressWarnings("unchecked")\r
- protected Matrix4f getArmatureMatrix(Structure boneStructure) {\r
- DynamicArray<Number> boneMat = (DynamicArray<Number>)boneStructure.getFieldValue("arm_mat");\r
- Matrix4f m = new Matrix4f();\r
- for(int i = 0; i < 4; ++i) {\r
- for(int j = 0; j < 4; ++j) {\r
- m.set(i, j, boneMat.get(j, i).floatValue());\r
- }\r
- }\r
- return m;\r
- }\r
+ /**\r
+ * This method reads the tracks of the armature object.\r
+ * @param actionStructure\r
+ * @param dataRepository\r
+ * the data repository\r
+ * @param objectName\r
+ * the name of animation owner\r
+ * @param animationName\r
+ * the name of the animation\r
+ * @return a list of tracks for the armature\r
+ * @throws BlenderFileException\r
+ * this exception is thrown when the blender file is somehow corrupted\r
+ */\r
+ public BoneTrack[] getTracks(Structure actionStructure, DataRepository dataRepository, String objectName, String animationName) throws BlenderFileException {\r
+ LOGGER.log(Level.INFO, "Getting tracks!");\r
+ IpoHelper ipoHelper = dataRepository.getHelper(IpoHelper.class);\r
+ int fps = dataRepository.getBlenderKey().getFps();\r
+ int[] animationFrames = dataRepository.getBlenderKey().getAnimationFrames(objectName, animationName);\r
+ Structure chanbase = (Structure) actionStructure.getFieldValue("chanbase");\r
+ List<Structure> actionChannels = chanbase.evaluateListBase(dataRepository);//bActionChannel\r
+ if (actionChannels != null && actionChannels.size() > 0 && (bonesMap == null || bonesMap.size() == 0)) {\r
+ throw new IllegalStateException("No bones found! Cannot proceed to calculating tracks!");\r
+ }\r
+ List<BoneTrack> tracks = new ArrayList<BoneTrack>();\r
+ for (Structure bActionChannel : actionChannels) {\r
+ String name = bActionChannel.getFieldValue("name").toString();\r
+ Integer boneIndex = bonesMap.get(name);\r
+ if (boneIndex != null) {\r
+ Pointer p = (Pointer) bActionChannel.getFieldValue("ipo");\r
+ if (!p.isNull()) {\r
+ Structure ipoStructure = p.fetchData(dataRepository.getInputStream()).get(0);\r
+ Ipo ipo = ipoHelper.createIpo(ipoStructure, dataRepository);\r
+ tracks.add(ipo.calculateTrack(boneIndex.intValue(), animationFrames[0], animationFrames[1], fps));\r
+ }\r
+ }\r
+ }\r
+ return tracks.toArray(new BoneTrack[tracks.size()]);\r
+ }\r
\r
- /**\r
- * This method reads the bone with its children.\r
- * @param boneStructure\r
- * a structure containing the bone data\r
- * @param parent\r
- * the bone parent; if null then we read the root bone\r
- * @param dataRepository\r
- * the data repository\r
- * @return the bone transformation data; contains bone chierarchy and the bone's matrices\r
- * @throws BlenderFileException\r
- * this exception is thrown when the blender file is somehow corrupted\r
- */\r
- @SuppressWarnings("unchecked")\r
- protected BoneTransformationData readBoneAndItsChildren(Structure boneStructure, BoneTransformationData parent, DataRepository dataRepository) throws BlenderFileException {\r
- String name = boneStructure.getFieldValue("name").toString();\r
- Bone bone = new Bone(name);\r
- int bonesAmount = bonesOMAs.size();\r
- bonesOMAs.put(bone, boneStructure.getOldMemoryAddress());\r
- if(bonesAmount == bonesOMAs.size()) {\r
- throw new IllegalStateException("Two bones has the same hash value and thereforw a bone was overriden in the bones<->OMA map! Improve the hash algorithm!");\r
- }\r
- Matrix4f boneArmatureMatrix = this.getArmatureMatrix(boneStructure);\r
- DynamicArray<Float> sizeArray = (DynamicArray<Float>) boneStructure.getFieldValue("size");\r
- Vector3f size = new Vector3f(sizeArray.get(0), sizeArray.get(1), sizeArray.get(2));\r
- BoneTransformationData boneTransformationData = new BoneTransformationData(boneArmatureMatrix, size, bone, parent);\r
- dataRepository.addLoadedFeatures(boneStructure.getOldMemoryAddress(), name, boneStructure, bone);\r
+ /**\r
+ * This bone returns transformation matrix of the bone that is relative to\r
+ * its armature object.\r
+ * @param boneStructure the bone's structure\r
+ * @return bone's transformation matrix in armature space\r
+ */\r
+ @SuppressWarnings("unchecked")\r
+ protected Matrix4f getArmatureMatrix(Structure boneStructure) {\r
+ DynamicArray<Number> boneMat = (DynamicArray<Number>) boneStructure.getFieldValue("arm_mat");\r
+ Matrix4f m = new Matrix4f();\r
+ for (int i = 0; i < 4; ++i) {\r
+ for (int j = 0; j < 4; ++j) {\r
+ m.set(i, j, boneMat.get(j, i).floatValue());\r
+ }\r
+ }\r
+ return m;\r
+ }\r
\r
- Structure childbase = (Structure)boneStructure.getFieldValue("childbase");\r
- List<Structure> children = childbase.evaluateListBase(dataRepository);//Bone\r
- for(Structure boneChild : children) {\r
- this.readBoneAndItsChildren(boneChild, boneTransformationData, dataRepository);\r
- }\r
- return boneTransformationData;\r
- }\r
+ /**\r
+ * This method reads the bone with its children.\r
+ * @param boneStructure\r
+ * a structure containing the bone data\r
+ * @param parent\r
+ * the bone parent; if null then we read the root bone\r
+ * @param dataRepository\r
+ * the data repository\r
+ * @return the bone transformation data; contains bone chierarchy and the bone's matrices\r
+ * @throws BlenderFileException\r
+ * this exception is thrown when the blender file is somehow corrupted\r
+ */\r
+ @SuppressWarnings("unchecked")\r
+ protected BoneTransformationData readBoneAndItsChildren(Structure boneStructure, BoneTransformationData parent, DataRepository dataRepository) throws BlenderFileException {\r
+ String name = boneStructure.getFieldValue("name").toString();\r
+ Bone bone = new Bone(name);\r
+ int bonesAmount = bonesOMAs.size();\r
+ bonesOMAs.put(bone, boneStructure.getOldMemoryAddress());\r
+ if (bonesAmount == bonesOMAs.size()) {\r
+ throw new IllegalStateException("Two bones has the same hash value and thereforw a bone was overriden in the bones<->OMA map! Improve the hash algorithm!");\r
+ }\r
+ Matrix4f boneArmatureMatrix = this.getArmatureMatrix(boneStructure);\r
+ DynamicArray<Float> sizeArray = (DynamicArray<Float>) boneStructure.getFieldValue("size");\r
+ Vector3f size = new Vector3f(sizeArray.get(0), sizeArray.get(1), sizeArray.get(2));\r
+ BoneTransformationData boneTransformationData = new BoneTransformationData(boneArmatureMatrix, size, bone, parent);\r
+ dataRepository.addLoadedFeatures(boneStructure.getOldMemoryAddress(), name, boneStructure, bone);\r
\r
- /**\r
- * This method assigns transformations to the bone.\r
- * @param btd\r
- * the bone data containing the bone we assign transformation to\r
- * @param additionalRootBoneTransformation\r
- * additional bone transformation which indicates it's mesh parent and armature object transformations\r
- * @param boneList\r
- * a list of all read bones\r
- */\r
- protected void assignBonesMatrices(BoneTransformationData btd, Matrix4f additionalRootBoneTransformation, List<Bone> boneList) {\r
- LOGGER.info("[" + btd.bone.getName() + "] additionalRootBoneTransformation =\n" + additionalRootBoneTransformation);\r
- Matrix4f totalInverseParentMatrix = btd.parent != null ? btd.parent.totalInverseBoneParentMatrix : Matrix4f.IDENTITY;\r
- LOGGER.info("[" + btd.bone.getName() + "] totalInverseParentMatrix =\n" + totalInverseParentMatrix);\r
- Matrix4f restMatrix = additionalRootBoneTransformation.mult(btd.boneArmatureMatrix);\r
- LOGGER.info("[" + btd.bone.getName() + "] restMatrix =\n" + restMatrix);\r
- btd.totalInverseBoneParentMatrix = restMatrix.clone().invert();\r
- restMatrix = totalInverseParentMatrix.mult(restMatrix);\r
- LOGGER.info("[" + btd.bone.getName() + "] resultMatrix =\n" + restMatrix);\r
- btd.bone.setBindTransforms(restMatrix.toTranslationVector(), restMatrix.toRotationQuat(), btd.size);\r
- boneList.add(btd.bone);\r
- bonesMap.put(btd.bone.getName(), Integer.valueOf(boneList.size() - 1));\r
- if(btd.children != null && btd.children.size() > 0) {\r
- for(BoneTransformationData child : btd.children) {\r
- this.assignBonesMatrices(child, additionalRootBoneTransformation, boneList);\r
- btd.bone.addChild(child.bone);\r
- }\r
- }\r
- }\r
+ Structure childbase = (Structure) boneStructure.getFieldValue("childbase");\r
+ List<Structure> children = childbase.evaluateListBase(dataRepository);//Bone\r
+ for (Structure boneChild : children) {\r
+ this.readBoneAndItsChildren(boneChild, boneTransformationData, dataRepository);\r
+ }\r
+ return boneTransformationData;\r
+ }\r
\r
- /**\r
- * This method returns bone transformation data for the bone of a given name.\r
- * @param boneName\r
- * the name of the bone\r
- * @return bone's transformation data\r
- */\r
- public BoneTransformationData getBoneTransformationDataRoot(int index) {\r
- return boneDataRoots.get(index);\r
- }\r
+ /**\r
+ * This method assigns transformations to the bone.\r
+ * @param btd\r
+ * the bone data containing the bone we assign transformation to\r
+ * @param additionalRootBoneTransformation\r
+ * additional bone transformation which indicates it's mesh parent and armature object transformations\r
+ * @param boneList\r
+ * a list of all read bones\r
+ */\r
+ protected void assignBonesMatrices(BoneTransformationData btd, Matrix4f additionalRootBoneTransformation, List<Bone> boneList) {\r
+ LOGGER.info("[" + btd.bone.getName() + "] additionalRootBoneTransformation =\n" + additionalRootBoneTransformation);\r
+ Matrix4f totalInverseParentMatrix = btd.parent != null ? btd.parent.totalInverseBoneParentMatrix : Matrix4f.IDENTITY;\r
+ LOGGER.info("[" + btd.bone.getName() + "] totalInverseParentMatrix =\n" + totalInverseParentMatrix);\r
+ Matrix4f restMatrix = additionalRootBoneTransformation.mult(btd.boneArmatureMatrix);\r
+ LOGGER.info("[" + btd.bone.getName() + "] restMatrix =\n" + restMatrix);\r
+ btd.totalInverseBoneParentMatrix = restMatrix.clone().invert();\r
+ restMatrix = totalInverseParentMatrix.mult(restMatrix);\r
+ LOGGER.info("[" + btd.bone.getName() + "] resultMatrix =\n" + restMatrix);\r
+ btd.bone.setBindTransforms(restMatrix.toTranslationVector(), restMatrix.toRotationQuat(), btd.size);\r
+ boneList.add(btd.bone);\r
+ bonesMap.put(btd.bone.getName(), Integer.valueOf(boneList.size() - 1));\r
+ if (btd.children != null && btd.children.size() > 0) {\r
+ for (BoneTransformationData child : btd.children) {\r
+ this.assignBonesMatrices(child, additionalRootBoneTransformation, boneList);\r
+ btd.bone.addChild(child.bone);\r
+ }\r
+ }\r
+ }\r
\r
- /**\r
- * This method returns the amount of bones transformations roots.\r
- * @return the amount of bones transformations roots\r
- */\r
- public int getBoneTransformationDataRootsSize() {\r
- return boneDataRoots.size();\r
- }\r
+ /**\r
+ * This method returns bone transformation data for the bone of a given name.\r
+ * @param boneName\r
+ * the name of the bone\r
+ * @return bone's transformation data\r
+ */\r
+ public BoneTransformationData getBoneTransformationDataRoot(int index) {\r
+ return boneDataRoots.get(index);\r
+ }\r
\r
- /**\r
- * This class holds the data needed later for bone transformation calculation and to bind parent with children.\r
- * @author Marcin Roguski\r
- */\r
- private static class BoneTransformationData {\r
- /** Inverse matrix of bone's parent bone. */\r
- private Matrix4f totalInverseBoneParentMatrix;\r
- /** Bone's matrix in armature's space. */\r
- private Matrix4f boneArmatureMatrix;\r
- /** Bone's size (apparently it is held outside the transformation matrix. */\r
- private Vector3f size;\r
- /** The bone the data applies to. */\r
- private Bone bone;\r
- /** The parent of the above mentioned bone (not assigned yet). */\r
- private BoneTransformationData parent;\r
- /** The children of the current bone. */\r
- private List<BoneTransformationData> children;\r
+ /**\r
+ * This method returns the amount of bones transformations roots.\r
+ * @return the amount of bones transformations roots\r
+ */\r
+ public int getBoneTransformationDataRootsSize() {\r
+ return boneDataRoots.size();\r
+ }\r
\r
- /**\r
- * Private constructor creates the object and assigns the given data.\r
- * @param boneArmatureMatrix\r
- * the matrix of the current bone\r
- * @param size\r
- * the bone's size\r
- * @param bone\r
- * the current bone\r
- * @param parent\r
- * the parent structure of the bone\r
- */\r
- private BoneTransformationData(Matrix4f boneArmatureMatrix, Vector3f size, Bone bone, BoneTransformationData parent) {\r
- this.boneArmatureMatrix = boneArmatureMatrix;\r
- this.size = size;\r
- this.bone = bone;\r
- this.parent = parent;\r
- this.children = new ArrayList<ArmatureHelper.BoneTransformationData>();\r
- if(this.parent != null) {\r
- this.parent.children.add(this);\r
- }\r
- }\r
- }\r
+ /**\r
+ * This class holds the data needed later for bone transformation calculation and to bind parent with children.\r
+ * @author Marcin Roguski\r
+ */\r
+ private static class BoneTransformationData {\r
\r
- /**\r
- * This method creates the whole bones structure. Assignes transformations to bones and combines children with\r
- * parents.\r
- * @param armatureOMA\r
- * old memory address of bones' armature object\r
- * @param additionalRootBoneTransformation\r
- * additional bone transformation which indicates it's mesh parent and armature object transformations\r
- * @return\r
- */\r
- public Bone[] buildBonesStructure(Long armatureOMA, Matrix4f additionalRootBoneTransformation) {//TODO: uwzględnić wiele szkieletów\r
- List<Bone> bones = new ArrayList<Bone>(boneDataRoots.size() + 1);\r
- bones.add(new Bone(null));\r
- for(BoneTransformationData btd : boneDataRoots) {\r
- this.assignBonesMatrices(btd, additionalRootBoneTransformation, bones);\r
- }\r
- return bones.toArray(new Bone[bones.size()]);\r
- }\r
- \r
- /**\r
- * This method assigns an immovable bone to vertices that have no bone assigned. They have the bone index with the\r
- * value -1.\r
- * @param immovableBoneIndex\r
- * the ondex of immovable bone\r
- * @param meshes\r
- * a list of meshes whose vertices will be assigned to immovable bone\r
- */\r
- public void assignBoneToOrphanedVertices(byte immovableBoneIndex, Mesh[] meshes) {\r
- //bone indices are common for all the object's meshes (vertex indices specify which are to be used)\r
- VertexBuffer boneIndices = meshes[0].getBuffer(Type.BoneIndex);//common buffer to all the meshes\r
- ByteBuffer data = (ByteBuffer)boneIndices.getData();\r
- for(int i = 0; i < boneIndices.getNumElements(); ++i) {\r
- if(data.get(i) == -1) {\r
- data.put(i, immovableBoneIndex);\r
- }\r
- }\r
- }\r
+ /** Inverse matrix of bone's parent bone. */\r
+ private Matrix4f totalInverseBoneParentMatrix;\r
+ /** Bone's matrix in armature's space. */\r
+ private Matrix4f boneArmatureMatrix;\r
+ /** Bone's size (apparently it is held outside the transformation matrix. */\r
+ private Vector3f size;\r
+ /** The bone the data applies to. */\r
+ private Bone bone;\r
+ /** The parent of the above mentioned bone (not assigned yet). */\r
+ private BoneTransformationData parent;\r
+ /** The children of the current bone. */\r
+ private List<BoneTransformationData> children;\r
\r
- @Override\r
- public void clearState() {\r
- bonesMap.clear();\r
- boneDataRoots.clear();\r
- }\r
+ /**\r
+ * Private constructor creates the object and assigns the given data.\r
+ * @param boneArmatureMatrix\r
+ * the matrix of the current bone\r
+ * @param size\r
+ * the bone's size\r
+ * @param bone\r
+ * the current bone\r
+ * @param parent\r
+ * the parent structure of the bone\r
+ */\r
+ private BoneTransformationData(Matrix4f boneArmatureMatrix, Vector3f size, Bone bone, BoneTransformationData parent) {\r
+ this.boneArmatureMatrix = boneArmatureMatrix;\r
+ this.size = size;\r
+ this.bone = bone;\r
+ this.parent = parent;\r
+ this.children = new ArrayList<ArmatureHelper.BoneTransformationData>();\r
+ if (this.parent != null) {\r
+ this.parent.children.add(this);\r
+ }\r
+ }\r
+ }\r
+\r
+ /**\r
+ * This method creates the whole bones structure. Assignes transformations to bones and combines children with\r
+ * parents.\r
+ * @param armatureOMA\r
+ * old memory address of bones' armature object\r
+ * @param additionalRootBoneTransformation\r
+ * additional bone transformation which indicates it's mesh parent and armature object transformations\r
+ * @return\r
+ */\r
+ public Bone[] buildBonesStructure(Long armatureOMA, Matrix4f additionalRootBoneTransformation) {//TODO: uwzględnić wiele szkieletów\r
+ List<Bone> bones = new ArrayList<Bone>(boneDataRoots.size() + 1);\r
+ bones.add(new Bone(null));\r
+ for (BoneTransformationData btd : boneDataRoots) {\r
+ this.assignBonesMatrices(btd, additionalRootBoneTransformation, bones);\r
+ }\r
+ return bones.toArray(new Bone[bones.size()]);\r
+ }\r
+\r
+ /**\r
+ * This method assigns an immovable bone to vertices that have no bone assigned. They have the bone index with the\r
+ * value -1.\r
+ * @param immovableBoneIndex\r
+ * the ondex of immovable bone\r
+ * @param meshes\r
+ * a list of meshes whose vertices will be assigned to immovable bone\r
+ */\r
+ public void assignBoneToOrphanedVertices(byte immovableBoneIndex, Mesh[] meshes) {\r
+ //bone indices are common for all the object's meshes (vertex indices specify which are to be used)\r
+ VertexBuffer boneIndices = meshes[0].getBuffer(Type.BoneIndex);//common buffer to all the meshes\r
+ ByteBuffer data = (ByteBuffer) boneIndices.getData();\r
+ for (int i = 0; i < boneIndices.getNumElements(); ++i) {\r
+ if (data.get(i) == -1) {\r
+ data.put(i, immovableBoneIndex);\r
+ }\r
+ }\r
+ }\r
+\r
+ @Override\r
+ public void clearState() {\r
+ bonesMap.clear();\r
+ boneDataRoots.clear();\r
+ }\r
}\r
* @author Marcin Roguski\r
*/\r
public class CameraHelper extends AbstractBlenderHelper {\r
- private static final Logger LOGGER = Logger.getLogger(CameraHelper.class.getName());\r
- \r
- protected static final int DEFAULT_CAM_WIDTH = 100;\r
- protected static final int DEFAULT_CAM_HEIGHT = 100;\r
- /**\r
- * This constructor parses the given blender version and stores the result. Some functionalities may differ in\r
- * different blender versions.\r
- * @param blenderVersion\r
- * the version read from the blend file\r
- */\r
- public CameraHelper(String blenderVersion) {\r
- super(blenderVersion);\r
- }\r
\r
- /**\r
- * This method reads the camera object.\r
- * @param structure the structure containing the camera data\r
- * @return the camera object\r
- * @throws BlenderFileException\r
- */\r
- public Camera toCamera(Structure structure) throws BlenderFileException {\r
- Camera result = new Camera(DEFAULT_CAM_WIDTH, DEFAULT_CAM_HEIGHT);\r
- int type = ((Number)structure.getFieldValue("type")).intValue();\r
- if(type != 0 && type != 1) {\r
- LOGGER.log(Level.WARNING, "Unknown camera type: " + type + ". Perspective camera is being used!");\r
- type = 0;\r
- }\r
- //type==0 - perspective; type==1 - orthographic; perspective is used as default\r
- result.setParallelProjection(type == 1);\r
- float angle = ((Number)structure.getFieldValue("angle")).floatValue();\r
- float aspect = 0;\r
- float clipsta = ((Number)structure.getFieldValue("clipsta")).floatValue();\r
- float clipend = ((Number)structure.getFieldValue("clipend")).floatValue();\r
- if(type == 0) {\r
- aspect = ((Number)structure.getFieldValue("lens")).floatValue();\r
- } else {\r
- aspect = ((Number)structure.getFieldValue("ortho_scale")).floatValue();\r
- }\r
- result.setFrustumPerspective(angle, aspect, clipsta, clipend);\r
- return result;\r
- } \r
+ private static final Logger LOGGER = Logger.getLogger(CameraHelper.class.getName());\r
+ protected static final int DEFAULT_CAM_WIDTH = 100;\r
+ protected static final int DEFAULT_CAM_HEIGHT = 100;\r
+\r
+ /**\r
+ * This constructor parses the given blender version and stores the result. Some functionalities may differ in\r
+ * different blender versions.\r
+ * @param blenderVersion\r
+ * the version read from the blend file\r
+ */\r
+ public CameraHelper(String blenderVersion) {\r
+ super(blenderVersion);\r
+ }\r
+\r
+ /**\r
+ * This method reads the camera object.\r
+ * @param structure the structure containing the camera data\r
+ * @return the camera object\r
+ * @throws BlenderFileException\r
+ */\r
+ public Camera toCamera(Structure structure) throws BlenderFileException {\r
+ Camera result = new Camera(DEFAULT_CAM_WIDTH, DEFAULT_CAM_HEIGHT);\r
+ int type = ((Number) structure.getFieldValue("type")).intValue();\r
+ if (type != 0 && type != 1) {\r
+ LOGGER.log(Level.WARNING, "Unknown camera type: {0}. Perspective camera is being used!", type);\r
+ type = 0;\r
+ }\r
+ //type==0 - perspective; type==1 - orthographic; perspective is used as default\r
+ result.setParallelProjection(type == 1);\r
+ float angle = ((Number) structure.getFieldValue("angle")).floatValue();\r
+ float aspect = 0;\r
+ float clipsta = ((Number) structure.getFieldValue("clipsta")).floatValue();\r
+ float clipend = ((Number) structure.getFieldValue("clipend")).floatValue();\r
+ if (type == 0) {\r
+ aspect = ((Number) structure.getFieldValue("lens")).floatValue();\r
+ } else {\r
+ aspect = ((Number) structure.getFieldValue("ortho_scale")).floatValue();\r
+ }\r
+ result.setFrustumPerspective(angle, aspect, clipsta, clipend);\r
+ return result;\r
+ }\r
}\r
import com.jme3.scene.plugins.blender.utils.AbstractBlenderHelper;\r
import com.jme3.scene.plugins.blender.utils.DataRepository;\r
import com.jme3.scene.plugins.blender.utils.Pointer;\r
+import java.util.logging.Level;\r
\r
/**\r
* This class should be used for constraint calculations.\r
* @author Marcin Roguski\r
*/\r
public class ConstraintHelper extends AbstractBlenderHelper {\r
- /**\r
- * A table containing implementations of influence functions for constraints. It should contain functions for\r
- * blender at least 249 and higher.\r
- */\r
- protected static AbstractInfluenceFunction[] influenceFunctions;\r
-\r
- /**\r
- * Constraints stored for object with the given old memory address.\r
- */\r
- protected Map<Long, Constraint[]> constraints = new HashMap<Long, Constraint[]>();\r
-\r
- /**\r
- * Helper constructor. It's main task is to generate the affection functions. These functions are common to all\r
- * ConstraintHelper instances. Unfortunately this constructor might grow large. If it becomes too large - I shall\r
- * consider refactoring. The constructor parses the given blender version and stores the result. Some\r
- * functionalities may differ in different blender versions.\r
- * @param blenderVersion\r
- * the version read from the blend file\r
- */\r
- public ConstraintHelper(String blenderVersion, DataRepository dataRepository) {\r
- super(blenderVersion);\r
- if(influenceFunctions == null) {\r
- //TODO: synchronization\r
- influenceFunctions = new AbstractInfluenceFunction[ConstraintType.getLastDefinedTypeValue() + 1];\r
- //ACTION constraint (TODO: to implement)\r
- influenceFunctions[ConstraintType.CONSTRAINT_TYPE_ACTION.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_ACTION, dataRepository) {};\r
-\r
- //CHILDOF constraint (TODO: to implement)\r
- influenceFunctions[ConstraintType.CONSTRAINT_TYPE_CHILDOF.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_CHILDOF, dataRepository) {};\r
-\r
- //CLAMPTO constraint\r
- influenceFunctions[ConstraintType.CONSTRAINT_TYPE_CLAMPTO.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_CLAMPTO, dataRepository) {\r
- @Override\r
- public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation, Constraint constraint) {\r
- this.validateConstraintType(constraint.getData());\r
- LOGGER.info(constraint.getName() + " not active! Curves not yet implemented!");//TODO: implement when curves are implemented\r
- }\r
- };\r
-\r
- //DISTLIMIT constraint\r
- influenceFunctions[ConstraintType.CONSTRAINT_TYPE_DISTLIMIT.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_DISTLIMIT, dataRepository) {\r
- @Override\r
- public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation, Constraint constraint) {\r
- Structure constraintStructure = constraint.getData();\r
- this.validateConstraintType(constraintStructure);\r
- Vector3f targetLocation = this.getTargetLocation(constraint);\r
- BoneTrack boneTrack = this.getBoneTrack(skeleton, boneAnimation, constraint);\r
- if(boneTrack != null) {\r
- //TODO: target vertex group !!!\r
- float dist = ((Number)constraintStructure.getFieldValue("dist")).floatValue();\r
- int mode = ((Number)constraintStructure.getFieldValue("mode")).intValue();\r
-\r
- int maxFrames = boneTrack.getTimes().length;\r
- Vector3f[] translations = boneTrack.getTranslations();\r
- for(int frame = 0; frame < maxFrames; ++frame) {\r
- Vector3f v = translations[frame].subtract(targetLocation);\r
- float currentDistance = v.length();\r
- float influence = constraint.getIpo().calculateValue(frame);\r
- float modifier = 0.0f;\r
- switch(mode) {\r
- case LIMITDIST_INSIDE:\r
- if(currentDistance >= dist) {\r
- modifier = (dist - currentDistance) / currentDistance;\r
- }\r
- break;\r
- case LIMITDIST_ONSURFACE:\r
- modifier = (dist - currentDistance) / currentDistance;\r
- break;\r
- case LIMITDIST_OUTSIDE:\r
- if(currentDistance <= dist) {\r
- modifier = (dist - currentDistance) / currentDistance;\r
- }\r
- break;\r
- default:\r
- throw new IllegalStateException("Unknown distance limit constraint mode: " + mode);\r
- }\r
- translations[frame].addLocal(v.multLocal(modifier * influence));\r
- }\r
- boneTrack.setKeyframes(boneTrack.getTimes(), translations, boneTrack.getRotations(), boneTrack.getScales());\r
- }\r
- }\r
- };\r
-\r
- //FOLLOWPATH constraint\r
- influenceFunctions[ConstraintType.CONSTRAINT_TYPE_FOLLOWPATH.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_FOLLOWPATH, dataRepository) {\r
- @Override\r
- public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation, Constraint constraint) {\r
- this.validateConstraintType(constraint.getData());\r
- LOGGER.info(constraint.getName() + " not active! Curves not yet implemented!");//TODO: implement when curves are implemented\r
- }\r
- };\r
-\r
- //KINEMATIC constraint (TODO: to implement)\r
- influenceFunctions[ConstraintType.CONSTRAINT_TYPE_KINEMATIC.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_KINEMATIC, dataRepository) {\r
- @Override\r
- public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation, Constraint constraint) {\r
- Structure constraintStructure = constraint.getData();\r
- this.validateConstraintType(constraintStructure);\r
- /*Long boneOMA = constraint.getBoneOMA();\r
- //IK solver is only attached to bones\r
- Bone ownerBone = (Bone)dataRepository.getLoadedFeature(boneOMA, LoadedFeatureDataType.LOADED_FEATURE);\r
-\r
- //get the target point\r
- Object targetObject = this.getTarget(constraint, LoadedFeatureDataType.LOADED_FEATURE);\r
- Vector3f pt = null;//Point Target\r
- if(targetObject instanceof Bone) {\r
- pt = ((Bone)targetObject).getModelSpacePosition();\r
- } else if(targetObject instanceof Node) {\r
- pt = ((Node)targetObject).getWorldTranslation();\r
- } else if(targetObject instanceof Skeleton) {\r
- Structure armatureNodeStructure = (Structure)this.getTarget(constraint, LoadedFeatureDataType.LOADED_STRUCTURE);\r
- ObjectHelper objectHelper = dataRepository.getHelper(ObjectHelper.class);\r
- Transform transform = objectHelper.getTransformation(armatureNodeStructure);\r
- pt = transform.getTranslation();\r
- } else {\r
- throw new IllegalStateException("Unknown target object type! Should be Node, Bone or Skeleton and there is: " + targetObject.getClass().getName());\r
- }\r
- //preparing data\r
- int maxIterations = ((Number)constraintStructure.getFieldValue("iterations")).intValue();\r
- CalculationBone[] bones = this.getBonesToCalculate(ownerBone, skeleton, boneAnimation);\r
- for(int i=0;i<bones.length;++i) {\r
- System.out.println(Arrays.toString(bones[i].track.getTranslations()));\r
- System.out.println(Arrays.toString(bones[i].track.getRotations()));\r
- System.out.println("===============================");\r
- }\r
- Quaternion rotation = new Quaternion();\r
- int maxFrames = bones[0].track.getTimes().length;//all tracks should have the same amount of frames\r
-\r
- for(int frame = 0; frame < maxFrames; ++frame) {\r
- float error = IK_SOLVER_ERROR;\r
- int iteration = 0;\r
- while(error >= IK_SOLVER_ERROR && iteration <= maxIterations) {\r
- //rotating the bones\r
- for(int i = 0; i < bones.length - 1; ++i) {\r
- Vector3f pe = bones[i].getEndPoint();\r
- Vector3f pc = bones[i + 1].getWorldTranslation().clone();\r
-\r
- Vector3f peSUBpc = pe.subtract(pc).normalizeLocal();\r
- Vector3f ptSUBpc = pt.subtract(pc).normalizeLocal();\r
-\r
- float theta = FastMath.acos(peSUBpc.dot(ptSUBpc));\r
- Vector3f direction = peSUBpc.cross(ptSUBpc).normalizeLocal();\r
- bones[i].rotate(rotation.fromAngleAxis(theta, direction), frame);\r
- }\r
- error = pt.subtract(bones[0].getEndPoint()).length();\r
- ++iteration;\r
- }\r
- System.out.println("error = " + error + " iterations = " + iteration);\r
- }\r
- \r
- for(CalculationBone bone : bones) {\r
- bone.applyCalculatedTracks();\r
- }\r
- \r
- System.out.println("&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&");\r
- for(int i=0;i<bones.length;++i) {\r
- System.out.println(Arrays.toString(bones[i].track.getTranslations()));\r
- System.out.println(Arrays.toString(bones[i].track.getRotations()));\r
- System.out.println("===============================");\r
- }*/\r
- }\r
-\r
- /**\r
- * This method returns bones used for rotation calculations.\r
- * @param bone\r
- * the bone to which the constraint is applied\r
- * @param skeleton\r
- * the skeleton owning the bone and its ancestors\r
- * @param boneAnimation\r
- * the bone animation data that stores the traces for the skeleton's bones\r
- * @return a list of bones to imitate the bone's movement during IK solving\r
- */\r
- private CalculationBone[] getBonesToCalculate(Bone bone, Skeleton skeleton, BoneAnimation boneAnimation) {\r
- List<CalculationBone> bonesList = new ArrayList<CalculationBone>();\r
- Bone currentBone = bone;\r
- do {\r
- int boneIndex = skeleton.getBoneIndex(currentBone);\r
- for(int i = 0; i < boneAnimation.getTracks().length; ++i) {\r
- if(boneAnimation.getTracks()[i].getTargetBoneIndex() == boneIndex) {\r
- bonesList.add(new CalculationBone(currentBone, boneAnimation.getTracks()[i]));\r
- break;\r
- }\r
- }\r
- currentBone = currentBone.getParent();\r
- } while(currentBone != null);\r
- //attaching children\r
- CalculationBone[] result = bonesList.toArray(new CalculationBone[bonesList.size()]);\r
- for(int i = result.length - 1; i > 0; --i) {\r
- result[i].attachChild(result[i - 1]);\r
- }\r
- return result;\r
- }\r
- };\r
-\r
- //LOCKTRACK constraint (TODO: to implement)\r
- influenceFunctions[ConstraintType.CONSTRAINT_TYPE_LOCKTRACK.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_LOCKTRACK, dataRepository) {};\r
-\r
- //LOCLIKE constraint\r
- influenceFunctions[ConstraintType.CONSTRAINT_TYPE_LOCLIKE.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_LOCLIKE, dataRepository) {\r
- @Override\r
- public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation, Constraint constraint) {\r
- Structure constraintData = constraint.getData();\r
- this.validateConstraintType(constraintData);\r
- BoneTrack boneTrack = this.getBoneTrack(skeleton, boneAnimation, constraint);\r
- if(boneTrack != null) {\r
- Vector3f targetLocation = this.getTargetLocation(constraint);\r
- int flag = ((Number)constraintData.getFieldValue("flag")).intValue();\r
- Vector3f[] translations = boneTrack.getTranslations();\r
- int maxFrames = translations.length;\r
- for(int frame = 0; frame < maxFrames; ++frame) {\r
- Vector3f offset = Vector3f.ZERO;\r
- if((flag & LOCLIKE_OFFSET) != 0) {//we add the original location to the copied location\r
- offset = translations[frame].clone();\r
- }\r
-\r
- if((flag & LOCLIKE_X) != 0) {\r
- translations[frame].x = targetLocation.x;\r
- if((flag & LOCLIKE_X_INVERT) != 0) {\r
- translations[frame].x = -translations[frame].x;\r
- }\r
- } else if((flag & LOCLIKE_Y) != 0) {\r
- translations[frame].y = targetLocation.y;\r
- if((flag & LOCLIKE_Y_INVERT) != 0) {\r
- translations[frame].y = -translations[frame].y;\r
- }\r
- } else if((flag & LOCLIKE_Z) != 0) {\r
- translations[frame].z = targetLocation.z;\r
- if((flag & LOCLIKE_Z_INVERT) != 0) {\r
- translations[frame].z = -translations[frame].z;\r
- }\r
- }\r
- translations[frame].addLocal(offset);//TODO: ipo influence\r
- }\r
- boneTrack.setKeyframes(boneTrack.getTimes(), translations, boneTrack.getRotations(), boneTrack.getScales());\r
- }\r
- }\r
- };\r
-\r
- //LOCLIMIT constraint\r
- influenceFunctions[ConstraintType.CONSTRAINT_TYPE_LOCLIMIT.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_LOCLIMIT, dataRepository) {\r
- @Override\r
- public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation, Constraint constraint) {\r
- Structure constraintStructure = constraint.getData();\r
- this.validateConstraintType(constraintStructure);\r
- BoneTrack boneTrack = this.getBoneTrack(skeleton, boneAnimation, constraint);\r
- if(boneTrack != null) {\r
- int flag = ((Number)constraintStructure.getFieldValue("flag")).intValue();\r
- Vector3f[] translations = boneTrack.getTranslations();\r
- int maxFrames = translations.length;\r
- for(int frame = 0; frame < maxFrames; ++frame) {\r
- float influence = constraint.getIpo().calculateValue(frame);\r
- if((flag & LIMIT_XMIN) != 0) {\r
- float xmin = ((Number)constraintStructure.getFieldValue("xmin")).floatValue();\r
- if(translations[frame].x < xmin) {\r
- translations[frame].x -= (translations[frame].x - xmin) * influence;\r
- }\r
- }\r
- if((flag & LIMIT_XMAX) != 0) {\r
- float xmax = ((Number)constraintStructure.getFieldValue("xmax")).floatValue();\r
- if(translations[frame].x > xmax) {\r
- translations[frame].x -= (translations[frame].x - xmax) * influence;\r
- }\r
- }\r
- if((flag & LIMIT_YMIN) != 0) {\r
- float ymin = ((Number)constraintStructure.getFieldValue("ymin")).floatValue();\r
- if(translations[frame].y < ymin) {\r
- translations[frame].y -= (translations[frame].y - ymin) * influence;\r
- }\r
- }\r
- if((flag & LIMIT_YMAX) != 0) {\r
- float ymax = ((Number)constraintStructure.getFieldValue("ymax")).floatValue();\r
- if(translations[frame].y > ymax) {\r
- translations[frame].y -= (translations[frame].y - ymax) * influence;\r
- }\r
- }\r
- if((flag & LIMIT_ZMIN) != 0) {\r
- float zmin = ((Number)constraintStructure.getFieldValue("zmin")).floatValue();\r
- if(translations[frame].z < zmin) {\r
- translations[frame].z -= (translations[frame].z - zmin) * influence;\r
- }\r
- }\r
- if((flag & LIMIT_ZMAX) != 0) {\r
- float zmax = ((Number)constraintStructure.getFieldValue("zmax")).floatValue();\r
- if(translations[frame].z > zmax) {\r
- translations[frame].z -= (translations[frame].z - zmax) * influence;\r
- }\r
- }//TODO: consider constraint space !!!\r
- }\r
- boneTrack.setKeyframes(boneTrack.getTimes(), translations, boneTrack.getRotations(), boneTrack.getScales());\r
- }\r
- }\r
- };\r
-\r
- //MINMAX constraint (TODO: to implement)\r
- influenceFunctions[ConstraintType.CONSTRAINT_TYPE_MINMAX.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_MINMAX, dataRepository) {};\r
-\r
- //NULL constraint - does nothing\r
- influenceFunctions[ConstraintType.CONSTRAINT_TYPE_NULL.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_NULL, dataRepository) {};\r
-\r
- //PYTHON constraint (TODO: to implement)\r
- influenceFunctions[ConstraintType.CONSTRAINT_TYPE_PYTHON.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_PYTHON, dataRepository) {};\r
-\r
- //RIGIDBODYJOINT constraint (TODO: to implement)\r
- influenceFunctions[ConstraintType.CONSTRAINT_TYPE_RIGIDBODYJOINT.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_RIGIDBODYJOINT, dataRepository) {};\r
-\r
- //ROTLIKE constraint\r
- influenceFunctions[ConstraintType.CONSTRAINT_TYPE_ROTLIKE.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_ROTLIKE, dataRepository) {\r
- @Override\r
- public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation, Constraint constraint) {\r
- Structure constraintData = constraint.getData();\r
- this.validateConstraintType(constraintData);\r
- BoneTrack boneTrack = this.getBoneTrack(skeleton, boneAnimation, constraint);\r
- if(boneTrack != null) {\r
- Quaternion targetRotation = this.getTargetRotation(constraint);\r
- int flag = ((Number)constraintData.getFieldValue("flag")).intValue();\r
- float[] targetAngles = targetRotation.toAngles(null);\r
- Quaternion[] rotations = boneTrack.getRotations();\r
- int maxFrames = rotations.length;\r
- for(int frame = 0; frame < maxFrames; ++frame) {\r
- float[] angles = rotations[frame].toAngles(null);\r
-\r
- Quaternion offset = Quaternion.IDENTITY;\r
- if((flag & ROTLIKE_OFFSET) != 0) {//we add the original rotation to the copied rotation\r
- offset = rotations[frame].clone();\r
- }\r
-\r
- if((flag & ROTLIKE_X) != 0) {\r
- angles[0] = targetAngles[0];\r
- if((flag & ROTLIKE_X_INVERT) != 0) {\r
- angles[0] = -angles[0];\r
- }\r
- } else if((flag & ROTLIKE_Y) != 0) {\r
- angles[1] = targetAngles[1];\r
- if((flag & ROTLIKE_Y_INVERT) != 0) {\r
- angles[1] = -angles[1];\r
- }\r
- } else if((flag & ROTLIKE_Z) != 0) {\r
- angles[2] = targetAngles[2];\r
- if((flag & ROTLIKE_Z_INVERT) != 0) {\r
- angles[2] = -angles[2];\r
- }\r
- }\r
- rotations[frame].fromAngles(angles).multLocal(offset);//TODO: ipo influence\r
- }\r
- boneTrack.setKeyframes(boneTrack.getTimes(), boneTrack.getTranslations(), rotations, boneTrack.getScales());\r
- }\r
- }\r
- };\r
-\r
- //ROTLIMIT constraint\r
- influenceFunctions[ConstraintType.CONSTRAINT_TYPE_ROTLIMIT.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_ROTLIMIT, dataRepository) {\r
- @Override\r
- public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation, Constraint constraint) {\r
- Structure constraintStructure = constraint.getData();\r
- this.validateConstraintType(constraintStructure);\r
- BoneTrack boneTrack = this.getBoneTrack(skeleton, boneAnimation, constraint);\r
- if(boneTrack != null) {\r
- int flag = ((Number)constraintStructure.getFieldValue("flag")).intValue();\r
- Quaternion[] rotations = boneTrack.getRotations();\r
- int maxFrames = rotations.length;\r
- for(int frame = 0; frame < maxFrames; ++frame) {\r
- float[] angles = rotations[frame].toAngles(null);\r
- float influence = constraint.getIpo().calculateValue(frame);\r
- if((flag & LIMIT_XROT) != 0) {\r
- float xmin = ((Number)constraintStructure.getFieldValue("xmin")).floatValue() * FastMath.DEG_TO_RAD;\r
- float xmax = ((Number)constraintStructure.getFieldValue("xmax")).floatValue() * FastMath.DEG_TO_RAD;\r
- float difference = 0.0f;\r
- if(angles[0] < xmin) {\r
- difference = (angles[0] - xmin) * influence;\r
- } else if(angles[0] > xmax) {\r
- difference = (angles[0] - xmax) * influence;\r
- }\r
- angles[0] -= difference;\r
- }\r
- if((flag & LIMIT_YROT) != 0) {\r
- float ymin = ((Number)constraintStructure.getFieldValue("ymin")).floatValue() * FastMath.DEG_TO_RAD;\r
- float ymax = ((Number)constraintStructure.getFieldValue("ymax")).floatValue() * FastMath.DEG_TO_RAD;\r
- float difference = 0.0f;\r
- if(angles[1] < ymin) {\r
- difference = (angles[1] - ymin) * influence;\r
- } else if(angles[1] > ymax) {\r
- difference = (angles[1] - ymax) * influence;\r
- }\r
- angles[1] -= difference;\r
- }\r
- if((flag & LIMIT_ZROT) != 0) {\r
- float zmin = ((Number)constraintStructure.getFieldValue("zmin")).floatValue() * FastMath.DEG_TO_RAD;\r
- float zmax = ((Number)constraintStructure.getFieldValue("zmax")).floatValue() * FastMath.DEG_TO_RAD;\r
- float difference = 0.0f;\r
- if(angles[2] < zmin) {\r
- difference = (angles[2] - zmin) * influence;\r
- } else if(angles[2] > zmax) {\r
- difference = (angles[2] - zmax) * influence;\r
- }\r
- angles[2] -= difference;\r
- }\r
- rotations[frame].fromAngles(angles);//TODO: consider constraint space !!!\r
- }\r
- boneTrack.setKeyframes(boneTrack.getTimes(), boneTrack.getTranslations(), rotations, boneTrack.getScales());\r
- }\r
- }\r
- };\r
-\r
- //SHRINKWRAP constraint (TODO: to implement)\r
- influenceFunctions[ConstraintType.CONSTRAINT_TYPE_SHRINKWRAP.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_SHRINKWRAP, dataRepository) {};\r
-\r
- //SIZELIKE constraint\r
- influenceFunctions[ConstraintType.CONSTRAINT_TYPE_SIZELIKE.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_SIZELIKE, dataRepository) {\r
- @Override\r
- public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation, Constraint constraint) {\r
- Structure constraintData = constraint.getData();\r
- this.validateConstraintType(constraintData);\r
- Vector3f targetScale = this.getTargetLocation(constraint);\r
- BoneTrack boneTrack = this.getBoneTrack(skeleton, boneAnimation, constraint);\r
- if(boneTrack != null) {\r
- int flag = ((Number)constraintData.getFieldValue("flag")).intValue();\r
- Vector3f[] scales = boneTrack.getScales();\r
- int maxFrames = scales.length;\r
- for(int frame = 0; frame < maxFrames; ++frame) {\r
- Vector3f offset = Vector3f.ZERO;\r
- if((flag & LOCLIKE_OFFSET) != 0) {//we add the original scale to the copied scale\r
- offset = scales[frame].clone();\r
- }\r
-\r
- if((flag & SIZELIKE_X) != 0) {\r
- scales[frame].x = targetScale.x;\r
- } else if((flag & SIZELIKE_Y) != 0) {\r
- scales[frame].y = targetScale.y;\r
- } else if((flag & SIZELIKE_Z) != 0) {\r
- scales[frame].z = targetScale.z;\r
- }\r
- scales[frame].addLocal(offset);//TODO: ipo influence\r
- //TODO: add or multiply???\r
- }\r
- boneTrack.setKeyframes(boneTrack.getTimes(), boneTrack.getTranslations(), boneTrack.getRotations(), scales);\r
- }\r
- }\r
- };\r
-\r
- //SIZELIMIT constraint\r
- influenceFunctions[ConstraintType.CONSTRAINT_TYPE_SIZELIMIT.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_SIZELIMIT, dataRepository) {\r
- @Override\r
- public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation, Constraint constraint) {\r
- Structure constraintStructure = constraint.getData();\r
- this.validateConstraintType(constraintStructure);\r
- BoneTrack boneTrack = this.getBoneTrack(skeleton, boneAnimation, constraint);\r
- if(boneTrack != null) {\r
- int flag = ((Number)constraintStructure.getFieldValue("flag")).intValue();\r
- Vector3f[] scales = boneTrack.getScales();\r
- int maxFrames = scales.length;\r
- for(int frame = 0; frame < maxFrames; ++frame) {\r
- float influence = constraint.getIpo().calculateValue(frame);\r
- if((flag & LIMIT_XMIN) != 0) {\r
- float xmin = ((Number)constraintStructure.getFieldValue("xmin")).floatValue();\r
- if(scales[frame].x < xmin) {\r
- scales[frame].x -= (scales[frame].x - xmin) * influence;\r
- }\r
- }\r
- if((flag & LIMIT_XMAX) != 0) {\r
- float xmax = ((Number)constraintStructure.getFieldValue("xmax")).floatValue();\r
- if(scales[frame].x > xmax) {\r
- scales[frame].x -= (scales[frame].x - xmax) * influence;\r
- }\r
- }\r
- if((flag & LIMIT_YMIN) != 0) {\r
- float ymin = ((Number)constraintStructure.getFieldValue("ymin")).floatValue();\r
- if(scales[frame].y < ymin) {\r
- scales[frame].y -= (scales[frame].y - ymin) * influence;\r
- }\r
- }\r
- if((flag & LIMIT_YMAX) != 0) {\r
- float ymax = ((Number)constraintStructure.getFieldValue("ymax")).floatValue();\r
- if(scales[frame].y > ymax) {\r
- scales[frame].y -= (scales[frame].y - ymax) * influence;\r
- }\r
- }\r
- if((flag & LIMIT_ZMIN) != 0) {\r
- float zmin = ((Number)constraintStructure.getFieldValue("zmin")).floatValue();\r
- if(scales[frame].z < zmin) {\r
- scales[frame].z -= (scales[frame].z - zmin) * influence;\r
- }\r
- }\r
- if((flag & LIMIT_ZMAX) != 0) {\r
- float zmax = ((Number)constraintStructure.getFieldValue("zmax")).floatValue();\r
- if(scales[frame].z > zmax) {\r
- scales[frame].z -= (scales[frame].z - zmax) * influence;\r
- }\r
- }//TODO: consider constraint space !!!\r
- }\r
- boneTrack.setKeyframes(boneTrack.getTimes(), boneTrack.getTranslations(), boneTrack.getRotations(), scales);\r
- }\r
- }\r
- };\r
-\r
- //STRETCHTO constraint (TODO: to implement)\r
- influenceFunctions[ConstraintType.CONSTRAINT_TYPE_STRETCHTO.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_STRETCHTO, dataRepository) {};\r
-\r
- //TRANSFORM constraint (TODO: to implement)\r
- influenceFunctions[ConstraintType.CONSTRAINT_TYPE_TRANSFORM.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_TRANSFORM, dataRepository) {};\r
- }\r
- }\r
-\r
- /**\r
- * This method reads constraints for for the given structure. The constraints are loaded only once for object/bone.\r
- * @param ownerOMA\r
- * the owner's old memory address\r
- * @param objectStructure\r
- * the structure we read constraint's for\r
- * @param dataRepository\r
- * the data repository\r
- * @throws BlenderFileException\r
- */\r
- public void loadConstraints(Structure objectStructure, DataRepository dataRepository) throws BlenderFileException {\r
- // reading influence ipos for the constraints\r
- IpoHelper ipoHelper = dataRepository.getHelper(IpoHelper.class);\r
- Map<String, Map<String, Ipo>> constraintsIpos = new HashMap<String, Map<String, Ipo>>();\r
- Pointer pActions = (Pointer)objectStructure.getFieldValue("action");\r
- if(!pActions.isNull()) {\r
- List<Structure> actions = pActions.fetchData(dataRepository.getInputStream());\r
- for(Structure action : actions) {\r
- Structure chanbase = (Structure)action.getFieldValue("chanbase");\r
- List<Structure> actionChannels = chanbase.evaluateListBase(dataRepository);\r
- for(Structure actionChannel : actionChannels) {\r
- Map<String, Ipo> ipos = new HashMap<String, Ipo>();\r
- Structure constChannels = (Structure)actionChannel.getFieldValue("constraintChannels");\r
- List<Structure> constraintChannels = constChannels.evaluateListBase(dataRepository);\r
- for(Structure constraintChannel : constraintChannels) {\r
- Pointer pIpo = (Pointer)constraintChannel.getFieldValue("ipo");\r
- if(!pIpo.isNull()) {\r
- String constraintName = constraintChannel.getFieldValue("name").toString();\r
- Ipo ipo = ipoHelper.createIpo(pIpo.fetchData(dataRepository.getInputStream()).get(0), dataRepository);\r
- ipos.put(constraintName, ipo);\r
- }\r
- }\r
- String actionName = actionChannel.getFieldValue("name").toString();\r
- constraintsIpos.put(actionName, ipos);\r
- }\r
- }\r
- }\r
-\r
- //loading constraints connected with the object's bones\r
- List<Constraint> constraintsList = new ArrayList<Constraint>();\r
- Pointer pPose = (Pointer)objectStructure.getFieldValue("pose");//TODO: what if the object has two armatures ????\r
- if(!pPose.isNull()) {\r
- //getting pose channels\r
- List<Structure> poseChannels = ((Structure)pPose.fetchData(dataRepository.getInputStream()).get(0).getFieldValue("chanbase")).evaluateListBase(dataRepository);\r
- for(Structure poseChannel : poseChannels) {\r
- Long boneOMA = Long.valueOf(((Pointer)poseChannel.getFieldValue("bone")).getOldMemoryAddress());\r
- //the name is read directly from structure because bone might not yet be loaded\r
- String name = dataRepository.getFileBlock(boneOMA).getStructure(dataRepository).getFieldValue("name").toString();\r
- List<Structure> constraints = ((Structure)poseChannel.getFieldValue("constraints")).evaluateListBase(dataRepository);\r
- for(Structure constraint : constraints) {\r
- int type = ((Number)constraint.getFieldValue("type")).intValue();\r
- String constraintName = constraint.getFieldValue("name").toString();\r
- Ipo ipo = constraintsIpos.get(name).get(constraintName);\r
- if(ipo == null) {\r
- float enforce = ((Number)constraint.getFieldValue("enforce")).floatValue();\r
- ipo = ipoHelper.createIpo(enforce);\r
- }\r
- Space ownerSpace = Space.valueOf(((Number)constraint.getFieldValue("ownspace")).byteValue());\r
- Space targetSpace = Space.valueOf(((Number)constraint.getFieldValue("tarspace")).byteValue());\r
- Constraint c = new Constraint(constraint, influenceFunctions[type], boneOMA, ownerSpace, targetSpace, ipo, dataRepository);\r
- constraintsList.add(c);\r
- }\r
- }\r
- }\r
- /* TODO: reading constraints for objects (implement when object's animation will be available)\r
- List<Structure> constraintChannels = ((Structure)objectStructure.getFieldValue("constraintChannels")).evaluateListBase(dataRepository);\r
- for(Structure constraintChannel : constraintChannels) {\r
- System.out.println(constraintChannel);\r
- }\r
-\r
- //loading constraints connected with the object itself (TODO: test this)\r
- if(!this.constraints.containsKey(objectStructure.getOldMemoryAddress())) {\r
- List<Structure> constraints = ((Structure)objectStructure.getFieldValue("constraints")).evaluateListBase(dataRepository);\r
- Constraint[] result = new Constraint[constraints.size()];\r
- int i = 0;\r
- for(Structure constraint : constraints) {\r
- int type = ((Number)constraint.getFieldValue("type")).intValue();\r
- String name = constraint.getFieldValue("name").toString();\r
- result[i++] = new Constraint(constraint, influenceFunctions[type], null, dataRepository);//TODO: influence ipos for object animation\r
- }\r
- this.constraints.put(objectStructure.getOldMemoryAddress(), result);\r
- }\r
- */\r
- if(constraintsList.size() > 0) {\r
- this.constraints.put(objectStructure.getOldMemoryAddress(), constraintsList.toArray(new Constraint[constraintsList.size()]));\r
- }\r
- }\r
-\r
- /**\r
- * This method returns a list of constraints of the feature's constraints. The order of constraints is important.\r
- * @param ownerOMA\r
- * the owner's old memory address\r
- * @return a table of constraints for the feature specified by old memory address\r
- */\r
- public Constraint[] getConstraints(Long ownerOMA) {\r
- return constraints.get(ownerOMA);\r
- }\r
-\r
- @Override\r
- public void clearState() {\r
- constraints.clear();\r
- }\r
-\r
- /**\r
- * The purpose of this class is to imitate bone's movement when calculating inverse kinematics.\r
- * @author Marcin Roguski\r
- */\r
- private static class CalculationBone extends Node {\r
- /** The name of the bone. Only to be used in toString method. */\r
- private String boneName;\r
- /** The bone's tracks. Will be altered at the end of calculation process. */\r
- private BoneTrack track;\r
- /** The starting position of the bone. */\r
- private Vector3f startTranslation;\r
- /** The starting rotation of the bone. */\r
- private Quaternion startRotation;\r
- /** The starting scale of the bone. */\r
- private Vector3f startScale;\r
- \r
- private Vector3f[] translations;\r
- private Quaternion[] rotations;\r
- private Vector3f[] scales;\r
-\r
- /**\r
- * Constructor. Stores the track, starting transformation and sets the transformation to the starting positions.\r
- * @param bone\r
- * the bone this class will imitate\r
- * @param track\r
- * the bone's tracks\r
- */\r
- public CalculationBone(Bone bone, BoneTrack track) {\r
- this.boneName = bone.getName();\r
- this.track = track;\r
- this.startRotation = bone.getModelSpaceRotation().clone();\r
- this.startTranslation = bone.getModelSpacePosition().clone();\r
- this.startScale = bone.getModelSpaceScale().clone();\r
- this.translations = track.getTranslations();\r
- this.rotations = track.getRotations();\r
- this.scales = track.getScales();\r
- this.reset();\r
- }\r
-\r
- /**\r
- * This method returns the end point of the bone. If the bone has parent it is calculated from the start point\r
- * of parent to the start point of this bone. If the bone doesn't have a parent the end location is considered\r
- * to be 1 point up along Y axis (scale is applied if set to != 1.0);\r
- * @return the end point of this bone\r
- */\r
- //TODO: set to Z axis if user defined it this way\r
- public Vector3f getEndPoint() {\r
- if(this.getParent() == null) {\r
- return new Vector3f(0, this.getLocalScale().y, 0);\r
- } else {\r
- Node parent = this.getParent();\r
- return parent.getWorldTranslation().subtract(this.getWorldTranslation()).multLocal(this.getWorldScale());\r
- }\r
- }\r
-\r
- /**\r
- * This method resets the calculation bone to the starting position.\r
- */\r
- public void reset() {\r
- this.setLocalTranslation(startTranslation);\r
- this.setLocalRotation(startRotation);\r
- this.setLocalScale(startScale);\r
- }\r
-\r
- @Override\r
- public int attachChild(Spatial child) {\r
- if(this.getChildren() != null && this.getChildren().size() > 1) {\r
- throw new IllegalStateException(this.getClass().getName() + " class instance can only have one child!");\r
- }\r
- return super.attachChild(child);\r
- }\r
- \r
- public Spatial rotate(Quaternion rot, int frame) {\r
- Spatial spatial = super.rotate(rot);\r
- this.updateWorldTransforms();\r
- if(this.getChildren()!=null && this.getChildren().size()>0) {\r
- CalculationBone child = (CalculationBone)this.getChild(0);\r
- child.updateWorldTransforms();\r
- }\r
- rotations[frame].set(this.getLocalRotation());\r
- translations[frame].set(this.getLocalTranslation());\r
- if(scales!=null) {\r
- scales[frame].set(this.getLocalScale());\r
- }\r
- return spatial;\r
- }\r
- \r
- public void applyCalculatedTracks() {\r
- track.setKeyframes(track.getTimes(), translations, rotations);//TODO:scales\r
- }\r
- \r
- @Override\r
- public String toString() {\r
- return boneName+ ": " + this.getLocalRotation() + " " + this.getLocalTranslation();\r
- }\r
- }\r
+\r
+ /**\r
+ * A table containing implementations of influence functions for constraints. It should contain functions for\r
+ * blender at least 249 and higher.\r
+ */\r
+ protected static AbstractInfluenceFunction[] influenceFunctions;\r
+ /**\r
+ * Constraints stored for object with the given old memory address.\r
+ */\r
+ protected Map<Long, Constraint[]> constraints = new HashMap<Long, Constraint[]>();\r
+\r
+ /**\r
+ * Helper constructor. It's main task is to generate the affection functions. These functions are common to all\r
+ * ConstraintHelper instances. Unfortunately this constructor might grow large. If it becomes too large - I shall\r
+ * consider refactoring. The constructor parses the given blender version and stores the result. Some\r
+ * functionalities may differ in different blender versions.\r
+ * @param blenderVersion\r
+ * the version read from the blend file\r
+ */\r
+ public ConstraintHelper(String blenderVersion, DataRepository dataRepository) {\r
+ super(blenderVersion);\r
+ if (influenceFunctions == null) {\r
+ //TODO: synchronization\r
+ influenceFunctions = new AbstractInfluenceFunction[ConstraintType.getLastDefinedTypeValue() + 1];\r
+ //ACTION constraint (TODO: to implement)\r
+ influenceFunctions[ConstraintType.CONSTRAINT_TYPE_ACTION.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_ACTION, dataRepository) {\r
+ };\r
+\r
+ //CHILDOF constraint (TODO: to implement)\r
+ influenceFunctions[ConstraintType.CONSTRAINT_TYPE_CHILDOF.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_CHILDOF, dataRepository) {\r
+ };\r
+\r
+ //CLAMPTO constraint\r
+ influenceFunctions[ConstraintType.CONSTRAINT_TYPE_CLAMPTO.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_CLAMPTO, dataRepository) {\r
+\r
+ @Override\r
+ public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation, Constraint constraint) {\r
+ this.validateConstraintType(constraint.getData());\r
+ LOGGER.log(Level.INFO, "{0} not active! Curves not yet implemented!", constraint.getName());//TODO: implement when curves are implemented\r
+ }\r
+ };\r
+\r
+ //DISTLIMIT constraint\r
+ influenceFunctions[ConstraintType.CONSTRAINT_TYPE_DISTLIMIT.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_DISTLIMIT, dataRepository) {\r
+\r
+ @Override\r
+ public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation, Constraint constraint) {\r
+ Structure constraintStructure = constraint.getData();\r
+ this.validateConstraintType(constraintStructure);\r
+ Vector3f targetLocation = this.getTargetLocation(constraint);\r
+ BoneTrack boneTrack = this.getBoneTrack(skeleton, boneAnimation, constraint);\r
+ if (boneTrack != null) {\r
+ //TODO: target vertex group !!!\r
+ float dist = ((Number) constraintStructure.getFieldValue("dist")).floatValue();\r
+ int mode = ((Number) constraintStructure.getFieldValue("mode")).intValue();\r
+\r
+ int maxFrames = boneTrack.getTimes().length;\r
+ Vector3f[] translations = boneTrack.getTranslations();\r
+ for (int frame = 0; frame < maxFrames; ++frame) {\r
+ Vector3f v = translations[frame].subtract(targetLocation);\r
+ float currentDistance = v.length();\r
+ float influence = constraint.getIpo().calculateValue(frame);\r
+ float modifier = 0.0f;\r
+ switch (mode) {\r
+ case LIMITDIST_INSIDE:\r
+ if (currentDistance >= dist) {\r
+ modifier = (dist - currentDistance) / currentDistance;\r
+ }\r
+ break;\r
+ case LIMITDIST_ONSURFACE:\r
+ modifier = (dist - currentDistance) / currentDistance;\r
+ break;\r
+ case LIMITDIST_OUTSIDE:\r
+ if (currentDistance <= dist) {\r
+ modifier = (dist - currentDistance) / currentDistance;\r
+ }\r
+ break;\r
+ default:\r
+ throw new IllegalStateException("Unknown distance limit constraint mode: " + mode);\r
+ }\r
+ translations[frame].addLocal(v.multLocal(modifier * influence));\r
+ }\r
+ boneTrack.setKeyframes(boneTrack.getTimes(), translations, boneTrack.getRotations(), boneTrack.getScales());\r
+ }\r
+ }\r
+ };\r
+\r
+ //FOLLOWPATH constraint\r
+ influenceFunctions[ConstraintType.CONSTRAINT_TYPE_FOLLOWPATH.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_FOLLOWPATH, dataRepository) {\r
+\r
+ @Override\r
+ public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation, Constraint constraint) {\r
+ this.validateConstraintType(constraint.getData());\r
+ LOGGER.log(Level.INFO, "{0} not active! Curves not yet implemented!", constraint.getName());//TODO: implement when curves are implemented\r
+ }\r
+ };\r
+\r
+ //KINEMATIC constraint (TODO: to implement)\r
+ influenceFunctions[ConstraintType.CONSTRAINT_TYPE_KINEMATIC.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_KINEMATIC, dataRepository) {\r
+\r
+ @Override\r
+ public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation, Constraint constraint) {\r
+ Structure constraintStructure = constraint.getData();\r
+ this.validateConstraintType(constraintStructure);\r
+ /*Long boneOMA = constraint.getBoneOMA();\r
+ //IK solver is only attached to bones\r
+ Bone ownerBone = (Bone)dataRepository.getLoadedFeature(boneOMA, LoadedFeatureDataType.LOADED_FEATURE);\r
+ \r
+ //get the target point\r
+ Object targetObject = this.getTarget(constraint, LoadedFeatureDataType.LOADED_FEATURE);\r
+ Vector3f pt = null;//Point Target\r
+ if(targetObject instanceof Bone) {\r
+ pt = ((Bone)targetObject).getModelSpacePosition();\r
+ } else if(targetObject instanceof Node) {\r
+ pt = ((Node)targetObject).getWorldTranslation();\r
+ } else if(targetObject instanceof Skeleton) {\r
+ Structure armatureNodeStructure = (Structure)this.getTarget(constraint, LoadedFeatureDataType.LOADED_STRUCTURE);\r
+ ObjectHelper objectHelper = dataRepository.getHelper(ObjectHelper.class);\r
+ Transform transform = objectHelper.getTransformation(armatureNodeStructure);\r
+ pt = transform.getTranslation();\r
+ } else {\r
+ throw new IllegalStateException("Unknown target object type! Should be Node, Bone or Skeleton and there is: " + targetObject.getClass().getName());\r
+ }\r
+ //preparing data\r
+ int maxIterations = ((Number)constraintStructure.getFieldValue("iterations")).intValue();\r
+ CalculationBone[] bones = this.getBonesToCalculate(ownerBone, skeleton, boneAnimation);\r
+ for(int i=0;i<bones.length;++i) {\r
+ System.out.println(Arrays.toString(bones[i].track.getTranslations()));\r
+ System.out.println(Arrays.toString(bones[i].track.getRotations()));\r
+ System.out.println("===============================");\r
+ }\r
+ Quaternion rotation = new Quaternion();\r
+ int maxFrames = bones[0].track.getTimes().length;//all tracks should have the same amount of frames\r
+ \r
+ for(int frame = 0; frame < maxFrames; ++frame) {\r
+ float error = IK_SOLVER_ERROR;\r
+ int iteration = 0;\r
+ while(error >= IK_SOLVER_ERROR && iteration <= maxIterations) {\r
+ //rotating the bones\r
+ for(int i = 0; i < bones.length - 1; ++i) {\r
+ Vector3f pe = bones[i].getEndPoint();\r
+ Vector3f pc = bones[i + 1].getWorldTranslation().clone();\r
+ \r
+ Vector3f peSUBpc = pe.subtract(pc).normalizeLocal();\r
+ Vector3f ptSUBpc = pt.subtract(pc).normalizeLocal();\r
+ \r
+ float theta = FastMath.acos(peSUBpc.dot(ptSUBpc));\r
+ Vector3f direction = peSUBpc.cross(ptSUBpc).normalizeLocal();\r
+ bones[i].rotate(rotation.fromAngleAxis(theta, direction), frame);\r
+ }\r
+ error = pt.subtract(bones[0].getEndPoint()).length();\r
+ ++iteration;\r
+ }\r
+ System.out.println("error = " + error + " iterations = " + iteration);\r
+ }\r
+ \r
+ for(CalculationBone bone : bones) {\r
+ bone.applyCalculatedTracks();\r
+ }\r
+ \r
+ System.out.println("&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&");\r
+ for(int i=0;i<bones.length;++i) {\r
+ System.out.println(Arrays.toString(bones[i].track.getTranslations()));\r
+ System.out.println(Arrays.toString(bones[i].track.getRotations()));\r
+ System.out.println("===============================");\r
+ }*/\r
+ }\r
+\r
+ /**\r
+ * This method returns bones used for rotation calculations.\r
+ * @param bone\r
+ * the bone to which the constraint is applied\r
+ * @param skeleton\r
+ * the skeleton owning the bone and its ancestors\r
+ * @param boneAnimation\r
+ * the bone animation data that stores the traces for the skeleton's bones\r
+ * @return a list of bones to imitate the bone's movement during IK solving\r
+ */\r
+ private CalculationBone[] getBonesToCalculate(Bone bone, Skeleton skeleton, BoneAnimation boneAnimation) {\r
+ List<CalculationBone> bonesList = new ArrayList<CalculationBone>();\r
+ Bone currentBone = bone;\r
+ do {\r
+ int boneIndex = skeleton.getBoneIndex(currentBone);\r
+ for (int i = 0; i < boneAnimation.getTracks().length; ++i) {\r
+ if (boneAnimation.getTracks()[i].getTargetBoneIndex() == boneIndex) {\r
+ bonesList.add(new CalculationBone(currentBone, boneAnimation.getTracks()[i]));\r
+ break;\r
+ }\r
+ }\r
+ currentBone = currentBone.getParent();\r
+ } while (currentBone != null);\r
+ //attaching children\r
+ CalculationBone[] result = bonesList.toArray(new CalculationBone[bonesList.size()]);\r
+ for (int i = result.length - 1; i > 0; --i) {\r
+ result[i].attachChild(result[i - 1]);\r
+ }\r
+ return result;\r
+ }\r
+ };\r
+\r
+ //LOCKTRACK constraint (TODO: to implement)\r
+ influenceFunctions[ConstraintType.CONSTRAINT_TYPE_LOCKTRACK.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_LOCKTRACK, dataRepository) {\r
+ };\r
+\r
+ //LOCLIKE constraint\r
+ influenceFunctions[ConstraintType.CONSTRAINT_TYPE_LOCLIKE.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_LOCLIKE, dataRepository) {\r
+\r
+ @Override\r
+ public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation, Constraint constraint) {\r
+ Structure constraintData = constraint.getData();\r
+ this.validateConstraintType(constraintData);\r
+ BoneTrack boneTrack = this.getBoneTrack(skeleton, boneAnimation, constraint);\r
+ if (boneTrack != null) {\r
+ Vector3f targetLocation = this.getTargetLocation(constraint);\r
+ int flag = ((Number) constraintData.getFieldValue("flag")).intValue();\r
+ Vector3f[] translations = boneTrack.getTranslations();\r
+ int maxFrames = translations.length;\r
+ for (int frame = 0; frame < maxFrames; ++frame) {\r
+ Vector3f offset = Vector3f.ZERO;\r
+ if ((flag & LOCLIKE_OFFSET) != 0) {//we add the original location to the copied location\r
+ offset = translations[frame].clone();\r
+ }\r
+\r
+ if ((flag & LOCLIKE_X) != 0) {\r
+ translations[frame].x = targetLocation.x;\r
+ if ((flag & LOCLIKE_X_INVERT) != 0) {\r
+ translations[frame].x = -translations[frame].x;\r
+ }\r
+ } else if ((flag & LOCLIKE_Y) != 0) {\r
+ translations[frame].y = targetLocation.y;\r
+ if ((flag & LOCLIKE_Y_INVERT) != 0) {\r
+ translations[frame].y = -translations[frame].y;\r
+ }\r
+ } else if ((flag & LOCLIKE_Z) != 0) {\r
+ translations[frame].z = targetLocation.z;\r
+ if ((flag & LOCLIKE_Z_INVERT) != 0) {\r
+ translations[frame].z = -translations[frame].z;\r
+ }\r
+ }\r
+ translations[frame].addLocal(offset);//TODO: ipo influence\r
+ }\r
+ boneTrack.setKeyframes(boneTrack.getTimes(), translations, boneTrack.getRotations(), boneTrack.getScales());\r
+ }\r
+ }\r
+ };\r
+\r
+ //LOCLIMIT constraint\r
+ influenceFunctions[ConstraintType.CONSTRAINT_TYPE_LOCLIMIT.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_LOCLIMIT, dataRepository) {\r
+\r
+ @Override\r
+ public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation, Constraint constraint) {\r
+ Structure constraintStructure = constraint.getData();\r
+ this.validateConstraintType(constraintStructure);\r
+ BoneTrack boneTrack = this.getBoneTrack(skeleton, boneAnimation, constraint);\r
+ if (boneTrack != null) {\r
+ int flag = ((Number) constraintStructure.getFieldValue("flag")).intValue();\r
+ Vector3f[] translations = boneTrack.getTranslations();\r
+ int maxFrames = translations.length;\r
+ for (int frame = 0; frame < maxFrames; ++frame) {\r
+ float influence = constraint.getIpo().calculateValue(frame);\r
+ if ((flag & LIMIT_XMIN) != 0) {\r
+ float xmin = ((Number) constraintStructure.getFieldValue("xmin")).floatValue();\r
+ if (translations[frame].x < xmin) {\r
+ translations[frame].x -= (translations[frame].x - xmin) * influence;\r
+ }\r
+ }\r
+ if ((flag & LIMIT_XMAX) != 0) {\r
+ float xmax = ((Number) constraintStructure.getFieldValue("xmax")).floatValue();\r
+ if (translations[frame].x > xmax) {\r
+ translations[frame].x -= (translations[frame].x - xmax) * influence;\r
+ }\r
+ }\r
+ if ((flag & LIMIT_YMIN) != 0) {\r
+ float ymin = ((Number) constraintStructure.getFieldValue("ymin")).floatValue();\r
+ if (translations[frame].y < ymin) {\r
+ translations[frame].y -= (translations[frame].y - ymin) * influence;\r
+ }\r
+ }\r
+ if ((flag & LIMIT_YMAX) != 0) {\r
+ float ymax = ((Number) constraintStructure.getFieldValue("ymax")).floatValue();\r
+ if (translations[frame].y > ymax) {\r
+ translations[frame].y -= (translations[frame].y - ymax) * influence;\r
+ }\r
+ }\r
+ if ((flag & LIMIT_ZMIN) != 0) {\r
+ float zmin = ((Number) constraintStructure.getFieldValue("zmin")).floatValue();\r
+ if (translations[frame].z < zmin) {\r
+ translations[frame].z -= (translations[frame].z - zmin) * influence;\r
+ }\r
+ }\r
+ if ((flag & LIMIT_ZMAX) != 0) {\r
+ float zmax = ((Number) constraintStructure.getFieldValue("zmax")).floatValue();\r
+ if (translations[frame].z > zmax) {\r
+ translations[frame].z -= (translations[frame].z - zmax) * influence;\r
+ }\r
+ }//TODO: consider constraint space !!!\r
+ }\r
+ boneTrack.setKeyframes(boneTrack.getTimes(), translations, boneTrack.getRotations(), boneTrack.getScales());\r
+ }\r
+ }\r
+ };\r
+\r
+ //MINMAX constraint (TODO: to implement)\r
+ influenceFunctions[ConstraintType.CONSTRAINT_TYPE_MINMAX.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_MINMAX, dataRepository) {\r
+ };\r
+\r
+ //NULL constraint - does nothing\r
+ influenceFunctions[ConstraintType.CONSTRAINT_TYPE_NULL.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_NULL, dataRepository) {\r
+ };\r
+\r
+ //PYTHON constraint (TODO: to implement)\r
+ influenceFunctions[ConstraintType.CONSTRAINT_TYPE_PYTHON.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_PYTHON, dataRepository) {\r
+ };\r
+\r
+ //RIGIDBODYJOINT constraint (TODO: to implement)\r
+ influenceFunctions[ConstraintType.CONSTRAINT_TYPE_RIGIDBODYJOINT.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_RIGIDBODYJOINT, dataRepository) {\r
+ };\r
+\r
+ //ROTLIKE constraint\r
+ influenceFunctions[ConstraintType.CONSTRAINT_TYPE_ROTLIKE.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_ROTLIKE, dataRepository) {\r
+\r
+ @Override\r
+ public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation, Constraint constraint) {\r
+ Structure constraintData = constraint.getData();\r
+ this.validateConstraintType(constraintData);\r
+ BoneTrack boneTrack = this.getBoneTrack(skeleton, boneAnimation, constraint);\r
+ if (boneTrack != null) {\r
+ Quaternion targetRotation = this.getTargetRotation(constraint);\r
+ int flag = ((Number) constraintData.getFieldValue("flag")).intValue();\r
+ float[] targetAngles = targetRotation.toAngles(null);\r
+ Quaternion[] rotations = boneTrack.getRotations();\r
+ int maxFrames = rotations.length;\r
+ for (int frame = 0; frame < maxFrames; ++frame) {\r
+ float[] angles = rotations[frame].toAngles(null);\r
+\r
+ Quaternion offset = Quaternion.IDENTITY;\r
+ if ((flag & ROTLIKE_OFFSET) != 0) {//we add the original rotation to the copied rotation\r
+ offset = rotations[frame].clone();\r
+ }\r
+\r
+ if ((flag & ROTLIKE_X) != 0) {\r
+ angles[0] = targetAngles[0];\r
+ if ((flag & ROTLIKE_X_INVERT) != 0) {\r
+ angles[0] = -angles[0];\r
+ }\r
+ } else if ((flag & ROTLIKE_Y) != 0) {\r
+ angles[1] = targetAngles[1];\r
+ if ((flag & ROTLIKE_Y_INVERT) != 0) {\r
+ angles[1] = -angles[1];\r
+ }\r
+ } else if ((flag & ROTLIKE_Z) != 0) {\r
+ angles[2] = targetAngles[2];\r
+ if ((flag & ROTLIKE_Z_INVERT) != 0) {\r
+ angles[2] = -angles[2];\r
+ }\r
+ }\r
+ rotations[frame].fromAngles(angles).multLocal(offset);//TODO: ipo influence\r
+ }\r
+ boneTrack.setKeyframes(boneTrack.getTimes(), boneTrack.getTranslations(), rotations, boneTrack.getScales());\r
+ }\r
+ }\r
+ };\r
+\r
+ //ROTLIMIT constraint\r
+ influenceFunctions[ConstraintType.CONSTRAINT_TYPE_ROTLIMIT.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_ROTLIMIT, dataRepository) {\r
+\r
+ @Override\r
+ public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation, Constraint constraint) {\r
+ Structure constraintStructure = constraint.getData();\r
+ this.validateConstraintType(constraintStructure);\r
+ BoneTrack boneTrack = this.getBoneTrack(skeleton, boneAnimation, constraint);\r
+ if (boneTrack != null) {\r
+ int flag = ((Number) constraintStructure.getFieldValue("flag")).intValue();\r
+ Quaternion[] rotations = boneTrack.getRotations();\r
+ int maxFrames = rotations.length;\r
+ for (int frame = 0; frame < maxFrames; ++frame) {\r
+ float[] angles = rotations[frame].toAngles(null);\r
+ float influence = constraint.getIpo().calculateValue(frame);\r
+ if ((flag & LIMIT_XROT) != 0) {\r
+ float xmin = ((Number) constraintStructure.getFieldValue("xmin")).floatValue() * FastMath.DEG_TO_RAD;\r
+ float xmax = ((Number) constraintStructure.getFieldValue("xmax")).floatValue() * FastMath.DEG_TO_RAD;\r
+ float difference = 0.0f;\r
+ if (angles[0] < xmin) {\r
+ difference = (angles[0] - xmin) * influence;\r
+ } else if (angles[0] > xmax) {\r
+ difference = (angles[0] - xmax) * influence;\r
+ }\r
+ angles[0] -= difference;\r
+ }\r
+ if ((flag & LIMIT_YROT) != 0) {\r
+ float ymin = ((Number) constraintStructure.getFieldValue("ymin")).floatValue() * FastMath.DEG_TO_RAD;\r
+ float ymax = ((Number) constraintStructure.getFieldValue("ymax")).floatValue() * FastMath.DEG_TO_RAD;\r
+ float difference = 0.0f;\r
+ if (angles[1] < ymin) {\r
+ difference = (angles[1] - ymin) * influence;\r
+ } else if (angles[1] > ymax) {\r
+ difference = (angles[1] - ymax) * influence;\r
+ }\r
+ angles[1] -= difference;\r
+ }\r
+ if ((flag & LIMIT_ZROT) != 0) {\r
+ float zmin = ((Number) constraintStructure.getFieldValue("zmin")).floatValue() * FastMath.DEG_TO_RAD;\r
+ float zmax = ((Number) constraintStructure.getFieldValue("zmax")).floatValue() * FastMath.DEG_TO_RAD;\r
+ float difference = 0.0f;\r
+ if (angles[2] < zmin) {\r
+ difference = (angles[2] - zmin) * influence;\r
+ } else if (angles[2] > zmax) {\r
+ difference = (angles[2] - zmax) * influence;\r
+ }\r
+ angles[2] -= difference;\r
+ }\r
+ rotations[frame].fromAngles(angles);//TODO: consider constraint space !!!\r
+ }\r
+ boneTrack.setKeyframes(boneTrack.getTimes(), boneTrack.getTranslations(), rotations, boneTrack.getScales());\r
+ }\r
+ }\r
+ };\r
+\r
+ //SHRINKWRAP constraint (TODO: to implement)\r
+ influenceFunctions[ConstraintType.CONSTRAINT_TYPE_SHRINKWRAP.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_SHRINKWRAP, dataRepository) {\r
+ };\r
+\r
+ //SIZELIKE constraint\r
+ influenceFunctions[ConstraintType.CONSTRAINT_TYPE_SIZELIKE.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_SIZELIKE, dataRepository) {\r
+\r
+ @Override\r
+ public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation, Constraint constraint) {\r
+ Structure constraintData = constraint.getData();\r
+ this.validateConstraintType(constraintData);\r
+ Vector3f targetScale = this.getTargetLocation(constraint);\r
+ BoneTrack boneTrack = this.getBoneTrack(skeleton, boneAnimation, constraint);\r
+ if (boneTrack != null) {\r
+ int flag = ((Number) constraintData.getFieldValue("flag")).intValue();\r
+ Vector3f[] scales = boneTrack.getScales();\r
+ int maxFrames = scales.length;\r
+ for (int frame = 0; frame < maxFrames; ++frame) {\r
+ Vector3f offset = Vector3f.ZERO;\r
+ if ((flag & LOCLIKE_OFFSET) != 0) {//we add the original scale to the copied scale\r
+ offset = scales[frame].clone();\r
+ }\r
+\r
+ if ((flag & SIZELIKE_X) != 0) {\r
+ scales[frame].x = targetScale.x;\r
+ } else if ((flag & SIZELIKE_Y) != 0) {\r
+ scales[frame].y = targetScale.y;\r
+ } else if ((flag & SIZELIKE_Z) != 0) {\r
+ scales[frame].z = targetScale.z;\r
+ }\r
+ scales[frame].addLocal(offset);//TODO: ipo influence\r
+ //TODO: add or multiply???\r
+ }\r
+ boneTrack.setKeyframes(boneTrack.getTimes(), boneTrack.getTranslations(), boneTrack.getRotations(), scales);\r
+ }\r
+ }\r
+ };\r
+\r
+ //SIZELIMIT constraint\r
+ influenceFunctions[ConstraintType.CONSTRAINT_TYPE_SIZELIMIT.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_SIZELIMIT, dataRepository) {\r
+\r
+ @Override\r
+ public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation, Constraint constraint) {\r
+ Structure constraintStructure = constraint.getData();\r
+ this.validateConstraintType(constraintStructure);\r
+ BoneTrack boneTrack = this.getBoneTrack(skeleton, boneAnimation, constraint);\r
+ if (boneTrack != null) {\r
+ int flag = ((Number) constraintStructure.getFieldValue("flag")).intValue();\r
+ Vector3f[] scales = boneTrack.getScales();\r
+ int maxFrames = scales.length;\r
+ for (int frame = 0; frame < maxFrames; ++frame) {\r
+ float influence = constraint.getIpo().calculateValue(frame);\r
+ if ((flag & LIMIT_XMIN) != 0) {\r
+ float xmin = ((Number) constraintStructure.getFieldValue("xmin")).floatValue();\r
+ if (scales[frame].x < xmin) {\r
+ scales[frame].x -= (scales[frame].x - xmin) * influence;\r
+ }\r
+ }\r
+ if ((flag & LIMIT_XMAX) != 0) {\r
+ float xmax = ((Number) constraintStructure.getFieldValue("xmax")).floatValue();\r
+ if (scales[frame].x > xmax) {\r
+ scales[frame].x -= (scales[frame].x - xmax) * influence;\r
+ }\r
+ }\r
+ if ((flag & LIMIT_YMIN) != 0) {\r
+ float ymin = ((Number) constraintStructure.getFieldValue("ymin")).floatValue();\r
+ if (scales[frame].y < ymin) {\r
+ scales[frame].y -= (scales[frame].y - ymin) * influence;\r
+ }\r
+ }\r
+ if ((flag & LIMIT_YMAX) != 0) {\r
+ float ymax = ((Number) constraintStructure.getFieldValue("ymax")).floatValue();\r
+ if (scales[frame].y > ymax) {\r
+ scales[frame].y -= (scales[frame].y - ymax) * influence;\r
+ }\r
+ }\r
+ if ((flag & LIMIT_ZMIN) != 0) {\r
+ float zmin = ((Number) constraintStructure.getFieldValue("zmin")).floatValue();\r
+ if (scales[frame].z < zmin) {\r
+ scales[frame].z -= (scales[frame].z - zmin) * influence;\r
+ }\r
+ }\r
+ if ((flag & LIMIT_ZMAX) != 0) {\r
+ float zmax = ((Number) constraintStructure.getFieldValue("zmax")).floatValue();\r
+ if (scales[frame].z > zmax) {\r
+ scales[frame].z -= (scales[frame].z - zmax) * influence;\r
+ }\r
+ }//TODO: consider constraint space !!!\r
+ }\r
+ boneTrack.setKeyframes(boneTrack.getTimes(), boneTrack.getTranslations(), boneTrack.getRotations(), scales);\r
+ }\r
+ }\r
+ };\r
+\r
+ //STRETCHTO constraint (TODO: to implement)\r
+ influenceFunctions[ConstraintType.CONSTRAINT_TYPE_STRETCHTO.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_STRETCHTO, dataRepository) {\r
+ };\r
+\r
+ //TRANSFORM constraint (TODO: to implement)\r
+ influenceFunctions[ConstraintType.CONSTRAINT_TYPE_TRANSFORM.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_TRANSFORM, dataRepository) {\r
+ };\r
+ }\r
+ }\r
+\r
+ /**\r
+ * This method reads constraints for for the given structure. The constraints are loaded only once for object/bone.\r
+ * @param ownerOMA\r
+ * the owner's old memory address\r
+ * @param objectStructure\r
+ * the structure we read constraint's for\r
+ * @param dataRepository\r
+ * the data repository\r
+ * @throws BlenderFileException\r
+ */\r
+ public void loadConstraints(Structure objectStructure, DataRepository dataRepository) throws BlenderFileException {\r
+ // reading influence ipos for the constraints\r
+ IpoHelper ipoHelper = dataRepository.getHelper(IpoHelper.class);\r
+ Map<String, Map<String, Ipo>> constraintsIpos = new HashMap<String, Map<String, Ipo>>();\r
+ Pointer pActions = (Pointer) objectStructure.getFieldValue("action");\r
+ if (!pActions.isNull()) {\r
+ List<Structure> actions = pActions.fetchData(dataRepository.getInputStream());\r
+ for (Structure action : actions) {\r
+ Structure chanbase = (Structure) action.getFieldValue("chanbase");\r
+ List<Structure> actionChannels = chanbase.evaluateListBase(dataRepository);\r
+ for (Structure actionChannel : actionChannels) {\r
+ Map<String, Ipo> ipos = new HashMap<String, Ipo>();\r
+ Structure constChannels = (Structure) actionChannel.getFieldValue("constraintChannels");\r
+ List<Structure> constraintChannels = constChannels.evaluateListBase(dataRepository);\r
+ for (Structure constraintChannel : constraintChannels) {\r
+ Pointer pIpo = (Pointer) constraintChannel.getFieldValue("ipo");\r
+ if (!pIpo.isNull()) {\r
+ String constraintName = constraintChannel.getFieldValue("name").toString();\r
+ Ipo ipo = ipoHelper.createIpo(pIpo.fetchData(dataRepository.getInputStream()).get(0), dataRepository);\r
+ ipos.put(constraintName, ipo);\r
+ }\r
+ }\r
+ String actionName = actionChannel.getFieldValue("name").toString();\r
+ constraintsIpos.put(actionName, ipos);\r
+ }\r
+ }\r
+ }\r
+\r
+ //loading constraints connected with the object's bones\r
+ List<Constraint> constraintsList = new ArrayList<Constraint>();\r
+ Pointer pPose = (Pointer) objectStructure.getFieldValue("pose");//TODO: what if the object has two armatures ????\r
+ if (!pPose.isNull()) {\r
+ //getting pose channels\r
+ List<Structure> poseChannels = ((Structure) pPose.fetchData(dataRepository.getInputStream()).get(0).getFieldValue("chanbase")).evaluateListBase(dataRepository);\r
+ for (Structure poseChannel : poseChannels) {\r
+ Long boneOMA = Long.valueOf(((Pointer) poseChannel.getFieldValue("bone")).getOldMemoryAddress());\r
+ //the name is read directly from structure because bone might not yet be loaded\r
+ String name = dataRepository.getFileBlock(boneOMA).getStructure(dataRepository).getFieldValue("name").toString();\r
+ List<Structure> constraints = ((Structure) poseChannel.getFieldValue("constraints")).evaluateListBase(dataRepository);\r
+ for (Structure constraint : constraints) {\r
+ int type = ((Number) constraint.getFieldValue("type")).intValue();\r
+ String constraintName = constraint.getFieldValue("name").toString();\r
+ Ipo ipo = constraintsIpos.get(name).get(constraintName);\r
+ if (ipo == null) {\r
+ float enforce = ((Number) constraint.getFieldValue("enforce")).floatValue();\r
+ ipo = ipoHelper.createIpo(enforce);\r
+ }\r
+ Space ownerSpace = Space.valueOf(((Number) constraint.getFieldValue("ownspace")).byteValue());\r
+ Space targetSpace = Space.valueOf(((Number) constraint.getFieldValue("tarspace")).byteValue());\r
+ Constraint c = new Constraint(constraint, influenceFunctions[type], boneOMA, ownerSpace, targetSpace, ipo, dataRepository);\r
+ constraintsList.add(c);\r
+ }\r
+ }\r
+ }\r
+ /* TODO: reading constraints for objects (implement when object's animation will be available)\r
+ List<Structure> constraintChannels = ((Structure)objectStructure.getFieldValue("constraintChannels")).evaluateListBase(dataRepository);\r
+ for(Structure constraintChannel : constraintChannels) {\r
+ System.out.println(constraintChannel);\r
+ }\r
+ \r
+ //loading constraints connected with the object itself (TODO: test this)\r
+ if(!this.constraints.containsKey(objectStructure.getOldMemoryAddress())) {\r
+ List<Structure> constraints = ((Structure)objectStructure.getFieldValue("constraints")).evaluateListBase(dataRepository);\r
+ Constraint[] result = new Constraint[constraints.size()];\r
+ int i = 0;\r
+ for(Structure constraint : constraints) {\r
+ int type = ((Number)constraint.getFieldValue("type")).intValue();\r
+ String name = constraint.getFieldValue("name").toString();\r
+ result[i++] = new Constraint(constraint, influenceFunctions[type], null, dataRepository);//TODO: influence ipos for object animation\r
+ }\r
+ this.constraints.put(objectStructure.getOldMemoryAddress(), result);\r
+ }\r
+ */\r
+ if (constraintsList.size() > 0) {\r
+ this.constraints.put(objectStructure.getOldMemoryAddress(), constraintsList.toArray(new Constraint[constraintsList.size()]));\r
+ }\r
+ }\r
+\r
+ /**\r
+ * This method returns a list of constraints of the feature's constraints. The order of constraints is important.\r
+ * @param ownerOMA\r
+ * the owner's old memory address\r
+ * @return a table of constraints for the feature specified by old memory address\r
+ */\r
+ public Constraint[] getConstraints(Long ownerOMA) {\r
+ return constraints.get(ownerOMA);\r
+ }\r
+\r
+ @Override\r
+ public void clearState() {\r
+ constraints.clear();\r
+ }\r
+\r
+ /**\r
+ * The purpose of this class is to imitate bone's movement when calculating inverse kinematics.\r
+ * @author Marcin Roguski\r
+ */\r
+ private static class CalculationBone extends Node {\r
+\r
+ /** The name of the bone. Only to be used in toString method. */\r
+ private String boneName;\r
+ /** The bone's tracks. Will be altered at the end of calculation process. */\r
+ private BoneTrack track;\r
+ /** The starting position of the bone. */\r
+ private Vector3f startTranslation;\r
+ /** The starting rotation of the bone. */\r
+ private Quaternion startRotation;\r
+ /** The starting scale of the bone. */\r
+ private Vector3f startScale;\r
+ private Vector3f[] translations;\r
+ private Quaternion[] rotations;\r
+ private Vector3f[] scales;\r
+\r
+ /**\r
+ * Constructor. Stores the track, starting transformation and sets the transformation to the starting positions.\r
+ * @param bone\r
+ * the bone this class will imitate\r
+ * @param track\r
+ * the bone's tracks\r
+ */\r
+ public CalculationBone(Bone bone, BoneTrack track) {\r
+ this.boneName = bone.getName();\r
+ this.track = track;\r
+ this.startRotation = bone.getModelSpaceRotation().clone();\r
+ this.startTranslation = bone.getModelSpacePosition().clone();\r
+ this.startScale = bone.getModelSpaceScale().clone();\r
+ this.translations = track.getTranslations();\r
+ this.rotations = track.getRotations();\r
+ this.scales = track.getScales();\r
+ this.reset();\r
+ }\r
+\r
+ /**\r
+ * This method returns the end point of the bone. If the bone has parent it is calculated from the start point\r
+ * of parent to the start point of this bone. If the bone doesn't have a parent the end location is considered\r
+ * to be 1 point up along Y axis (scale is applied if set to != 1.0);\r
+ * @return the end point of this bone\r
+ */\r
+ //TODO: set to Z axis if user defined it this way\r
+ public Vector3f getEndPoint() {\r
+ if (this.getParent() == null) {\r
+ return new Vector3f(0, this.getLocalScale().y, 0);\r
+ } else {\r
+ Node parent = this.getParent();\r
+ return parent.getWorldTranslation().subtract(this.getWorldTranslation()).multLocal(this.getWorldScale());\r
+ }\r
+ }\r
+\r
+ /**\r
+ * This method resets the calculation bone to the starting position.\r
+ */\r
+ public void reset() {\r
+ this.setLocalTranslation(startTranslation);\r
+ this.setLocalRotation(startRotation);\r
+ this.setLocalScale(startScale);\r
+ }\r
+\r
+ @Override\r
+ public int attachChild(Spatial child) {\r
+ if (this.getChildren() != null && this.getChildren().size() > 1) {\r
+ throw new IllegalStateException(this.getClass().getName() + " class instance can only have one child!");\r
+ }\r
+ return super.attachChild(child);\r
+ }\r
+\r
+ public Spatial rotate(Quaternion rot, int frame) {\r
+ Spatial spatial = super.rotate(rot);\r
+ this.updateWorldTransforms();\r
+ if (this.getChildren() != null && this.getChildren().size() > 0) {\r
+ CalculationBone child = (CalculationBone) this.getChild(0);\r
+ child.updateWorldTransforms();\r
+ }\r
+ rotations[frame].set(this.getLocalRotation());\r
+ translations[frame].set(this.getLocalTranslation());\r
+ if (scales != null) {\r
+ scales[frame].set(this.getLocalScale());\r
+ }\r
+ return spatial;\r
+ }\r
+\r
+ public void applyCalculatedTracks() {\r
+ track.setKeyframes(track.getTimes(), translations, rotations);//TODO:scales\r
+ }\r
+\r
+ @Override\r
+ public String toString() {\r
+ return boneName + ": " + this.getLocalRotation() + " " + this.getLocalTranslation();\r
+ }\r
+ }\r
}\r
* @author Marcin Roguski\r
*/\r
public class CurvesHelper extends AbstractBlenderHelper {\r
- private static final Logger LOGGER = Logger.getLogger(CurvesHelper.class.getName());\r
- \r
- /** This variable indicates if the Y asxis is the UP axis or not. */\r
- protected boolean fixUpAxis;\r
- /** Minimum basis U function degree for NURBS curves and surfaces. */\r
- protected int minimumBasisUFunctionDegree = 4;\r
- /** Minimum basis V function degree for NURBS curves and surfaces. */\r
- protected int minimumBasisVFunctionDegree = 4;\r
- \r
- /**\r
- * This constructor parses the given blender version and stores the result. Some functionalities may differ in\r
- * different blender versions.\r
- * @param blenderVersion\r
- * the version read from the blend file\r
- */\r
- public CurvesHelper(String blenderVersion) {\r
- super(blenderVersion);\r
- }\r
- \r
- /**\r
- * This method sets the Y is UP axis. By default the UP axis is Z (just like in blender).\r
- * @param fixUpAxis\r
- * a variable that indicates if the Y asxis is the UP axis or not\r
- */\r
- public void setyIsUpAxis(boolean fixUpAxis) {\r
- this.fixUpAxis = fixUpAxis;\r
- }\r
-\r
- /**\r
- * This method converts given curve structure into a list of geometries representing the curve. The list is used here because on object\r
- * can have several separate curves.\r
- * @param curveStructure\r
- * the curve structure\r
- * @param dataRepository\r
- * the data repository\r
- * @return a list of geometries repreenting a single curve object\r
- * @throws BlenderFileException\r
- */\r
- public List<Geometry> toCurve(Structure curveStructure, DataRepository dataRepository) throws BlenderFileException {\r
- String name = curveStructure.getName();\r
- int flag = ((Number)curveStructure.getFieldValue("flag")).intValue();\r
- boolean is3D = (flag & 0x01) != 0;\r
- boolean isFront = (flag & 0x02) != 0 && !is3D;\r
- boolean isBack = (flag & 0x04) != 0 && !is3D;\r
- if(isFront) {\r
- LOGGER.warning("No front face in curve implemented yet!");//TODO: implement front face\r
- }\r
- if(isBack) {\r
- LOGGER.warning("No back face in curve implemented yet!");//TODO: implement back face\r
- }\r
- \r
- //reading nurbs (and sorting them by material)\r
- List<Structure> nurbStructures = ((Structure)curveStructure.getFieldValue("nurb")).evaluateListBase(dataRepository);\r
- Map<Number, List<Structure>> nurbs = new HashMap<Number, List<Structure>>();\r
- for(Structure nurb : nurbStructures) {\r
- Number matNumber = (Number) nurb.getFieldValue("mat_nr");\r
- List<Structure> nurbList = nurbs.get(matNumber);\r
- if(nurbList==null) {\r
- nurbList = new ArrayList<Structure>();\r
- nurbs.put(matNumber, nurbList);\r
- }\r
- nurbList.add(nurb);\r
- }\r
- \r
- //getting materials\r
- MaterialHelper materialHelper = dataRepository.getHelper(MaterialHelper.class);\r
- Material[] materials = materialHelper.getMaterials(curveStructure, dataRepository);\r
- if(materials==null) {\r
- materials = new Material[] { dataRepository.getDefaultMaterial().clone() };\r
- }\r
- for(Material material : materials) {\r
- material.getAdditionalRenderState().setFaceCullMode(FaceCullMode.Off);\r
- }\r
- \r
- //getting or creating bevel object\r
- List<Geometry> bevelObject = null;\r
- Pointer pBevelObject = (Pointer) curveStructure.getFieldValue("bevobj");\r
- if(!pBevelObject.isNull()) {\r
- Pointer pBevelStructure = (Pointer) pBevelObject.fetchData(dataRepository.getInputStream()).get(0).getFieldValue("data");\r
- Structure bevelStructure = pBevelStructure.fetchData(dataRepository.getInputStream()).get(0);\r
- bevelObject = this.toCurve(bevelStructure, dataRepository);\r
- } else {\r
- int bevResol = ((Number)curveStructure.getFieldValue("bevresol")).intValue();\r
- float extrude = ((Number)curveStructure.getFieldValue("ext1")).floatValue();\r
- float bevelDepth = ((Number)curveStructure.getFieldValue("ext2")).floatValue();\r
- if(bevelDepth>0.0f) {\r
- float handlerLength = bevelDepth/2.0f;\r
- \r
- List<Vector3f> conrtolPoints = new ArrayList<Vector3f>(extrude>0.0f ? 19 : 13);\r
- conrtolPoints.add(new Vector3f(-bevelDepth,extrude,0));\r
- conrtolPoints.add(new Vector3f(-bevelDepth,handlerLength+extrude,0));\r
- \r
- conrtolPoints.add(new Vector3f(-handlerLength,bevelDepth+extrude,0));\r
- conrtolPoints.add(new Vector3f(0,bevelDepth+extrude,0));\r
- conrtolPoints.add(new Vector3f(handlerLength,bevelDepth+extrude,0));\r
- \r
- conrtolPoints.add(new Vector3f(bevelDepth,extrude + handlerLength,0));\r
- conrtolPoints.add(new Vector3f(bevelDepth,extrude,0));\r
- conrtolPoints.add(new Vector3f(bevelDepth,extrude - handlerLength,0));\r
- \r
- if(extrude>0.0f) {\r
- conrtolPoints.add(new Vector3f(bevelDepth,-extrude + handlerLength,0));\r
- conrtolPoints.add(new Vector3f(bevelDepth,-extrude,0));\r
- conrtolPoints.add(new Vector3f(bevelDepth,-extrude-handlerLength,0));\r
- }\r
- \r
- conrtolPoints.add(new Vector3f(handlerLength,-bevelDepth-extrude,0));\r
- conrtolPoints.add(new Vector3f(0,-bevelDepth-extrude,0));\r
- conrtolPoints.add(new Vector3f(-handlerLength,-bevelDepth-extrude,0));\r
- \r
- conrtolPoints.add(new Vector3f(-bevelDepth,-handlerLength - extrude,0));\r
- conrtolPoints.add(new Vector3f(-bevelDepth,-extrude,0));\r
- \r
- if(extrude>0.0f) {\r
- conrtolPoints.add(new Vector3f(-bevelDepth,handlerLength - extrude,0));\r
- \r
- conrtolPoints.add(new Vector3f(-bevelDepth,-handlerLength + extrude,0));\r
- conrtolPoints.add(new Vector3f(-bevelDepth,extrude,0));\r
- }\r
- \r
- Spline bevelSpline = new Spline(SplineType.Bezier, conrtolPoints, 0, false);\r
- Curve bevelCurve = new Curve(bevelSpline, bevResol);\r
- bevelObject = new ArrayList<Geometry>(1);\r
- bevelObject.add(new Geometry("", bevelCurve));\r
- } else if(extrude>0.0f) {\r
- Spline bevelSpline = new Spline(SplineType.Linear, new Vector3f[] {\r
- new Vector3f(0, extrude, 0), new Vector3f(0, -extrude, 0)\r
- }, 1, false);\r
- Curve bevelCurve = new Curve(bevelSpline, bevResol);\r
- bevelObject = new ArrayList<Geometry>(1);\r
- bevelObject.add(new Geometry("", bevelCurve));\r
- }\r
- }\r
- \r
- //getting taper object\r
- Curve taperObject = null;\r
- Pointer pTaperObject = (Pointer) curveStructure.getFieldValue("taperobj");\r
- if(bevelObject!=null && !pTaperObject.isNull()) {\r
- Pointer pTaperStructure = (Pointer) pTaperObject.fetchData(dataRepository.getInputStream()).get(0).getFieldValue("data");\r
- Structure taperStructure = pTaperStructure.fetchData(dataRepository.getInputStream()).get(0);\r
- taperObject = this.loadTaperObject(taperStructure, dataRepository);\r
- }\r
- \r
- Vector3f loc = this.getLoc(curveStructure);\r
- //creating the result curves\r
- List<Geometry> result = new ArrayList<Geometry>(nurbs.size());\r
- for(Entry<Number, List<Structure>> nurbEntry : nurbs.entrySet()) {\r
- for(Structure nurb : nurbEntry.getValue()) {\r
- int type = ((Number)nurb.getFieldValue("type")).intValue();\r
- List<Geometry> nurbGeoms = null;\r
- if((type & 0x01)!=0) {//Bezier curve\r
- nurbGeoms = this.loadBezierCurve(loc, nurb, bevelObject, taperObject, dataRepository);\r
- } else if((type & 0x04)!=0) {//NURBS\r
- nurbGeoms = this.loadNurb(loc, nurb, bevelObject, taperObject, dataRepository);\r
- }\r
- if(nurbGeoms!=null) {//setting the name and assigning materials\r
- for(Geometry nurbGeom : nurbGeoms) {\r
- nurbGeom.setMaterial(materials[nurbEntry.getKey().intValue()]);\r
- nurbGeom.setName(name);\r
- result.add(nurbGeom);\r
- }\r
- }\r
- }\r
- }\r
- return result;\r
- }\r
- \r
- /**\r
- * This method loads the bezier curve.\r
- * @param loc\r
- * the translation of the curve\r
- * @param nurb\r
- * the nurb structure\r
- * @param bevelObject\r
- * the bevel object\r
- * @param taperObject\r
- * the taper object\r
- * @param dataRepository\r
- * the data repository\r
- * @return a list of geometries representing the curves\r
- * @throws BlenderFileException\r
- * an exception is thrown when there are problems with the blender file\r
- */\r
- protected List<Geometry> loadBezierCurve(Vector3f loc, Structure nurb, List<Geometry> bevelObject, Curve taperObject, \r
- DataRepository dataRepository) throws BlenderFileException {\r
- Pointer pBezierTriple = (Pointer) nurb.getFieldValue("bezt");\r
- List<Geometry> result = new ArrayList<Geometry>();\r
- if(!pBezierTriple.isNull()) {\r
- boolean smooth = (((Number)nurb.getFlatFieldValue("flag")).intValue() & 0x01) !=0;\r
- int resolution = ((Number)nurb.getFieldValue("resolu")).intValue();\r
- boolean cyclic = (((Number)nurb.getFieldValue("flagu")).intValue() & 0x01) != 0;\r
- \r
- //creating the curve object\r
- BezierCurve bezierCurve = new BezierCurve(0, pBezierTriple.fetchData(dataRepository.getInputStream()), 3);\r
- List<Vector3f> controlPoints = bezierCurve.getControlPoints();\r
- if(cyclic) {\r
- //copy the first three points at the end\r
- for(int i=0;i<3;++i) {\r
- controlPoints.add(controlPoints.get(i));\r
- }\r
- }\r
- //removing the first and last handles\r
- controlPoints.remove(0);\r
- controlPoints.remove(controlPoints.size()-1);\r
-\r
- //creating curve\r
- Spline spline = new Spline(SplineType.Bezier, controlPoints, 0, false);\r
- Curve curve = new Curve(spline, resolution);\r
- if(bevelObject==null) {//creating a normal curve\r
- Geometry curveGeometry = new Geometry(null, curve);\r
- result.add(curveGeometry);\r
- //TODO: use front and back flags; surface excluding algorithm for bezier circles should be added\r
- } else {//creating curve with bevel and taper shape\r
- result = this.applyBevelAndTaper(curve, bevelObject, taperObject, smooth, dataRepository);\r
- }\r
- }\r
- return result;\r
- }\r
- \r
- /**\r
- * This method loads the NURBS curve or surface.\r
- * @param loc\r
- * object's location\r
- * @param nurb\r
- * the NURBS data structure\r
- * @param bevelObject\r
- * the bevel object to be applied\r
- * @param taperObject\r
- * the taper object to be applied\r
- * @param dataRepository\r
- * the data repository\r
- * @return a list of geometries that represents the loaded NURBS curve or surface\r
- * @throws BlenderFileException\r
- * an exception is throw when problems with blender loaded data occurs\r
- */\r
- @SuppressWarnings("unchecked")\r
- protected List<Geometry> loadNurb(Vector3f loc, Structure nurb, List<Geometry> bevelObject, Curve taperObject, \r
- DataRepository dataRepository) throws BlenderFileException {\r
- //loading the knots\r
- List<Float>[] knots = new List[2];\r
- Pointer[] pKnots = new Pointer[] { (Pointer) nurb.getFieldValue("knotsu"), (Pointer) nurb.getFieldValue("knotsv") };\r
- for(int i=0;i<knots.length; ++i) {\r
- if(!pKnots[i].isNull()) {\r
- FileBlockHeader fileBlockHeader = dataRepository.getFileBlock(pKnots[i].getOldMemoryAddress());\r
- BlenderInputStream blenderInputStream = dataRepository.getInputStream();\r
- blenderInputStream.setPosition(fileBlockHeader.getBlockPosition());\r
- int knotsAmount = fileBlockHeader.getCount() * fileBlockHeader.getSize() / 4;\r
- knots[i] = new ArrayList<Float>(knotsAmount);\r
- for(int j=0;j<knotsAmount;++j) {\r
- knots[i].add(Float.valueOf(blenderInputStream.readFloat()));\r
- }\r
- }\r
- }\r
- \r
- //loading the flags and orders (basis functions degrees)\r
- int flagU = ((Number)nurb.getFieldValue("flagu")).intValue();\r
- int flagV = ((Number)nurb.getFieldValue("flagv")).intValue();\r
- int orderU = ((Number)nurb.getFieldValue("orderu")).intValue();\r
- int orderV = ((Number)nurb.getFieldValue("orderv")).intValue();\r
- \r
- //loading control points and their weights\r
- int pntsU = ((Number)nurb.getFieldValue("pntsu")).intValue();\r
- int pntsV = ((Number)nurb.getFieldValue("pntsv")).intValue();\r
- List<Structure> bPoints = ((Pointer)nurb.getFieldValue("bp")).fetchData(dataRepository.getInputStream());\r
- List<List<Vector4f>> controlPoints = new ArrayList<List<Vector4f>>(pntsV);\r
- for(int i=0;i<pntsV;++i) {\r
- List<Vector4f> uControlPoints = new ArrayList<Vector4f>(pntsU);\r
- for(int j=0;j<pntsU;++j) {\r
- DynamicArray<Float> vec = (DynamicArray<Float>)bPoints.get(j + i*pntsU).getFieldValue("vec");\r
- if(fixUpAxis) {\r
- uControlPoints.add(new Vector4f(vec.get(0).floatValue(), vec.get(2).floatValue(), -vec.get(1).floatValue(), vec.get(3).floatValue()));\r
- } else {\r
- uControlPoints.add(new Vector4f(vec.get(0).floatValue(), vec.get(1).floatValue(), vec.get(2).floatValue(), vec.get(3).floatValue()));\r
- }\r
- }\r
- if((flagU & 0x01) != 0) {\r
- for(int k=0;k<orderU - 1;++k) {\r
- uControlPoints.add(uControlPoints.get(k));\r
- }\r
- }\r
- controlPoints.add(uControlPoints);\r
- }\r
- if((flagV & 0x01) != 0) {\r
- for(int k=0;k<orderV - 1;++k) {\r
- controlPoints.add(controlPoints.get(k));\r
- }\r
- }\r
- \r
- int resolu = ((Number)nurb.getFieldValue("resolu")).intValue() + 1;\r
- List<Geometry> result;\r
- if(knots[1]==null) {//creating the curve\r
- Spline nurbSpline = new Spline(controlPoints.get(0), knots[0]);\r
- Curve nurbCurve = new Curve(nurbSpline, resolu);\r
- if(bevelObject!=null) {\r
- result = this.applyBevelAndTaper(nurbCurve, bevelObject, taperObject, true, dataRepository);//TODO: smooth\r
- } else {\r
- result = new ArrayList<Geometry>(1);\r
- Geometry nurbGeometry = new Geometry("", nurbCurve);\r
- result.add(nurbGeometry);\r
- }\r
- } else {//creating the nurb surface\r
- int resolv = ((Number)nurb.getFieldValue("resolv")).intValue() + 1;\r
- Surface nurbSurface = Surface.createNurbsSurface(controlPoints, knots, resolu, resolv, orderU, orderV);\r
- Geometry nurbGeometry = new Geometry("", nurbSurface);\r
- result = new ArrayList<Geometry>(1);\r
- result.add(nurbGeometry);\r
- }\r
- return result;\r
- }\r
- \r
- /**\r
- * This method returns the taper scale that should be applied to the object.\r
- * @param taperPoints\r
- * the taper points\r
- * @param taperLength\r
- * the taper curve length\r
- * @param percent\r
- * the percent of way along the whole taper curve\r
- * @param store\r
- * the vector where the result will be stored\r
- */\r
- protected float getTaperScale(float[] taperPoints, float taperLength, float percent) {\r
- float length = taperLength * percent;\r
- float currentLength = 0;\r
- Vector3f p = new Vector3f();\r
- int i;\r
- for(i=0;i<taperPoints.length-6 && currentLength < length; i += 3) {\r
- p.set(taperPoints[i], taperPoints[i+1], taperPoints[i+2]);\r
- p.subtractLocal(taperPoints[i+3], taperPoints[i+4], taperPoints[i+5]);\r
- currentLength += p.length();\r
- }\r
- currentLength -= p.length();\r
- float leftLength = length - currentLength;\r
- float percentOnSegment = p.length()==0 ? 0 : leftLength / p.length();\r
- Vector3f store = FastMath.interpolateLinear(percentOnSegment, \r
- new Vector3f(taperPoints[i], taperPoints[i+1], taperPoints[i+2]), \r
- new Vector3f(taperPoints[i+3], taperPoints[i+4], taperPoints[i+5]));\r
- return store.y;\r
- }\r
- \r
- /**\r
- * This method applies bevel and taper objects to the curve.\r
- * @param curve\r
- * the curve we apply the objects to\r
- * @param bevelObject\r
- * the bevel object\r
- * @param taperObject\r
- * the taper object\r
- * @param smooth\r
- * the smooth flag\r
- * @param dataRepository\r
- * the data repository\r
- * @return a list of geometries representing the beveled and/or tapered curve\r
- */\r
- protected List<Geometry> applyBevelAndTaper(Curve curve, List<Geometry> bevelObject, Curve taperObject, \r
- boolean smooth, DataRepository dataRepository) {\r
- float[] curvePoints = BufferUtils.getFloatArray(curve.getFloatBuffer(Type.Position));\r
- MeshHelper meshHelper = dataRepository.getHelper(MeshHelper.class);\r
- float curveLength = curve.getLength();\r
- //TODO: use the smooth var\r
- \r
- //taper data\r
- float[] taperPoints = null;\r
- float taperLength = 0;\r
- if(taperObject!=null) {\r
- taperPoints = BufferUtils.getFloatArray(taperObject.getFloatBuffer(Type.Position));\r
- taperLength = taperObject.getLength();\r
- }\r
- \r
- //several objects can be allocated only once\r
- Vector3f p = new Vector3f();\r
- Vector3f z = new Vector3f(0,0,1);\r
- Vector3f negativeY = new Vector3f(0, -1, 0);\r
- Matrix4f m = new Matrix4f();\r
- float lengthAlongCurve = 0, taperScale = 1.0f;\r
- Quaternion planeRotation = new Quaternion();\r
- Quaternion zRotation = new Quaternion();\r
- float[] temp = new float[] {0,0,0,1};\r
- Map<Vector3f, Vector3f> normalMap = new HashMap<Vector3f, Vector3f>();//normalMap merges normals of faces that will be rendered smooth\r
- \r
- FloatBuffer[] vertexBuffers = new FloatBuffer[bevelObject.size()];\r
- FloatBuffer[] normalBuffers = new FloatBuffer[bevelObject.size()];\r
- IntBuffer[] indexBuffers = new IntBuffer[bevelObject.size()];\r
- for(int geomIndex = 0;geomIndex<bevelObject.size();++geomIndex) {\r
- Mesh mesh = bevelObject.get(geomIndex).getMesh();\r
- FloatBuffer positions = mesh.getFloatBuffer(Type.Position);\r
- float[] vertices = BufferUtils.getFloatArray(positions);\r
- \r
- for(int i=0;i<curvePoints.length;i+=3) {\r
- p.set(curvePoints[i], curvePoints[i+1], curvePoints[i+2]);\r
- Vector3f v;\r
- if(i==0) {\r
- v = new Vector3f(curvePoints[3] - p.x, curvePoints[4] - p.y, curvePoints[5] - p.z);\r
- } else if(i+3>=curvePoints.length) {\r
- v = new Vector3f(p.x - curvePoints[i-3], p.y - curvePoints[i-2], p.z - curvePoints[i-1]);\r
- lengthAlongCurve += v.length();\r
- } else {\r
- v = new Vector3f(curvePoints[i+3] - curvePoints[i-3],\r
- curvePoints[i+4] - curvePoints[i-2],\r
- curvePoints[i+5] - curvePoints[i-1]);\r
- lengthAlongCurve += new Vector3f(curvePoints[i+3] - p.x, curvePoints[i+4] - p.y, curvePoints[i+5] - p.z).length();\r
- }\r
- v.normalizeLocal();\r
- \r
- float angle = FastMath.acos(v.dot(z));\r
- v.crossLocal(z).normalizeLocal();//v is the rotation axis now\r
- planeRotation.fromAngleAxis(angle, v);\r
- \r
- Vector3f zAxisRotationVector = negativeY.cross(v).normalizeLocal();\r
- float zAxisRotationAngle = FastMath.acos(negativeY.dot(v));\r
- zRotation.fromAngleAxis(zAxisRotationAngle, zAxisRotationVector);\r
- \r
- //point transformation matrix\r
- if(taperPoints!=null) {\r
- taperScale = this.getTaperScale(taperPoints, taperLength, lengthAlongCurve / curveLength);\r
- }\r
- m.set(Matrix4f.IDENTITY);\r
- m.setRotationQuaternion(planeRotation.multLocal(zRotation));\r
- m.setTranslation(p);\r
- \r
- //these vertices need to be thrown on XY plane\r
- //and moved to the origin of [p1.x, p1.y] on the plane\r
- Vector3f[] verts = new Vector3f[vertices.length/3];\r
- for(int j=0;j<verts.length;++j) {\r
- temp[0] = vertices[j*3] * taperScale;\r
- temp[1] = vertices[j*3+1] * taperScale;\r
- temp[2] = 0;\r
- m.mult(temp);//the result is stored in the array\r
- if(fixUpAxis) {\r
- verts[j] = new Vector3f(temp[0], temp[1], temp[2]);\r
- } else {\r
- verts[j] = new Vector3f(temp[0], temp[2], -temp[1]);\r
- }\r
- }\r
- if(vertexBuffers[geomIndex]==null) {\r
- vertexBuffers[geomIndex] = BufferUtils.createFloatBuffer(verts.length * curvePoints.length);\r
- }\r
- FloatBuffer buffer = BufferUtils.createFloatBuffer(verts);\r
- vertexBuffers[geomIndex].put(buffer);\r
- \r
- //adding indexes\r
- IntBuffer indexBuffer = indexBuffers[geomIndex];\r
- if(indexBuffer==null) {\r
- //the amount of faces in the final mesh is the amount of edges in the bevel curve\r
- //(which is less by 1 than its number of vertices)\r
- //multiplied by 2 (because each edge has two faces assigned on both sides)\r
- //and multiplied by the amount of bevel curve repeats which is equal to the amount of vertices on the target curve\r
- //finally we need to subtract the bevel edges amount 2 times because the border edges have only one face attached\r
- //and at last multiply everything by 3 because each face needs 3 indexes to be described\r
- int bevelCurveEdgesAmount = verts.length-1;\r
- indexBuffer = BufferUtils.createIntBuffer(((bevelCurveEdgesAmount << 1) * curvePoints.length - bevelCurveEdgesAmount << 1) * 3);\r
- indexBuffers[geomIndex] = indexBuffer;\r
- }\r
- int pointOffset = i/3 * verts.length;\r
- if(i+3<curvePoints.length) {\r
- for(int index=0;index<verts.length-1;++index) {\r
- indexBuffer.put(index + pointOffset);\r
- indexBuffer.put(index + pointOffset + 1);\r
- indexBuffer.put(verts.length + index + pointOffset);\r
- indexBuffer.put(verts.length + index + pointOffset);\r
- indexBuffer.put(index + pointOffset + 1);\r
- indexBuffer.put(verts.length + index + pointOffset + 1);\r
- }\r
- }\r
- }\r
- }\r
- \r
- //calculating the normals\r
- for(int geomIndex = 0;geomIndex<bevelObject.size();++geomIndex) {\r
- Vector3f[] allVerts = BufferUtils.getVector3Array(vertexBuffers[geomIndex]);\r
- int[] allIndices = BufferUtils.getIntArray(indexBuffers[geomIndex]);\r
- for(int i=0;i<allIndices.length-3;i+=3) {\r
- Vector3f n = FastMath.computeNormal(allVerts[allIndices[i]], allVerts[allIndices[i + 1]], allVerts[allIndices[i + 2]]);\r
- meshHelper.addNormal(n, normalMap, smooth, allVerts[allIndices[i]], allVerts[allIndices[i + 1]], allVerts[allIndices[i + 2]]);\r
- }\r
- if(normalBuffers[geomIndex]==null) {\r
- normalBuffers[geomIndex] = BufferUtils.createFloatBuffer(allVerts.length * 3);\r
- }\r
- for(Vector3f v : allVerts) {\r
- Vector3f n = normalMap.get(v);\r
- normalBuffers[geomIndex].put(n.x);\r
- normalBuffers[geomIndex].put(n.y);\r
- normalBuffers[geomIndex].put(n.z);\r
- }\r
- }\r
-\r
- List<Geometry> result = new ArrayList<Geometry>(vertexBuffers.length);\r
- for(int i=0;i<vertexBuffers.length;++i) {\r
- Mesh mesh = new Mesh();\r
- mesh.setBuffer(Type.Position, 3, vertexBuffers[i]);\r
- mesh.setBuffer(Type.Index, 3, indexBuffers[i]);\r
- mesh.setBuffer(Type.Normal, 3, normalBuffers[i]);\r
- Geometry g = new Geometry("g" + i, mesh);\r
- g.updateModelBound();\r
- result.add(g);\r
- }\r
- \r
- return result;\r
- }\r
- \r
- /**\r
- * This method loads the taper object.\r
- * @param taperStructure\r
- * the taper structure\r
- * @param dataRepository\r
- * the data repository\r
- * @return the taper object\r
- * @throws BlenderFileException\r
- */\r
- protected Curve loadTaperObject(Structure taperStructure, DataRepository dataRepository) throws BlenderFileException {\r
- //reading nurbs\r
- List<Structure> nurbStructures = ((Structure)taperStructure.getFieldValue("nurb")).evaluateListBase(dataRepository);\r
- for(Structure nurb : nurbStructures) {\r
- Pointer pBezierTriple = (Pointer) nurb.getFieldValue("bezt");\r
- if(!pBezierTriple.isNull()) {\r
- //creating the curve object\r
- BezierCurve bezierCurve = new BezierCurve(0, pBezierTriple.fetchData(dataRepository.getInputStream()), 3);\r
- List<Vector3f> controlPoints = bezierCurve.getControlPoints();\r
- //removing the first and last handles\r
- controlPoints.remove(0);\r
- controlPoints.remove(controlPoints.size()-1);\r
- \r
- //return the first taper curve that has more than 3 control points\r
- if(controlPoints.size()>3) {\r
- Spline spline = new Spline(SplineType.Bezier, controlPoints, 0, false);\r
- int resolution = ((Number)taperStructure.getFieldValue("resolu")).intValue();\r
- return new Curve(spline, resolution);\r
- }\r
- }\r
- }\r
- return null;\r
- }\r
- \r
- /**\r
- * This method returns the translation of the curve. The UP axis is taken into account here.\r
- * @param curveStructure\r
- * the curve structure\r
- * @return curve translation\r
- */\r
- @SuppressWarnings("unchecked")\r
- protected Vector3f getLoc(Structure curveStructure) {\r
- DynamicArray<Number> locArray = (DynamicArray<Number>)curveStructure.getFieldValue("loc");\r
- if(fixUpAxis) {\r
- return new Vector3f(locArray.get(0).floatValue(), locArray.get(1).floatValue(), -locArray.get(2).floatValue());\r
- } else {\r
- return new Vector3f(locArray.get(0).floatValue(), locArray.get(2).floatValue(), locArray.get(1).floatValue());\r
- }\r
- }\r
+\r
+ private static final Logger LOGGER = Logger.getLogger(CurvesHelper.class.getName());\r
+ /** This variable indicates if the Y asxis is the UP axis or not. */\r
+ protected boolean fixUpAxis;\r
+ /** Minimum basis U function degree for NURBS curves and surfaces. */\r
+ protected int minimumBasisUFunctionDegree = 4;\r
+ /** Minimum basis V function degree for NURBS curves and surfaces. */\r
+ protected int minimumBasisVFunctionDegree = 4;\r
+\r
+ /**\r
+ * This constructor parses the given blender version and stores the result. Some functionalities may differ in\r
+ * different blender versions.\r
+ * @param blenderVersion\r
+ * the version read from the blend file\r
+ */\r
+ public CurvesHelper(String blenderVersion) {\r
+ super(blenderVersion);\r
+ }\r
+\r
+ /**\r
+ * This method sets the Y is UP axis. By default the UP axis is Z (just like in blender).\r
+ * @param fixUpAxis\r
+ * a variable that indicates if the Y asxis is the UP axis or not\r
+ */\r
+ public void setyIsUpAxis(boolean fixUpAxis) {\r
+ this.fixUpAxis = fixUpAxis;\r
+ }\r
+\r
+ /**\r
+ * This method converts given curve structure into a list of geometries representing the curve. The list is used here because on object\r
+ * can have several separate curves.\r
+ * @param curveStructure\r
+ * the curve structure\r
+ * @param dataRepository\r
+ * the data repository\r
+ * @return a list of geometries repreenting a single curve object\r
+ * @throws BlenderFileException\r
+ */\r
+ public List<Geometry> toCurve(Structure curveStructure, DataRepository dataRepository) throws BlenderFileException {\r
+ String name = curveStructure.getName();\r
+ int flag = ((Number) curveStructure.getFieldValue("flag")).intValue();\r
+ boolean is3D = (flag & 0x01) != 0;\r
+ boolean isFront = (flag & 0x02) != 0 && !is3D;\r
+ boolean isBack = (flag & 0x04) != 0 && !is3D;\r
+ if (isFront) {\r
+ LOGGER.warning("No front face in curve implemented yet!");//TODO: implement front face\r
+ }\r
+ if (isBack) {\r
+ LOGGER.warning("No back face in curve implemented yet!");//TODO: implement back face\r
+ }\r
+\r
+ //reading nurbs (and sorting them by material)\r
+ List<Structure> nurbStructures = ((Structure) curveStructure.getFieldValue("nurb")).evaluateListBase(dataRepository);\r
+ Map<Number, List<Structure>> nurbs = new HashMap<Number, List<Structure>>();\r
+ for (Structure nurb : nurbStructures) {\r
+ Number matNumber = (Number) nurb.getFieldValue("mat_nr");\r
+ List<Structure> nurbList = nurbs.get(matNumber);\r
+ if (nurbList == null) {\r
+ nurbList = new ArrayList<Structure>();\r
+ nurbs.put(matNumber, nurbList);\r
+ }\r
+ nurbList.add(nurb);\r
+ }\r
+\r
+ //getting materials\r
+ MaterialHelper materialHelper = dataRepository.getHelper(MaterialHelper.class);\r
+ Material[] materials = materialHelper.getMaterials(curveStructure, dataRepository);\r
+ if (materials == null) {\r
+ materials = new Material[]{dataRepository.getDefaultMaterial().clone()};\r
+ }\r
+ for (Material material : materials) {\r
+ material.getAdditionalRenderState().setFaceCullMode(FaceCullMode.Off);\r
+ }\r
+\r
+ //getting or creating bevel object\r
+ List<Geometry> bevelObject = null;\r
+ Pointer pBevelObject = (Pointer) curveStructure.getFieldValue("bevobj");\r
+ if (!pBevelObject.isNull()) {\r
+ Pointer pBevelStructure = (Pointer) pBevelObject.fetchData(dataRepository.getInputStream()).get(0).getFieldValue("data");\r
+ Structure bevelStructure = pBevelStructure.fetchData(dataRepository.getInputStream()).get(0);\r
+ bevelObject = this.toCurve(bevelStructure, dataRepository);\r
+ } else {\r
+ int bevResol = ((Number) curveStructure.getFieldValue("bevresol")).intValue();\r
+ float extrude = ((Number) curveStructure.getFieldValue("ext1")).floatValue();\r
+ float bevelDepth = ((Number) curveStructure.getFieldValue("ext2")).floatValue();\r
+ if (bevelDepth > 0.0f) {\r
+ float handlerLength = bevelDepth / 2.0f;\r
+\r
+ List<Vector3f> conrtolPoints = new ArrayList<Vector3f>(extrude > 0.0f ? 19 : 13);\r
+ conrtolPoints.add(new Vector3f(-bevelDepth, extrude, 0));\r
+ conrtolPoints.add(new Vector3f(-bevelDepth, handlerLength + extrude, 0));\r
+\r
+ conrtolPoints.add(new Vector3f(-handlerLength, bevelDepth + extrude, 0));\r
+ conrtolPoints.add(new Vector3f(0, bevelDepth + extrude, 0));\r
+ conrtolPoints.add(new Vector3f(handlerLength, bevelDepth + extrude, 0));\r
+\r
+ conrtolPoints.add(new Vector3f(bevelDepth, extrude + handlerLength, 0));\r
+ conrtolPoints.add(new Vector3f(bevelDepth, extrude, 0));\r
+ conrtolPoints.add(new Vector3f(bevelDepth, extrude - handlerLength, 0));\r
+\r
+ if (extrude > 0.0f) {\r
+ conrtolPoints.add(new Vector3f(bevelDepth, -extrude + handlerLength, 0));\r
+ conrtolPoints.add(new Vector3f(bevelDepth, -extrude, 0));\r
+ conrtolPoints.add(new Vector3f(bevelDepth, -extrude - handlerLength, 0));\r
+ }\r
+\r
+ conrtolPoints.add(new Vector3f(handlerLength, -bevelDepth - extrude, 0));\r
+ conrtolPoints.add(new Vector3f(0, -bevelDepth - extrude, 0));\r
+ conrtolPoints.add(new Vector3f(-handlerLength, -bevelDepth - extrude, 0));\r
+\r
+ conrtolPoints.add(new Vector3f(-bevelDepth, -handlerLength - extrude, 0));\r
+ conrtolPoints.add(new Vector3f(-bevelDepth, -extrude, 0));\r
+\r
+ if (extrude > 0.0f) {\r
+ conrtolPoints.add(new Vector3f(-bevelDepth, handlerLength - extrude, 0));\r
+\r
+ conrtolPoints.add(new Vector3f(-bevelDepth, -handlerLength + extrude, 0));\r
+ conrtolPoints.add(new Vector3f(-bevelDepth, extrude, 0));\r
+ }\r
+\r
+ Spline bevelSpline = new Spline(SplineType.Bezier, conrtolPoints, 0, false);\r
+ Curve bevelCurve = new Curve(bevelSpline, bevResol);\r
+ bevelObject = new ArrayList<Geometry>(1);\r
+ bevelObject.add(new Geometry("", bevelCurve));\r
+ } else if (extrude > 0.0f) {\r
+ Spline bevelSpline = new Spline(SplineType.Linear, new Vector3f[]{\r
+ new Vector3f(0, extrude, 0), new Vector3f(0, -extrude, 0)\r
+ }, 1, false);\r
+ Curve bevelCurve = new Curve(bevelSpline, bevResol);\r
+ bevelObject = new ArrayList<Geometry>(1);\r
+ bevelObject.add(new Geometry("", bevelCurve));\r
+ }\r
+ }\r
+\r
+ //getting taper object\r
+ Curve taperObject = null;\r
+ Pointer pTaperObject = (Pointer) curveStructure.getFieldValue("taperobj");\r
+ if (bevelObject != null && !pTaperObject.isNull()) {\r
+ Pointer pTaperStructure = (Pointer) pTaperObject.fetchData(dataRepository.getInputStream()).get(0).getFieldValue("data");\r
+ Structure taperStructure = pTaperStructure.fetchData(dataRepository.getInputStream()).get(0);\r
+ taperObject = this.loadTaperObject(taperStructure, dataRepository);\r
+ }\r
+\r
+ Vector3f loc = this.getLoc(curveStructure);\r
+ //creating the result curves\r
+ List<Geometry> result = new ArrayList<Geometry>(nurbs.size());\r
+ for (Entry<Number, List<Structure>> nurbEntry : nurbs.entrySet()) {\r
+ for (Structure nurb : nurbEntry.getValue()) {\r
+ int type = ((Number) nurb.getFieldValue("type")).intValue();\r
+ List<Geometry> nurbGeoms = null;\r
+ if ((type & 0x01) != 0) {//Bezier curve\r
+ nurbGeoms = this.loadBezierCurve(loc, nurb, bevelObject, taperObject, dataRepository);\r
+ } else if ((type & 0x04) != 0) {//NURBS\r
+ nurbGeoms = this.loadNurb(loc, nurb, bevelObject, taperObject, dataRepository);\r
+ }\r
+ if (nurbGeoms != null) {//setting the name and assigning materials\r
+ for (Geometry nurbGeom : nurbGeoms) {\r
+ nurbGeom.setMaterial(materials[nurbEntry.getKey().intValue()]);\r
+ nurbGeom.setName(name);\r
+ result.add(nurbGeom);\r
+ }\r
+ }\r
+ }\r
+ }\r
+ return result;\r
+ }\r
+\r
+ /**\r
+ * This method loads the bezier curve.\r
+ * @param loc\r
+ * the translation of the curve\r
+ * @param nurb\r
+ * the nurb structure\r
+ * @param bevelObject\r
+ * the bevel object\r
+ * @param taperObject\r
+ * the taper object\r
+ * @param dataRepository\r
+ * the data repository\r
+ * @return a list of geometries representing the curves\r
+ * @throws BlenderFileException\r
+ * an exception is thrown when there are problems with the blender file\r
+ */\r
+ protected List<Geometry> loadBezierCurve(Vector3f loc, Structure nurb, List<Geometry> bevelObject, Curve taperObject,\r
+ DataRepository dataRepository) throws BlenderFileException {\r
+ Pointer pBezierTriple = (Pointer) nurb.getFieldValue("bezt");\r
+ List<Geometry> result = new ArrayList<Geometry>();\r
+ if (!pBezierTriple.isNull()) {\r
+ boolean smooth = (((Number) nurb.getFlatFieldValue("flag")).intValue() & 0x01) != 0;\r
+ int resolution = ((Number) nurb.getFieldValue("resolu")).intValue();\r
+ boolean cyclic = (((Number) nurb.getFieldValue("flagu")).intValue() & 0x01) != 0;\r
+\r
+ //creating the curve object\r
+ BezierCurve bezierCurve = new BezierCurve(0, pBezierTriple.fetchData(dataRepository.getInputStream()), 3);\r
+ List<Vector3f> controlPoints = bezierCurve.getControlPoints();\r
+ if (cyclic) {\r
+ //copy the first three points at the end\r
+ for (int i = 0; i < 3; ++i) {\r
+ controlPoints.add(controlPoints.get(i));\r
+ }\r
+ }\r
+ //removing the first and last handles\r
+ controlPoints.remove(0);\r
+ controlPoints.remove(controlPoints.size() - 1);\r
+\r
+ //creating curve\r
+ Spline spline = new Spline(SplineType.Bezier, controlPoints, 0, false);\r
+ Curve curve = new Curve(spline, resolution);\r
+ if (bevelObject == null) {//creating a normal curve\r
+ Geometry curveGeometry = new Geometry(null, curve);\r
+ result.add(curveGeometry);\r
+ //TODO: use front and back flags; surface excluding algorithm for bezier circles should be added\r
+ } else {//creating curve with bevel and taper shape\r
+ result = this.applyBevelAndTaper(curve, bevelObject, taperObject, smooth, dataRepository);\r
+ }\r
+ }\r
+ return result;\r
+ }\r
+\r
+ /**\r
+ * This method loads the NURBS curve or surface.\r
+ * @param loc\r
+ * object's location\r
+ * @param nurb\r
+ * the NURBS data structure\r
+ * @param bevelObject\r
+ * the bevel object to be applied\r
+ * @param taperObject\r
+ * the taper object to be applied\r
+ * @param dataRepository\r
+ * the data repository\r
+ * @return a list of geometries that represents the loaded NURBS curve or surface\r
+ * @throws BlenderFileException\r
+ * an exception is throw when problems with blender loaded data occurs\r
+ */\r
+ @SuppressWarnings("unchecked")\r
+ protected List<Geometry> loadNurb(Vector3f loc, Structure nurb, List<Geometry> bevelObject, Curve taperObject,\r
+ DataRepository dataRepository) throws BlenderFileException {\r
+ //loading the knots\r
+ List<Float>[] knots = new List[2];\r
+ Pointer[] pKnots = new Pointer[]{(Pointer) nurb.getFieldValue("knotsu"), (Pointer) nurb.getFieldValue("knotsv")};\r
+ for (int i = 0; i < knots.length; ++i) {\r
+ if (!pKnots[i].isNull()) {\r
+ FileBlockHeader fileBlockHeader = dataRepository.getFileBlock(pKnots[i].getOldMemoryAddress());\r
+ BlenderInputStream blenderInputStream = dataRepository.getInputStream();\r
+ blenderInputStream.setPosition(fileBlockHeader.getBlockPosition());\r
+ int knotsAmount = fileBlockHeader.getCount() * fileBlockHeader.getSize() / 4;\r
+ knots[i] = new ArrayList<Float>(knotsAmount);\r
+ for (int j = 0; j < knotsAmount; ++j) {\r
+ knots[i].add(Float.valueOf(blenderInputStream.readFloat()));\r
+ }\r
+ }\r
+ }\r
+\r
+ //loading the flags and orders (basis functions degrees)\r
+ int flagU = ((Number) nurb.getFieldValue("flagu")).intValue();\r
+ int flagV = ((Number) nurb.getFieldValue("flagv")).intValue();\r
+ int orderU = ((Number) nurb.getFieldValue("orderu")).intValue();\r
+ int orderV = ((Number) nurb.getFieldValue("orderv")).intValue();\r
+\r
+ //loading control points and their weights\r
+ int pntsU = ((Number) nurb.getFieldValue("pntsu")).intValue();\r
+ int pntsV = ((Number) nurb.getFieldValue("pntsv")).intValue();\r
+ List<Structure> bPoints = ((Pointer) nurb.getFieldValue("bp")).fetchData(dataRepository.getInputStream());\r
+ List<List<Vector4f>> controlPoints = new ArrayList<List<Vector4f>>(pntsV);\r
+ for (int i = 0; i < pntsV; ++i) {\r
+ List<Vector4f> uControlPoints = new ArrayList<Vector4f>(pntsU);\r
+ for (int j = 0; j < pntsU; ++j) {\r
+ DynamicArray<Float> vec = (DynamicArray<Float>) bPoints.get(j + i * pntsU).getFieldValue("vec");\r
+ if (fixUpAxis) {\r
+ uControlPoints.add(new Vector4f(vec.get(0).floatValue(), vec.get(2).floatValue(), -vec.get(1).floatValue(), vec.get(3).floatValue()));\r
+ } else {\r
+ uControlPoints.add(new Vector4f(vec.get(0).floatValue(), vec.get(1).floatValue(), vec.get(2).floatValue(), vec.get(3).floatValue()));\r
+ }\r
+ }\r
+ if ((flagU & 0x01) != 0) {\r
+ for (int k = 0; k < orderU - 1; ++k) {\r
+ uControlPoints.add(uControlPoints.get(k));\r
+ }\r
+ }\r
+ controlPoints.add(uControlPoints);\r
+ }\r
+ if ((flagV & 0x01) != 0) {\r
+ for (int k = 0; k < orderV - 1; ++k) {\r
+ controlPoints.add(controlPoints.get(k));\r
+ }\r
+ }\r
+\r
+ int resolu = ((Number) nurb.getFieldValue("resolu")).intValue() + 1;\r
+ List<Geometry> result;\r
+ if (knots[1] == null) {//creating the curve\r
+ Spline nurbSpline = new Spline(controlPoints.get(0), knots[0]);\r
+ Curve nurbCurve = new Curve(nurbSpline, resolu);\r
+ if (bevelObject != null) {\r
+ result = this.applyBevelAndTaper(nurbCurve, bevelObject, taperObject, true, dataRepository);//TODO: smooth\r
+ } else {\r
+ result = new ArrayList<Geometry>(1);\r
+ Geometry nurbGeometry = new Geometry("", nurbCurve);\r
+ result.add(nurbGeometry);\r
+ }\r
+ } else {//creating the nurb surface\r
+ int resolv = ((Number) nurb.getFieldValue("resolv")).intValue() + 1;\r
+ Surface nurbSurface = Surface.createNurbsSurface(controlPoints, knots, resolu, resolv, orderU, orderV);\r
+ Geometry nurbGeometry = new Geometry("", nurbSurface);\r
+ result = new ArrayList<Geometry>(1);\r
+ result.add(nurbGeometry);\r
+ }\r
+ return result;\r
+ }\r
+\r
+ /**\r
+ * This method returns the taper scale that should be applied to the object.\r
+ * @param taperPoints\r
+ * the taper points\r
+ * @param taperLength\r
+ * the taper curve length\r
+ * @param percent\r
+ * the percent of way along the whole taper curve\r
+ * @param store\r
+ * the vector where the result will be stored\r
+ */\r
+ protected float getTaperScale(float[] taperPoints, float taperLength, float percent) {\r
+ float length = taperLength * percent;\r
+ float currentLength = 0;\r
+ Vector3f p = new Vector3f();\r
+ int i;\r
+ for (i = 0; i < taperPoints.length - 6 && currentLength < length; i += 3) {\r
+ p.set(taperPoints[i], taperPoints[i + 1], taperPoints[i + 2]);\r
+ p.subtractLocal(taperPoints[i + 3], taperPoints[i + 4], taperPoints[i + 5]);\r
+ currentLength += p.length();\r
+ }\r
+ currentLength -= p.length();\r
+ float leftLength = length - currentLength;\r
+ float percentOnSegment = p.length() == 0 ? 0 : leftLength / p.length();\r
+ Vector3f store = FastMath.interpolateLinear(percentOnSegment,\r
+ new Vector3f(taperPoints[i], taperPoints[i + 1], taperPoints[i + 2]),\r
+ new Vector3f(taperPoints[i + 3], taperPoints[i + 4], taperPoints[i + 5]));\r
+ return store.y;\r
+ }\r
+\r
+ /**\r
+ * This method applies bevel and taper objects to the curve.\r
+ * @param curve\r
+ * the curve we apply the objects to\r
+ * @param bevelObject\r
+ * the bevel object\r
+ * @param taperObject\r
+ * the taper object\r
+ * @param smooth\r
+ * the smooth flag\r
+ * @param dataRepository\r
+ * the data repository\r
+ * @return a list of geometries representing the beveled and/or tapered curve\r
+ */\r
+ protected List<Geometry> applyBevelAndTaper(Curve curve, List<Geometry> bevelObject, Curve taperObject,\r
+ boolean smooth, DataRepository dataRepository) {\r
+ float[] curvePoints = BufferUtils.getFloatArray(curve.getFloatBuffer(Type.Position));\r
+ MeshHelper meshHelper = dataRepository.getHelper(MeshHelper.class);\r
+ float curveLength = curve.getLength();\r
+ //TODO: use the smooth var\r
+\r
+ //taper data\r
+ float[] taperPoints = null;\r
+ float taperLength = 0;\r
+ if (taperObject != null) {\r
+ taperPoints = BufferUtils.getFloatArray(taperObject.getFloatBuffer(Type.Position));\r
+ taperLength = taperObject.getLength();\r
+ }\r
+\r
+ //several objects can be allocated only once\r
+ Vector3f p = new Vector3f();\r
+ Vector3f z = new Vector3f(0, 0, 1);\r
+ Vector3f negativeY = new Vector3f(0, -1, 0);\r
+ Matrix4f m = new Matrix4f();\r
+ float lengthAlongCurve = 0, taperScale = 1.0f;\r
+ Quaternion planeRotation = new Quaternion();\r
+ Quaternion zRotation = new Quaternion();\r
+ float[] temp = new float[]{0, 0, 0, 1};\r
+ Map<Vector3f, Vector3f> normalMap = new HashMap<Vector3f, Vector3f>();//normalMap merges normals of faces that will be rendered smooth\r
+\r
+ FloatBuffer[] vertexBuffers = new FloatBuffer[bevelObject.size()];\r
+ FloatBuffer[] normalBuffers = new FloatBuffer[bevelObject.size()];\r
+ IntBuffer[] indexBuffers = new IntBuffer[bevelObject.size()];\r
+ for (int geomIndex = 0; geomIndex < bevelObject.size(); ++geomIndex) {\r
+ Mesh mesh = bevelObject.get(geomIndex).getMesh();\r
+ FloatBuffer positions = mesh.getFloatBuffer(Type.Position);\r
+ float[] vertices = BufferUtils.getFloatArray(positions);\r
+\r
+ for (int i = 0; i < curvePoints.length; i += 3) {\r
+ p.set(curvePoints[i], curvePoints[i + 1], curvePoints[i + 2]);\r
+ Vector3f v;\r
+ if (i == 0) {\r
+ v = new Vector3f(curvePoints[3] - p.x, curvePoints[4] - p.y, curvePoints[5] - p.z);\r
+ } else if (i + 3 >= curvePoints.length) {\r
+ v = new Vector3f(p.x - curvePoints[i - 3], p.y - curvePoints[i - 2], p.z - curvePoints[i - 1]);\r
+ lengthAlongCurve += v.length();\r
+ } else {\r
+ v = new Vector3f(curvePoints[i + 3] - curvePoints[i - 3],\r
+ curvePoints[i + 4] - curvePoints[i - 2],\r
+ curvePoints[i + 5] - curvePoints[i - 1]);\r
+ lengthAlongCurve += new Vector3f(curvePoints[i + 3] - p.x, curvePoints[i + 4] - p.y, curvePoints[i + 5] - p.z).length();\r
+ }\r
+ v.normalizeLocal();\r
+\r
+ float angle = FastMath.acos(v.dot(z));\r
+ v.crossLocal(z).normalizeLocal();//v is the rotation axis now\r
+ planeRotation.fromAngleAxis(angle, v);\r
+\r
+ Vector3f zAxisRotationVector = negativeY.cross(v).normalizeLocal();\r
+ float zAxisRotationAngle = FastMath.acos(negativeY.dot(v));\r
+ zRotation.fromAngleAxis(zAxisRotationAngle, zAxisRotationVector);\r
+\r
+ //point transformation matrix\r
+ if (taperPoints != null) {\r
+ taperScale = this.getTaperScale(taperPoints, taperLength, lengthAlongCurve / curveLength);\r
+ }\r
+ m.set(Matrix4f.IDENTITY);\r
+ m.setRotationQuaternion(planeRotation.multLocal(zRotation));\r
+ m.setTranslation(p);\r
+\r
+ //these vertices need to be thrown on XY plane\r
+ //and moved to the origin of [p1.x, p1.y] on the plane\r
+ Vector3f[] verts = new Vector3f[vertices.length / 3];\r
+ for (int j = 0; j < verts.length; ++j) {\r
+ temp[0] = vertices[j * 3] * taperScale;\r
+ temp[1] = vertices[j * 3 + 1] * taperScale;\r
+ temp[2] = 0;\r
+ m.mult(temp);//the result is stored in the array\r
+ if (fixUpAxis) {\r
+ verts[j] = new Vector3f(temp[0], temp[1], temp[2]);\r
+ } else {\r
+ verts[j] = new Vector3f(temp[0], temp[2], -temp[1]);\r
+ }\r
+ }\r
+ if (vertexBuffers[geomIndex] == null) {\r
+ vertexBuffers[geomIndex] = BufferUtils.createFloatBuffer(verts.length * curvePoints.length);\r
+ }\r
+ FloatBuffer buffer = BufferUtils.createFloatBuffer(verts);\r
+ vertexBuffers[geomIndex].put(buffer);\r
+\r
+ //adding indexes\r
+ IntBuffer indexBuffer = indexBuffers[geomIndex];\r
+ if (indexBuffer == null) {\r
+ //the amount of faces in the final mesh is the amount of edges in the bevel curve\r
+ //(which is less by 1 than its number of vertices)\r
+ //multiplied by 2 (because each edge has two faces assigned on both sides)\r
+ //and multiplied by the amount of bevel curve repeats which is equal to the amount of vertices on the target curve\r
+ //finally we need to subtract the bevel edges amount 2 times because the border edges have only one face attached\r
+ //and at last multiply everything by 3 because each face needs 3 indexes to be described\r
+ int bevelCurveEdgesAmount = verts.length - 1;\r
+ indexBuffer = BufferUtils.createIntBuffer(((bevelCurveEdgesAmount << 1) * curvePoints.length - bevelCurveEdgesAmount << 1) * 3);\r
+ indexBuffers[geomIndex] = indexBuffer;\r
+ }\r
+ int pointOffset = i / 3 * verts.length;\r
+ if (i + 3 < curvePoints.length) {\r
+ for (int index = 0; index < verts.length - 1; ++index) {\r
+ indexBuffer.put(index + pointOffset);\r
+ indexBuffer.put(index + pointOffset + 1);\r
+ indexBuffer.put(verts.length + index + pointOffset);\r
+ indexBuffer.put(verts.length + index + pointOffset);\r
+ indexBuffer.put(index + pointOffset + 1);\r
+ indexBuffer.put(verts.length + index + pointOffset + 1);\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ //calculating the normals\r
+ for (int geomIndex = 0; geomIndex < bevelObject.size(); ++geomIndex) {\r
+ Vector3f[] allVerts = BufferUtils.getVector3Array(vertexBuffers[geomIndex]);\r
+ int[] allIndices = BufferUtils.getIntArray(indexBuffers[geomIndex]);\r
+ for (int i = 0; i < allIndices.length - 3; i += 3) {\r
+ Vector3f n = FastMath.computeNormal(allVerts[allIndices[i]], allVerts[allIndices[i + 1]], allVerts[allIndices[i + 2]]);\r
+ meshHelper.addNormal(n, normalMap, smooth, allVerts[allIndices[i]], allVerts[allIndices[i + 1]], allVerts[allIndices[i + 2]]);\r
+ }\r
+ if (normalBuffers[geomIndex] == null) {\r
+ normalBuffers[geomIndex] = BufferUtils.createFloatBuffer(allVerts.length * 3);\r
+ }\r
+ for (Vector3f v : allVerts) {\r
+ Vector3f n = normalMap.get(v);\r
+ normalBuffers[geomIndex].put(n.x);\r
+ normalBuffers[geomIndex].put(n.y);\r
+ normalBuffers[geomIndex].put(n.z);\r
+ }\r
+ }\r
+\r
+ List<Geometry> result = new ArrayList<Geometry>(vertexBuffers.length);\r
+ for (int i = 0; i < vertexBuffers.length; ++i) {\r
+ Mesh mesh = new Mesh();\r
+ mesh.setBuffer(Type.Position, 3, vertexBuffers[i]);\r
+ mesh.setBuffer(Type.Index, 3, indexBuffers[i]);\r
+ mesh.setBuffer(Type.Normal, 3, normalBuffers[i]);\r
+ Geometry g = new Geometry("g" + i, mesh);\r
+ g.updateModelBound();\r
+ result.add(g);\r
+ }\r
+\r
+ return result;\r
+ }\r
+\r
+ /**\r
+ * This method loads the taper object.\r
+ * @param taperStructure\r
+ * the taper structure\r
+ * @param dataRepository\r
+ * the data repository\r
+ * @return the taper object\r
+ * @throws BlenderFileException\r
+ */\r
+ protected Curve loadTaperObject(Structure taperStructure, DataRepository dataRepository) throws BlenderFileException {\r
+ //reading nurbs\r
+ List<Structure> nurbStructures = ((Structure) taperStructure.getFieldValue("nurb")).evaluateListBase(dataRepository);\r
+ for (Structure nurb : nurbStructures) {\r
+ Pointer pBezierTriple = (Pointer) nurb.getFieldValue("bezt");\r
+ if (!pBezierTriple.isNull()) {\r
+ //creating the curve object\r
+ BezierCurve bezierCurve = new BezierCurve(0, pBezierTriple.fetchData(dataRepository.getInputStream()), 3);\r
+ List<Vector3f> controlPoints = bezierCurve.getControlPoints();\r
+ //removing the first and last handles\r
+ controlPoints.remove(0);\r
+ controlPoints.remove(controlPoints.size() - 1);\r
+\r
+ //return the first taper curve that has more than 3 control points\r
+ if (controlPoints.size() > 3) {\r
+ Spline spline = new Spline(SplineType.Bezier, controlPoints, 0, false);\r
+ int resolution = ((Number) taperStructure.getFieldValue("resolu")).intValue();\r
+ return new Curve(spline, resolution);\r
+ }\r
+ }\r
+ }\r
+ return null;\r
+ }\r
+\r
+ /**\r
+ * This method returns the translation of the curve. The UP axis is taken into account here.\r
+ * @param curveStructure\r
+ * the curve structure\r
+ * @return curve translation\r
+ */\r
+ @SuppressWarnings("unchecked")\r
+ protected Vector3f getLoc(Structure curveStructure) {\r
+ DynamicArray<Number> locArray = (DynamicArray<Number>) curveStructure.getFieldValue("loc");\r
+ if (fixUpAxis) {\r
+ return new Vector3f(locArray.get(0).floatValue(), locArray.get(1).floatValue(), -locArray.get(2).floatValue());\r
+ } else {\r
+ return new Vector3f(locArray.get(0).floatValue(), locArray.get(2).floatValue(), locArray.get(1).floatValue());\r
+ }\r
+ }\r
}\r
* @author Marcin Roguski\r
*/\r
public class IpoHelper extends AbstractBlenderHelper {\r
- /**\r
- * This constructor parses the given blender version and stores the result. Some functionalities may differ in\r
- * different blender versions.\r
- * @param blenderVersion\r
- * the version read from the blend file\r
- */\r
- public IpoHelper(String blenderVersion) {\r
- super(blenderVersion);\r
- }\r
\r
- /**\r
- * This method creates an ipo object used for interpolation calculations.\r
- * @param ipoStructure\r
- * the structure with ipo definition\r
- * @param dataRepository\r
- * the data repository\r
- * @return the ipo object\r
- * @throws BlenderFileException\r
- * this exception is thrown when the blender file is somehow corrupted\r
- */\r
- public Ipo createIpo(Structure ipoStructure, DataRepository dataRepository) throws BlenderFileException {\r
- Structure curvebase = (Structure)ipoStructure.getFieldValue("curve");\r
+ /**\r
+ * This constructor parses the given blender version and stores the result. Some functionalities may differ in\r
+ * different blender versions.\r
+ * @param blenderVersion\r
+ * the version read from the blend file\r
+ */\r
+ public IpoHelper(String blenderVersion) {\r
+ super(blenderVersion);\r
+ }\r
\r
- //preparing bezier curves\r
- Ipo result = null;\r
- List<Structure> curves = curvebase.evaluateListBase(dataRepository);//IpoCurve\r
- if(curves.size() > 0) {\r
- BezierCurve[] bezierCurves = new BezierCurve[curves.size()];\r
- int frame = 0;\r
- for(Structure curve : curves) {\r
- Pointer pBezTriple = (Pointer)curve.getFieldValue("bezt");\r
- List<Structure> bezTriples = pBezTriple.fetchData(dataRepository.getInputStream());\r
- int type = ((Number)curve.getFieldValue("adrcode")).intValue();\r
- bezierCurves[frame++] = new BezierCurve(type, bezTriples, 2);\r
- }\r
- curves.clear();\r
- result = new Ipo(bezierCurves);\r
- dataRepository.addLoadedFeatures(ipoStructure.getOldMemoryAddress(), ipoStructure.getName(), ipoStructure, result);\r
- }\r
- return result;\r
- }\r
+ /**\r
+ * This method creates an ipo object used for interpolation calculations.\r
+ * @param ipoStructure\r
+ * the structure with ipo definition\r
+ * @param dataRepository\r
+ * the data repository\r
+ * @return the ipo object\r
+ * @throws BlenderFileException\r
+ * this exception is thrown when the blender file is somehow corrupted\r
+ */\r
+ public Ipo createIpo(Structure ipoStructure, DataRepository dataRepository) throws BlenderFileException {\r
+ Structure curvebase = (Structure) ipoStructure.getFieldValue("curve");\r
\r
- /**\r
- * This method creates an ipo with only a single value. No track type is specified so do not use it for calculating\r
- * tracks.\r
- * @param constValue\r
- * the value of this ipo\r
- * @return constant ipo\r
- */\r
- public Ipo createIpo(float constValue) {\r
- return new ConstIpo(constValue);\r
- }\r
+ //preparing bezier curves\r
+ Ipo result = null;\r
+ List<Structure> curves = curvebase.evaluateListBase(dataRepository);//IpoCurve\r
+ if (curves.size() > 0) {\r
+ BezierCurve[] bezierCurves = new BezierCurve[curves.size()];\r
+ int frame = 0;\r
+ for (Structure curve : curves) {\r
+ Pointer pBezTriple = (Pointer) curve.getFieldValue("bezt");\r
+ List<Structure> bezTriples = pBezTriple.fetchData(dataRepository.getInputStream());\r
+ int type = ((Number) curve.getFieldValue("adrcode")).intValue();\r
+ bezierCurves[frame++] = new BezierCurve(type, bezTriples, 2);\r
+ }\r
+ curves.clear();\r
+ result = new Ipo(bezierCurves);\r
+ dataRepository.addLoadedFeatures(ipoStructure.getOldMemoryAddress(), ipoStructure.getName(), ipoStructure, result);\r
+ }\r
+ return result;\r
+ }\r
\r
- \r
+ /**\r
+ * This method creates an ipo with only a single value. No track type is specified so do not use it for calculating\r
+ * tracks.\r
+ * @param constValue\r
+ * the value of this ipo\r
+ * @return constant ipo\r
+ */\r
+ public Ipo createIpo(float constValue) {\r
+ return new ConstIpo(constValue);\r
+ }\r
\r
- /**\r
- * Ipo constant curve. This is a curve with only one value and no specified type. This type of ipo cannot be used to\r
- * calculate tracks. It should only be used to calculate single value for a given frame.\r
- * @author Marcin Roguski\r
- */\r
- private class ConstIpo extends Ipo {\r
- /** The constant value of this ipo. */\r
- private float constValue;\r
+ /**\r
+ * Ipo constant curve. This is a curve with only one value and no specified type. This type of ipo cannot be used to\r
+ * calculate tracks. It should only be used to calculate single value for a given frame.\r
+ * @author Marcin Roguski\r
+ */\r
+ private class ConstIpo extends Ipo {\r
\r
- /**\r
- * Constructor. Stores the constant value of this ipo.\r
- * @param constValue\r
- * the constant value of this ipo\r
- */\r
- public ConstIpo(float constValue) {\r
- super(null);\r
- this.constValue = constValue;\r
- }\r
+ /** The constant value of this ipo. */\r
+ private float constValue;\r
\r
- @Override\r
- public float calculateValue(int frame) {\r
- return constValue;\r
- }\r
+ /**\r
+ * Constructor. Stores the constant value of this ipo.\r
+ * @param constValue\r
+ * the constant value of this ipo\r
+ */\r
+ public ConstIpo(float constValue) {\r
+ super(null);\r
+ this.constValue = constValue;\r
+ }\r
\r
- @Override\r
- public float calculateValue(int frame, int curveIndex) {\r
- return constValue;\r
- }\r
+ @Override\r
+ public float calculateValue(int frame) {\r
+ return constValue;\r
+ }\r
\r
- @Override\r
- public int getCurvesAmount() {\r
- return 0;\r
- }\r
- \r
- @Override\r
- public BoneTrack calculateTrack(int boneIndex, int startFrame, int stopFrame, int fps) {\r
- throw new IllegalStateException("Constatnt ipo object cannot be used for calculating bone tracks!");\r
- }\r
- }\r
+ @Override\r
+ public float calculateValue(int frame, int curveIndex) {\r
+ return constValue;\r
+ }\r
+\r
+ @Override\r
+ public int getCurvesAmount() {\r
+ return 0;\r
+ }\r
+\r
+ @Override\r
+ public BoneTrack calculateTrack(int boneIndex, int startFrame, int stopFrame, int fps) {\r
+ throw new IllegalStateException("Constatnt ipo object cannot be used for calculating bone tracks!");\r
+ }\r
+ }\r
}\r
* @author Marcin Roguski\r
*/\r
public class LightHelper extends AbstractBlenderHelper {\r
- private static final Logger LOGGER = Logger.getLogger(LightHelper.class.getName());\r
- \r
- /**\r
- * This constructor parses the given blender version and stores the result. Some functionalities may differ in\r
- * different blender versions.\r
- * @param blenderVersion\r
- * the version read from the blend file\r
- */\r
- public LightHelper(String blenderVersion) {\r
- super(blenderVersion);\r
- }\r
\r
- public Light toLight(Structure structure, DataRepository dataRepository) throws BlenderFileException {\r
- Light result = (Light)dataRepository.getLoadedFeature(structure.getOldMemoryAddress(), LoadedFeatureDataType.LOADED_FEATURE);\r
- if(result != null) {\r
- return result;\r
- }\r
- int type = ((Number)structure.getFieldValue("type")).intValue();\r
- switch(type) {\r
- case 0://Lamp\r
- result = new PointLight();\r
- float distance = ((Number)structure.getFieldValue("dist")).floatValue();\r
- ((PointLight)result).setRadius(distance);\r
- break;\r
- case 1://Sun\r
- LOGGER.log(Level.WARNING, "'Sun' lamp is not supported in jMonkeyEngine.");\r
- break;\r
- case 2://Spot\r
- LOGGER.log(Level.WARNING, "'Spot' lamp is not supported in jMonkeyEngine.");\r
- break;\r
- case 3://Hemi\r
- LOGGER.log(Level.WARNING, "'Hemi' lamp is not supported in jMonkeyEngine.");\r
- break;\r
- case 4://Area\r
- result = new DirectionalLight();\r
- break;\r
- default:\r
- throw new BlenderFileException("Unknown light source type: " + type);\r
- }\r
- if(result != null) {\r
- float r = ((Number)structure.getFieldValue("r")).floatValue();\r
- float g = ((Number)structure.getFieldValue("g")).floatValue();\r
- float b = ((Number)structure.getFieldValue("b")).floatValue();\r
- result.setColor(new ColorRGBA(r, g, b, 0.0f));//TODO: 0 czy 1 ???\r
- }\r
- return result;\r
- }\r
+ private static final Logger LOGGER = Logger.getLogger(LightHelper.class.getName());\r
+\r
+ /**\r
+ * This constructor parses the given blender version and stores the result. Some functionalities may differ in\r
+ * different blender versions.\r
+ * @param blenderVersion\r
+ * the version read from the blend file\r
+ */\r
+ public LightHelper(String blenderVersion) {\r
+ super(blenderVersion);\r
+ }\r
+\r
+ public Light toLight(Structure structure, DataRepository dataRepository) throws BlenderFileException {\r
+ Light result = (Light) dataRepository.getLoadedFeature(structure.getOldMemoryAddress(), LoadedFeatureDataType.LOADED_FEATURE);\r
+ if (result != null) {\r
+ return result;\r
+ }\r
+ int type = ((Number) structure.getFieldValue("type")).intValue();\r
+ switch (type) {\r
+ case 0://Lamp\r
+ result = new PointLight();\r
+ float distance = ((Number) structure.getFieldValue("dist")).floatValue();\r
+ ((PointLight) result).setRadius(distance);\r
+ break;\r
+ case 1://Sun\r
+ LOGGER.log(Level.WARNING, "'Sun' lamp is not supported in jMonkeyEngine.");\r
+ break;\r
+ case 2://Spot\r
+ LOGGER.log(Level.WARNING, "'Spot' lamp is not supported in jMonkeyEngine.");\r
+ break;\r
+ case 3://Hemi\r
+ LOGGER.log(Level.WARNING, "'Hemi' lamp is not supported in jMonkeyEngine.");\r
+ break;\r
+ case 4://Area\r
+ result = new DirectionalLight();\r
+ break;\r
+ default:\r
+ throw new BlenderFileException("Unknown light source type: " + type);\r
+ }\r
+ if (result != null) {\r
+ float r = ((Number) structure.getFieldValue("r")).floatValue();\r
+ float g = ((Number) structure.getFieldValue("g")).floatValue();\r
+ float b = ((Number) structure.getFieldValue("b")).floatValue();\r
+ result.setColor(new ColorRGBA(r, g, b, 0.0f));//TODO: 0 czy 1 ???\r
+ }\r
+ return result;\r
+ }\r
}\r
import com.jme3.util.BufferUtils;\r
\r
public class MaterialHelper extends AbstractBlenderHelper {\r
- private static final Logger LOGGER = Logger.getLogger(MaterialHelper.class.getName());\r
- protected static final float DEFAULT_SHININESS = 20.0f;\r
-\r
- public static final String TEXTURE_TYPE_COLOR = "ColorMap";\r
- public static final String TEXTURE_TYPE_DIFFUSE = "DiffuseMap";\r
- public static final String TEXTURE_TYPE_NORMAL = "NormalMap";\r
- public static final String TEXTURE_TYPE_SPECULAR = "SpecularMap";\r
- public static final String TEXTURE_TYPE_GLOW = "GlowMap";\r
- public static final String TEXTURE_TYPE_ALPHA = "AlphaMap";\r
-\r
- public static final Integer ALPHA_MASK_NONE = Integer.valueOf(0);\r
- public static final Integer ALPHA_MASK_CIRCLE = Integer.valueOf(1);\r
- public static final Integer ALPHA_MASK_CONE = Integer.valueOf(2);\r
- public static final Integer ALPHA_MASK_HYPERBOLE = Integer.valueOf(3);\r
- protected final Map<Integer, IAlphaMask> alphaMasks = new HashMap<Integer, IAlphaMask>();\r
-\r
- /**\r
- * The type of the material's diffuse shader.\r
- */\r
- public static enum DiffuseShader {\r
- LAMBERT, ORENNAYAR, TOON, MINNAERT, FRESNEL\r
- }\r
-\r
- /**\r
- * The type of the material's specular shader.\r
- */\r
- public static enum SpecularShader {\r
- COOKTORRENCE, PHONG, BLINN, TOON, WARDISO\r
- }\r
-\r
- /** Face cull mode. Should be excplicitly set before this helper is used. */\r
- protected FaceCullMode faceCullMode;\r
-\r
- /**\r
- * This constructor parses the given blender version and stores the result. Some functionalities may differ in different blender\r
- * versions.\r
- * \r
- * @param blenderVersion\r
- * the version read from the blend file\r
- */\r
- public MaterialHelper(String blenderVersion) {\r
- super(blenderVersion);\r
- // setting alpha masks\r
- alphaMasks.put(ALPHA_MASK_NONE, new IAlphaMask() {\r
- @Override\r
- public void setImageSize(int width, int height) {}\r
-\r
- @Override\r
- public byte getAlpha(float x, float y) {\r
- return (byte) 255;\r
- }\r
- });\r
- alphaMasks.put(ALPHA_MASK_CIRCLE, new IAlphaMask() {\r
- private float r;\r
- private float[] center;\r
-\r
- @Override\r
- public void setImageSize(int width, int height) {\r
- r = Math.min(width, height) * 0.5f;\r
- center = new float[] { width * 0.5f, height * 0.5f };\r
- }\r
-\r
- @Override\r
- public byte getAlpha(float x, float y) {\r
- float d = FastMath.abs(FastMath.sqrt((x - center[0]) * (x - center[0]) + (y - center[1]) * (y - center[1])));\r
- return (byte) (d >= r ? 0 : 255);\r
- }\r
- });\r
- alphaMasks.put(ALPHA_MASK_CONE, new IAlphaMask() {\r
- private float r;\r
- private float[] center;\r
-\r
- @Override\r
- public void setImageSize(int width, int height) {\r
- r = Math.min(width, height) * 0.5f;\r
- center = new float[] { width * 0.5f, height * 0.5f };\r
- }\r
-\r
- @Override\r
- public byte getAlpha(float x, float y) {\r
- float d = FastMath.abs(FastMath.sqrt((x - center[0]) * (x - center[0]) + (y - center[1]) * (y - center[1])));\r
- return (byte) (d >= r ? 0 : -255.0f * d / r + 255.0f);\r
- }\r
- });\r
- alphaMasks.put(ALPHA_MASK_HYPERBOLE, new IAlphaMask() {\r
- private float r;\r
- private float[] center;\r
-\r
- @Override\r
- public void setImageSize(int width, int height) {\r
- r = Math.min(width, height) * 0.5f;\r
- center = new float[] { width * 0.5f, height * 0.5f };\r
- }\r
-\r
- @Override\r
- public byte getAlpha(float x, float y) {\r
- float d = FastMath.abs(FastMath.sqrt((x - center[0]) * (x - center[0]) + (y - center[1]) * (y - center[1]))) / r;\r
- return d >= 1.0f ? 0 : (byte) ((-FastMath.sqrt((2.0f - d) * d) + 1.0f) * 255.0f);\r
- }\r
- });\r
- }\r
-\r
- /**\r
- * This method sets the face cull mode to be used with every loaded material.\r
- * \r
- * @param faceCullMode\r
- * the face cull mode\r
- */\r
- public void setFaceCullMode(FaceCullMode faceCullMode) {\r
- this.faceCullMode = faceCullMode;\r
- }\r
-\r
- @SuppressWarnings("unchecked")\r
- public Material toMaterial(Structure structure, DataRepository dataRepository) throws BlenderFileException {\r
- LOGGER.log(Level.INFO, "Loading material.");\r
- if (structure == null) {\r
- return dataRepository.getDefaultMaterial();\r
- }\r
- Material result = (Material) dataRepository.getLoadedFeature(structure.getOldMemoryAddress(), LoadedFeatureDataType.LOADED_FEATURE);\r
- if (result != null) {\r
- return result;\r
- }\r
-\r
- int mode = ((Number) structure.getFieldValue("mode")).intValue();\r
- boolean shadeless = (mode & 0x4) != 0;\r
- boolean vertexColor = (mode & 0x16) != 0;\r
- boolean transparent = (mode & 0x64) != 0;\r
-\r
- if (shadeless) {\r
- result = new Material(dataRepository.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");\r
- } else {\r
- result = new Material(dataRepository.getAssetManager(), "Common/MatDefs/Light/Lighting.j3md");\r
- }\r
-\r
- result.getAdditionalRenderState().setFaceCullMode(faceCullMode);\r
-\r
- if (transparent) {\r
- result.getAdditionalRenderState().setBlendMode(BlendMode.Alpha);\r
- }\r
-\r
- String name = structure.getName();\r
- LOGGER.log(Level.INFO, "Material's name: {0}", name);\r
- if (vertexColor) {\r
- if (shadeless) {\r
- result.setBoolean("VertexColor", true);\r
- } else {\r
- result.setBoolean("UseVertexColor", true);\r
- }\r
- }\r
-\r
- MaterialHelper materialHelper = dataRepository.getHelper(MaterialHelper.class);\r
- ColorRGBA diffuseColor = null;\r
- if (shadeless) {\r
- // color of shadeless? doesn't seem to work in blender ..\r
- } else {\r
- result.setBoolean("UseMaterialColors", true);\r
-\r
- // setting the colors\r
- DiffuseShader diffuseShader = materialHelper.getDiffuseShader(structure);\r
- result.setBoolean("Minnaert", diffuseShader == DiffuseShader.MINNAERT);\r
- diffuseColor = materialHelper.getDiffuseColor(structure, diffuseShader);\r
- result.setColor("Diffuse", diffuseColor);\r
-\r
- SpecularShader specularShader = materialHelper.getSpecularShader(structure);\r
- result.setBoolean("WardIso", specularShader == SpecularShader.WARDISO);\r
- result.setColor("Specular", materialHelper.getSpecularColor(structure, specularShader));\r
-\r
- result.setColor("Ambient", materialHelper.getAmbientColor(structure));\r
- result.setFloat("Shininess", materialHelper.getShininess(structure));\r
- }\r
-\r
- // texture\r
- if ((dataRepository.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.TEXTURES) != 0) {\r
- TextureHelper textureHelper = dataRepository.getHelper(TextureHelper.class);\r
- DynamicArray<Pointer> mtexs = (DynamicArray<Pointer>) structure.getFieldValue("mtex");\r
- for (int i = 0; i < mtexs.getTotalSize(); ++i) {\r
- Pointer p = mtexs.get(i);\r
- if (!p.isNull()) {\r
- List<Structure> mtex = p.fetchData(dataRepository.getInputStream());\r
- if (mtex.size() == 1) {\r
- Structure textureLink = mtex.get(0);\r
- int texflag = ((Number) textureLink.getFieldValue("texflag")).intValue();\r
- // int texco = ((Number) textureLink.getFieldValue("texco")).intValue();\r
- boolean negateTexture = (texflag & 0x04) == 0;\r
-\r
- // if(texco == 0x10) {//TEXCO_UV (this is only supported now)\r
- int mapto = ((Number) textureLink.getFieldValue("mapto")).intValue();\r
- if (mapto != 0) {\r
- Pointer pTex = (Pointer) textureLink.getFieldValue("tex");\r
- Structure tex = pTex.fetchData(dataRepository.getInputStream()).get(0);\r
- Texture texture = textureHelper.getTexture(tex, dataRepository);\r
- if (texture != null) {\r
- if ((mapto & 0x01) != 0) {// Col\r
- result.setBoolean("UseMaterialColors", Boolean.FALSE);\r
- // blending the texture with material color and texture's defined color\r
- int blendType = ((Number) textureLink.getFieldValue("blendtype")).intValue();\r
- float[] color = new float[] { ((Number) textureLink.getFieldValue("r")).floatValue(), ((Number) textureLink.getFieldValue("g")).floatValue(), ((Number) textureLink.getFieldValue("b")).floatValue() };\r
- float colfac = ((Number) textureLink.getFieldValue("colfac")).floatValue();\r
- texture = textureHelper.blendTexture(diffuseColor.getColorArray(), texture, color, colfac, blendType, negateTexture, dataRepository);\r
- texture.setWrap(WrapMode.Repeat);\r
- if (shadeless) {\r
- result.setTexture(TEXTURE_TYPE_COLOR, texture);\r
- } else {\r
- result.setTexture(TEXTURE_TYPE_DIFFUSE, texture);\r
- }\r
- }\r
- if ((mapto & 0x02) != 0) {// Nor\r
- result.setTexture(TEXTURE_TYPE_NORMAL, texture);\r
- }\r
- if ((mapto & 0x20) != 0) {// Spec\r
- result.setTexture(TEXTURE_TYPE_SPECULAR, texture);\r
- }\r
- if ((mapto & 0x40) != 0) {// Emit\r
- result.setTexture(TEXTURE_TYPE_GLOW, texture);\r
- }\r
- if ((mapto & 0x80) != 0) {// Alpha\r
- result.setTexture(TEXTURE_TYPE_ALPHA, texture);\r
- }\r
- } else {\r
- LOGGER.log(Level.WARNING, "Texture not found!");\r
- }\r
- }\r
- // } else {\r
- // Pointer pTex = (Pointer)textureLink.getFieldValue("tex");\r
- // List<Structure> texs = pTex.fetchData(dataRepository.getInputStream());\r
- // Structure tex = texs.get(0);\r
- // LOGGER.log(Level.WARNING, "Unsupported texture type: " + texco);\r
- // }\r
- } else {\r
- LOGGER.log(Level.WARNING, "Many textures. Not solved yet!");// TODO\r
- }\r
- }\r
- }\r
- }\r
- dataRepository.addLoadedFeatures(structure.getOldMemoryAddress(), structure.getName(), structure, result);\r
- return result;\r
- }\r
-\r
- /**\r
- * This method returns a material similar to the one given but without textures. If the material has no textures it is not cloned but\r
- * returned itself.\r
- * \r
- * @param material\r
- * a material to be cloned without textures\r
- * @param imageType\r
- * type of image defined by blender; the constants are defined in TextureHelper\r
- * @return material without textures of a specified type\r
- */\r
- public Material getNonTexturedMaterial(Material material, int imageType) {\r
- String[] textureParamNames = new String[] { TEXTURE_TYPE_DIFFUSE, TEXTURE_TYPE_NORMAL, TEXTURE_TYPE_GLOW, TEXTURE_TYPE_SPECULAR, TEXTURE_TYPE_ALPHA };\r
- Map<String, Texture> textures = new HashMap<String, Texture>(textureParamNames.length);\r
- for (String textureParamName : textureParamNames) {\r
- MatParamTexture matParamTexture = material.getTextureParam(textureParamName);\r
- if (matParamTexture != null) {\r
- textures.put(textureParamName, matParamTexture.getTextureValue());\r
- }\r
- }\r
- if (textures.isEmpty()) {\r
- return material;\r
- } else {\r
- // clear all textures first so that wo de not waste resources cloning them\r
- for (Entry<String, Texture> textureParamName : textures.entrySet()) {\r
- String name = textureParamName.getValue().getName();\r
- try {\r
- int type = Integer.parseInt(name);\r
- if (type == imageType) {\r
- material.clearParam(textureParamName.getKey());\r
- }\r
- } catch (NumberFormatException e) {\r
- LOGGER.log(Level.WARNING, "The name of the texture does not contain the texture type value! {0} will not be removed!", name);\r
- }\r
- }\r
- Material result = material.clone();\r
- // put the textures back in place\r
- for (Entry<String, Texture> textureEntry : textures.entrySet()) {\r
- material.setTexture(textureEntry.getKey(), textureEntry.getValue());\r
- }\r
- return result;\r
- }\r
- }\r
-\r
- /**\r
- * This method converts the given material into particles-usable material.\r
- * The texture and glow color are being copied.\r
- * The method assumes it receives the Lighting type of material.\r
- * @param material\r
- * the source material\r
- * @param dataRepository\r
- * the data repository\r
- * @return material converted into particles-usable material\r
- */\r
- public Material getParticlesMaterial(Material material, Integer alphaMaskIndex, DataRepository dataRepository) {\r
- Material result = new Material(dataRepository.getAssetManager(), "Common/MatDefs/Misc/Particle.j3md");\r
-\r
- // copying texture\r
- MatParam diffuseMap = material.getParam("DiffuseMap");\r
- if (diffuseMap != null) {\r
- Texture texture = ((Texture) diffuseMap.getValue()).clone();\r
-\r
- // applying alpha mask to the texture\r
- Image image = texture.getImage();\r
- ByteBuffer sourceBB = image.getData(0);\r
- sourceBB.rewind();\r
- int w = image.getWidth();\r
- int h = image.getHeight();\r
- ByteBuffer bb = BufferUtils.createByteBuffer(w * h * 4);\r
- IAlphaMask iAlphaMask = alphaMasks.get(alphaMaskIndex);\r
- iAlphaMask.setImageSize(w, h);\r
-\r
- for (int x = 0; x < w; ++x) {\r
- for (int y = 0; y < h; ++y) {\r
- bb.put(sourceBB.get());\r
- bb.put(sourceBB.get());\r
- bb.put(sourceBB.get());\r
- bb.put(iAlphaMask.getAlpha(x, y));\r
- }\r
- }\r
-\r
- image = new Image(Format.RGBA8, w, h, bb);\r
- texture.setImage(image);\r
-\r
- result.setTextureParam("Texture", VarType.Texture2D, texture);\r
- }\r
-\r
- // copying glow color\r
- MatParam glowColor = material.getParam("GlowColor");\r
- if (glowColor != null) {\r
- ColorRGBA color = (ColorRGBA) glowColor.getValue();\r
- result.setParam("GlowColor", VarType.Vector3, color);\r
- }\r
- return result;\r
- }\r
-\r
- /**\r
- * This method indicates if the material has a texture of a specified type.\r
- * \r
- * @param material\r
- * the material\r
- * @param textureType\r
- * the type of the texture\r
- * @return <b>true</b> if the texture exists in the material and <B>false</b> otherwise\r
- */\r
- public boolean hasTexture(Material material, String textureType) {\r
- if (material != null) {\r
- return material.getTextureParam(textureType) != null;\r
- }\r
- return false;\r
- }\r
-\r
- /**\r
- * This method returns the diffuse color\r
- * \r
- * @param materialStructure\r
- * @param diffuseShader\r
- * @return\r
- */\r
- public ColorRGBA getDiffuseColor(Structure materialStructure, DiffuseShader diffuseShader) {\r
- // bitwise 'or' of all textures mappings\r
- int commonMapto = ((Number) materialStructure.getFieldValue("mapto")).intValue();\r
-\r
- // diffuse color\r
- float r = ((Number) materialStructure.getFieldValue("r")).floatValue();\r
- float g = ((Number) materialStructure.getFieldValue("g")).floatValue();\r
- float b = ((Number) materialStructure.getFieldValue("b")).floatValue();\r
- float alpha = ((Number) materialStructure.getFieldValue("alpha")).floatValue();\r
- if ((commonMapto & 0x01) == 0x01) {// Col\r
- return new ColorRGBA(r, g, b, alpha);\r
- } else {\r
- switch (diffuseShader) {\r
- case FRESNEL:\r
- case ORENNAYAR:\r
- case TOON:\r
- break;// TODO: find what is the proper modification\r
- case MINNAERT:\r
- case LAMBERT:// TODO: check if that is correct\r
- float ref = ((Number) materialStructure.getFieldValue("ref")).floatValue();\r
- r *= ref;\r
- g *= ref;\r
- b *= ref;\r
- break;\r
- default:\r
- throw new IllegalStateException("Unknown diffuse shader type: " + diffuseShader.toString());\r
- }\r
- return new ColorRGBA(r, g, b, alpha);\r
- }\r
- }\r
-\r
- /**\r
- * This method returns an enum describing the type of a diffuse shader used by this material.\r
- * \r
- * @param materialStructure\r
- * the material structure filled with data\r
- * @return an enum describing the type of a diffuse shader used by this material\r
- */\r
- public DiffuseShader getDiffuseShader(Structure materialStructure) {\r
- int diff_shader = ((Number) materialStructure.getFieldValue("diff_shader")).intValue();\r
- return DiffuseShader.values()[diff_shader];\r
- }\r
-\r
- /**\r
- * This method returns an ambient color used by the material.\r
- * \r
- * @param materialStructure\r
- * the material structure filled with data\r
- * @return an ambient color used by the material\r
- */\r
- public ColorRGBA getAmbientColor(Structure materialStructure) {\r
- float r = ((Number) materialStructure.getFieldValue("ambr")).floatValue();\r
- float g = ((Number) materialStructure.getFieldValue("ambg")).floatValue();\r
- float b = ((Number) materialStructure.getFieldValue("ambb")).floatValue();\r
- float alpha = ((Number) materialStructure.getFieldValue("alpha")).floatValue();\r
- return new ColorRGBA(r, g, b, alpha);\r
- }\r
-\r
- /**\r
- * This method returns an enum describing the type of a specular shader used by this material.\r
- * \r
- * @param materialStructure\r
- * the material structure filled with data\r
- * @return an enum describing the type of a specular shader used by this material\r
- */\r
- public SpecularShader getSpecularShader(Structure materialStructure) {\r
- int spec_shader = ((Number) materialStructure.getFieldValue("spec_shader")).intValue();\r
- return SpecularShader.values()[spec_shader];\r
- }\r
-\r
- /**\r
- * This method returns a specular color used by the material.\r
- * \r
- * @param materialStructure\r
- * the material structure filled with data\r
- * @return a specular color used by the material\r
- */\r
- public ColorRGBA getSpecularColor(Structure materialStructure, SpecularShader specularShader) {\r
- float r = ((Number) materialStructure.getFieldValue("specr")).floatValue();\r
- float g = ((Number) materialStructure.getFieldValue("specg")).floatValue();\r
- float b = ((Number) materialStructure.getFieldValue("specb")).floatValue();\r
- float alpha = ((Number) materialStructure.getFieldValue("alpha")).floatValue();\r
- switch (specularShader) {\r
- case BLINN:\r
- case COOKTORRENCE:\r
- case TOON:\r
- case WARDISO:// TODO: find what is the proper modification\r
- break;\r
- case PHONG:// TODO: check if that is correct\r
- float spec = ((Number) materialStructure.getFieldValue("spec")).floatValue();\r
- r *= spec * 0.5f;\r
- g *= spec * 0.5f;\r
- b *= spec * 0.5f;\r
- break;\r
- default:\r
- throw new IllegalStateException("Unknown specular shader type: " + specularShader.toString());\r
- }\r
- return new ColorRGBA(r, g, b, alpha);\r
- }\r
-\r
- /**\r
- * This method returns the sihiness of this material or DEFAULT_SHININESS value if not present.\r
- * \r
- * @param materialStructure\r
- * the material structure filled with data\r
- * @return the sihiness of this material or DEFAULT_SHININESS value if not present\r
- */\r
- public float getShininess(Structure materialStructure) {\r
- float shininess = ((Number) materialStructure.getFieldValue("emit")).floatValue();\r
- return shininess > 0.0f ? shininess : DEFAULT_SHININESS;\r
- }\r
-\r
- /**\r
- * This method returns the table of materials connected to the specified structure. The given structure can be of any type (ie. mesh or\r
- * curve) but needs to have 'mat' field/\r
- * \r
- * @param structureWithMaterials\r
- * the structure containing the mesh data\r
- * @param dataRepository\r
- * the data repository\r
- * @return a list of vertices colors, each color belongs to a single vertex\r
- * @throws BlenderFileException\r
- * this exception is thrown when the blend file structure is somehow invalid or corrupted\r
- */\r
- public Material[] getMaterials(Structure structureWithMaterials, DataRepository dataRepository) throws BlenderFileException {\r
- Pointer ppMaterials = (Pointer) structureWithMaterials.getFieldValue("mat");\r
- Material[] materials = null;\r
- if (!ppMaterials.isNull()) {\r
- List<Structure> materialStructures = ppMaterials.fetchData(dataRepository.getInputStream());\r
- if (materialStructures != null && materialStructures.size() > 0) {\r
- MaterialHelper materialHelper = dataRepository.getHelper(MaterialHelper.class);\r
- materials = new Material[materialStructures.size()];\r
- int i = 0;\r
- for (Structure s : materialStructures) {\r
- Material material = (Material) dataRepository.getLoadedFeature(s.getOldMemoryAddress(), LoadedFeatureDataType.LOADED_FEATURE);\r
- if (material == null) {\r
- material = materialHelper.toMaterial(s, dataRepository);\r
- }\r
- materials[i++] = material;\r
- }\r
- }\r
- }\r
- return materials;\r
- }\r
-\r
- /**\r
- * This method converts rgb values to hsv values.\r
- * \r
- * @param rgb\r
- * rgb values of the color\r
- * @param hsv\r
- * hsv values of a color (this table contains the result of the transformation)\r
- */\r
- public void rgbToHsv(float r, float g, float b, float[] hsv) {\r
- float cmax = r;\r
- float cmin = r;\r
- cmax = g > cmax ? g : cmax;\r
- cmin = g < cmin ? g : cmin;\r
- cmax = b > cmax ? b : cmax;\r
- cmin = b < cmin ? b : cmin;\r
-\r
- hsv[2] = cmax; /* value */\r
- if (cmax != 0.0) {\r
- hsv[1] = (cmax - cmin) / cmax;\r
- } else {\r
- hsv[1] = 0.0f;\r
- hsv[0] = 0.0f;\r
- }\r
- if (hsv[1] == 0.0) {\r
- hsv[0] = -1.0f;\r
- } else {\r
- float cdelta = cmax - cmin;\r
- float rc = (cmax - r) / cdelta;\r
- float gc = (cmax - g) / cdelta;\r
- float bc = (cmax - b) / cdelta;\r
- if (r == cmax) {\r
- hsv[0] = bc - gc;\r
- } else if (g == cmax) {\r
- hsv[0] = 2.0f + rc - bc;\r
- } else {\r
- hsv[0] = 4.0f + gc - rc;\r
- }\r
- hsv[0] *= 60.0f;\r
- if (hsv[0] < 0.0f) {\r
- hsv[0] += 360.0f;\r
- }\r
- }\r
-\r
- hsv[0] /= 360.0f;\r
- if (hsv[0] < 0.0f) {\r
- hsv[0] = 0.0f;\r
- }\r
- }\r
-\r
- /**\r
- * This method converts rgb values to hsv values.\r
- * \r
- * @param h\r
- * hue\r
- * @param s\r
- * saturation\r
- * @param v\r
- * value\r
- * @param rgb\r
- * rgb result vector (should have 3 elements)\r
- */\r
- public void hsvToRgb(float h, float s, float v, float[] rgb) {\r
- h *= 360.0f;\r
- if (s == 0.0) {\r
- rgb[0] = rgb[1] = rgb[2] = v;\r
- } else {\r
- if (h == 360) {\r
- h = 0;\r
- } else {\r
- h /= 60;\r
- }\r
- int i = (int) Math.floor(h);\r
- float f = h - i;\r
- float p = v * (1.0f - s);\r
- float q = v * (1.0f - s * f);\r
- float t = v * (1.0f - s * (1.0f - f));\r
- switch (i) {\r
- case 0:\r
- rgb[0] = v;\r
- rgb[1] = t;\r
- rgb[2] = p;\r
- break;\r
- case 1:\r
- rgb[0] = q;\r
- rgb[1] = v;\r
- rgb[2] = p;\r
- break;\r
- case 2:\r
- rgb[0] = p;\r
- rgb[1] = v;\r
- rgb[2] = t;\r
- break;\r
- case 3:\r
- rgb[0] = p;\r
- rgb[1] = q;\r
- rgb[2] = v;\r
- break;\r
- case 4:\r
- rgb[0] = t;\r
- rgb[1] = p;\r
- rgb[2] = v;\r
- break;\r
- case 5:\r
- rgb[0] = v;\r
- rgb[1] = p;\r
- rgb[2] = q;\r
- break;\r
- }\r
- }\r
- }\r
-\r
- /**\r
- * An interface used in calculating alpha mask during particles' texture calculations.\r
- * @author Marcin Roguski (Kaelthas)\r
- */\r
- protected static interface IAlphaMask {\r
- /**\r
- * This method sets the size of the texture's image.\r
- * @param width\r
- * the width of the image\r
- * @param height\r
- * the height of the image\r
- */\r
- void setImageSize(int width, int height);\r
-\r
- /**\r
- * This method returns the alpha value for the specified texture position.\r
- * @param x\r
- * the X coordinate of the texture position\r
- * @param y\r
- * the Y coordinate of the texture position\r
- * @return the alpha value for the specified texture position\r
- */\r
- byte getAlpha(float x, float y);\r
- }\r
+\r
+ private static final Logger LOGGER = Logger.getLogger(MaterialHelper.class.getName());\r
+ protected static final float DEFAULT_SHININESS = 20.0f;\r
+ public static final String TEXTURE_TYPE_COLOR = "ColorMap";\r
+ public static final String TEXTURE_TYPE_DIFFUSE = "DiffuseMap";\r
+ public static final String TEXTURE_TYPE_NORMAL = "NormalMap";\r
+ public static final String TEXTURE_TYPE_SPECULAR = "SpecularMap";\r
+ public static final String TEXTURE_TYPE_GLOW = "GlowMap";\r
+ public static final String TEXTURE_TYPE_ALPHA = "AlphaMap";\r
+ public static final Integer ALPHA_MASK_NONE = Integer.valueOf(0);\r
+ public static final Integer ALPHA_MASK_CIRCLE = Integer.valueOf(1);\r
+ public static final Integer ALPHA_MASK_CONE = Integer.valueOf(2);\r
+ public static final Integer ALPHA_MASK_HYPERBOLE = Integer.valueOf(3);\r
+ protected final Map<Integer, AlphaMask> alphaMasks = new HashMap<Integer, AlphaMask>();\r
+\r
+ /**\r
+ * The type of the material's diffuse shader.\r
+ */\r
+ public static enum DiffuseShader {\r
+\r
+ LAMBERT, ORENNAYAR, TOON, MINNAERT, FRESNEL\r
+ }\r
+\r
+ /**\r
+ * The type of the material's specular shader.\r
+ */\r
+ public static enum SpecularShader {\r
+\r
+ COOKTORRENCE, PHONG, BLINN, TOON, WARDISO\r
+ }\r
+ /** Face cull mode. Should be excplicitly set before this helper is used. */\r
+ protected FaceCullMode faceCullMode;\r
+\r
+ /**\r
+ * This constructor parses the given blender version and stores the result. Some functionalities may differ in different blender\r
+ * versions.\r
+ * \r
+ * @param blenderVersion\r
+ * the version read from the blend file\r
+ */\r
+ public MaterialHelper(String blenderVersion) {\r
+ super(blenderVersion);\r
+ // setting alpha masks\r
+ alphaMasks.put(ALPHA_MASK_NONE, new AlphaMask() {\r
+\r
+ @Override\r
+ public void setImageSize(int width, int height) {\r
+ }\r
+\r
+ @Override\r
+ public byte getAlpha(float x, float y) {\r
+ return (byte) 255;\r
+ }\r
+ });\r
+ alphaMasks.put(ALPHA_MASK_CIRCLE, new AlphaMask() {\r
+\r
+ private float r;\r
+ private float[] center;\r
+\r
+ @Override\r
+ public void setImageSize(int width, int height) {\r
+ r = Math.min(width, height) * 0.5f;\r
+ center = new float[]{width * 0.5f, height * 0.5f};\r
+ }\r
+\r
+ @Override\r
+ public byte getAlpha(float x, float y) {\r
+ float d = FastMath.abs(FastMath.sqrt((x - center[0]) * (x - center[0]) + (y - center[1]) * (y - center[1])));\r
+ return (byte) (d >= r ? 0 : 255);\r
+ }\r
+ });\r
+ alphaMasks.put(ALPHA_MASK_CONE, new AlphaMask() {\r
+\r
+ private float r;\r
+ private float[] center;\r
+\r
+ @Override\r
+ public void setImageSize(int width, int height) {\r
+ r = Math.min(width, height) * 0.5f;\r
+ center = new float[]{width * 0.5f, height * 0.5f};\r
+ }\r
+\r
+ @Override\r
+ public byte getAlpha(float x, float y) {\r
+ float d = FastMath.abs(FastMath.sqrt((x - center[0]) * (x - center[0]) + (y - center[1]) * (y - center[1])));\r
+ return (byte) (d >= r ? 0 : -255.0f * d / r + 255.0f);\r
+ }\r
+ });\r
+ alphaMasks.put(ALPHA_MASK_HYPERBOLE, new AlphaMask() {\r
+\r
+ private float r;\r
+ private float[] center;\r
+\r
+ @Override\r
+ public void setImageSize(int width, int height) {\r
+ r = Math.min(width, height) * 0.5f;\r
+ center = new float[]{width * 0.5f, height * 0.5f};\r
+ }\r
+\r
+ @Override\r
+ public byte getAlpha(float x, float y) {\r
+ float d = FastMath.abs(FastMath.sqrt((x - center[0]) * (x - center[0]) + (y - center[1]) * (y - center[1]))) / r;\r
+ return d >= 1.0f ? 0 : (byte) ((-FastMath.sqrt((2.0f - d) * d) + 1.0f) * 255.0f);\r
+ }\r
+ });\r
+ }\r
+\r
+ /**\r
+ * This method sets the face cull mode to be used with every loaded material.\r
+ * \r
+ * @param faceCullMode\r
+ * the face cull mode\r
+ */\r
+ public void setFaceCullMode(FaceCullMode faceCullMode) {\r
+ this.faceCullMode = faceCullMode;\r
+ }\r
+\r
+ @SuppressWarnings("unchecked")\r
+ public Material toMaterial(Structure structure, DataRepository dataRepository) throws BlenderFileException {\r
+ LOGGER.log(Level.INFO, "Loading material.");\r
+ if (structure == null) {\r
+ return dataRepository.getDefaultMaterial();\r
+ }\r
+ Material result = (Material) dataRepository.getLoadedFeature(structure.getOldMemoryAddress(), LoadedFeatureDataType.LOADED_FEATURE);\r
+ if (result != null) {\r
+ return result;\r
+ }\r
+\r
+ int mode = ((Number) structure.getFieldValue("mode")).intValue();\r
+ boolean shadeless = (mode & 0x4) != 0;\r
+ boolean vertexColor = (mode & 0x16) != 0;\r
+ boolean transparent = (mode & 0x64) != 0;\r
+\r
+ if (shadeless) {\r
+ result = new Material(dataRepository.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");\r
+ } else {\r
+ result = new Material(dataRepository.getAssetManager(), "Common/MatDefs/Light/Lighting.j3md");\r
+ }\r
+\r
+ result.getAdditionalRenderState().setFaceCullMode(faceCullMode);\r
+\r
+ if (transparent) {\r
+ result.getAdditionalRenderState().setBlendMode(BlendMode.Alpha);\r
+ }\r
+\r
+ String name = structure.getName();\r
+ LOGGER.log(Level.INFO, "Material's name: {0}", name);\r
+ if (vertexColor) {\r
+ if (shadeless) {\r
+ result.setBoolean("VertexColor", true);\r
+ } else {\r
+ result.setBoolean("UseVertexColor", true);\r
+ }\r
+ }\r
+\r
+ MaterialHelper materialHelper = dataRepository.getHelper(MaterialHelper.class);\r
+ ColorRGBA diffuseColor = null;\r
+ if (shadeless) {\r
+ // color of shadeless? doesn't seem to work in blender ..\r
+ } else {\r
+ result.setBoolean("UseMaterialColors", true);\r
+\r
+ // setting the colors\r
+ DiffuseShader diffuseShader = materialHelper.getDiffuseShader(structure);\r
+ result.setBoolean("Minnaert", diffuseShader == DiffuseShader.MINNAERT);\r
+ diffuseColor = materialHelper.getDiffuseColor(structure, diffuseShader);\r
+ result.setColor("Diffuse", diffuseColor);\r
+\r
+ SpecularShader specularShader = materialHelper.getSpecularShader(structure);\r
+ result.setBoolean("WardIso", specularShader == SpecularShader.WARDISO);\r
+ result.setColor("Specular", materialHelper.getSpecularColor(structure, specularShader));\r
+\r
+ result.setColor("Ambient", materialHelper.getAmbientColor(structure));\r
+ result.setFloat("Shininess", materialHelper.getShininess(structure));\r
+ }\r
+\r
+ // texture\r
+ if ((dataRepository.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.TEXTURES) != 0) {\r
+ TextureHelper textureHelper = dataRepository.getHelper(TextureHelper.class);\r
+ DynamicArray<Pointer> mtexs = (DynamicArray<Pointer>) structure.getFieldValue("mtex");\r
+ for (int i = 0; i < mtexs.getTotalSize(); ++i) {\r
+ Pointer p = mtexs.get(i);\r
+ if (!p.isNull()) {\r
+ List<Structure> mtex = p.fetchData(dataRepository.getInputStream());\r
+ if (mtex.size() == 1) {\r
+ Structure textureLink = mtex.get(0);\r
+ int texflag = ((Number) textureLink.getFieldValue("texflag")).intValue();\r
+ // int texco = ((Number) textureLink.getFieldValue("texco")).intValue();\r
+ boolean negateTexture = (texflag & 0x04) == 0;\r
+\r
+ // if(texco == 0x10) {//TEXCO_UV (this is only supported now)\r
+ int mapto = ((Number) textureLink.getFieldValue("mapto")).intValue();\r
+ if (mapto != 0) {\r
+ Pointer pTex = (Pointer) textureLink.getFieldValue("tex");\r
+ Structure tex = pTex.fetchData(dataRepository.getInputStream()).get(0);\r
+ Texture texture = textureHelper.getTexture(tex, dataRepository);\r
+ if (texture != null) {\r
+ if ((mapto & 0x01) != 0) {// Col\r
+ result.setBoolean("UseMaterialColors", Boolean.FALSE);\r
+ // blending the texture with material color and texture's defined color\r
+ int blendType = ((Number) textureLink.getFieldValue("blendtype")).intValue();\r
+ float[] color = new float[]{((Number) textureLink.getFieldValue("r")).floatValue(), ((Number) textureLink.getFieldValue("g")).floatValue(), ((Number) textureLink.getFieldValue("b")).floatValue()};\r
+ float colfac = ((Number) textureLink.getFieldValue("colfac")).floatValue();\r
+ texture = textureHelper.blendTexture(diffuseColor.getColorArray(), texture, color, colfac, blendType, negateTexture, dataRepository);\r
+ texture.setWrap(WrapMode.Repeat);\r
+ if (shadeless) {\r
+ result.setTexture(TEXTURE_TYPE_COLOR, texture);\r
+ } else {\r
+ result.setTexture(TEXTURE_TYPE_DIFFUSE, texture);\r
+ }\r
+ }\r
+ if ((mapto & 0x02) != 0) {// Nor\r
+ result.setTexture(TEXTURE_TYPE_NORMAL, texture);\r
+ }\r
+ if ((mapto & 0x20) != 0) {// Spec\r
+ result.setTexture(TEXTURE_TYPE_SPECULAR, texture);\r
+ }\r
+ if ((mapto & 0x40) != 0) {// Emit\r
+ result.setTexture(TEXTURE_TYPE_GLOW, texture);\r
+ }\r
+ if ((mapto & 0x80) != 0) {// Alpha\r
+ result.setTexture(TEXTURE_TYPE_ALPHA, texture);\r
+ }\r
+ } else {\r
+ LOGGER.log(Level.WARNING, "Texture not found!");\r
+ }\r
+ }\r
+ // } else {\r
+ // Pointer pTex = (Pointer)textureLink.getFieldValue("tex");\r
+ // List<Structure> texs = pTex.fetchData(dataRepository.getInputStream());\r
+ // Structure tex = texs.get(0);\r
+ // LOGGER.log(Level.WARNING, "Unsupported texture type: " + texco);\r
+ // }\r
+ } else {\r
+ LOGGER.log(Level.WARNING, "Many textures. Not solved yet!");// TODO\r
+ }\r
+ }\r
+ }\r
+ }\r
+ dataRepository.addLoadedFeatures(structure.getOldMemoryAddress(), structure.getName(), structure, result);\r
+ return result;\r
+ }\r
+\r
+ /**\r
+ * This method returns a material similar to the one given but without textures. If the material has no textures it is not cloned but\r
+ * returned itself.\r
+ * \r
+ * @param material\r
+ * a material to be cloned without textures\r
+ * @param imageType\r
+ * type of image defined by blender; the constants are defined in TextureHelper\r
+ * @return material without textures of a specified type\r
+ */\r
+ public Material getNonTexturedMaterial(Material material, int imageType) {\r
+ String[] textureParamNames = new String[]{TEXTURE_TYPE_DIFFUSE, TEXTURE_TYPE_NORMAL, TEXTURE_TYPE_GLOW, TEXTURE_TYPE_SPECULAR, TEXTURE_TYPE_ALPHA};\r
+ Map<String, Texture> textures = new HashMap<String, Texture>(textureParamNames.length);\r
+ for (String textureParamName : textureParamNames) {\r
+ MatParamTexture matParamTexture = material.getTextureParam(textureParamName);\r
+ if (matParamTexture != null) {\r
+ textures.put(textureParamName, matParamTexture.getTextureValue());\r
+ }\r
+ }\r
+ if (textures.isEmpty()) {\r
+ return material;\r
+ } else {\r
+ // clear all textures first so that wo de not waste resources cloning them\r
+ for (Entry<String, Texture> textureParamName : textures.entrySet()) {\r
+ String name = textureParamName.getValue().getName();\r
+ try {\r
+ int type = Integer.parseInt(name);\r
+ if (type == imageType) {\r
+ material.clearParam(textureParamName.getKey());\r
+ }\r
+ } catch (NumberFormatException e) {\r
+ LOGGER.log(Level.WARNING, "The name of the texture does not contain the texture type value! {0} will not be removed!", name);\r
+ }\r
+ }\r
+ Material result = material.clone();\r
+ // put the textures back in place\r
+ for (Entry<String, Texture> textureEntry : textures.entrySet()) {\r
+ material.setTexture(textureEntry.getKey(), textureEntry.getValue());\r
+ }\r
+ return result;\r
+ }\r
+ }\r
+\r
+ /**\r
+ * This method converts the given material into particles-usable material.\r
+ * The texture and glow color are being copied.\r
+ * The method assumes it receives the Lighting type of material.\r
+ * @param material\r
+ * the source material\r
+ * @param dataRepository\r
+ * the data repository\r
+ * @return material converted into particles-usable material\r
+ */\r
+ public Material getParticlesMaterial(Material material, Integer alphaMaskIndex, DataRepository dataRepository) {\r
+ Material result = new Material(dataRepository.getAssetManager(), "Common/MatDefs/Misc/Particle.j3md");\r
+\r
+ // copying texture\r
+ MatParam diffuseMap = material.getParam("DiffuseMap");\r
+ if (diffuseMap != null) {\r
+ Texture texture = ((Texture) diffuseMap.getValue()).clone();\r
+\r
+ // applying alpha mask to the texture\r
+ Image image = texture.getImage();\r
+ ByteBuffer sourceBB = image.getData(0);\r
+ sourceBB.rewind();\r
+ int w = image.getWidth();\r
+ int h = image.getHeight();\r
+ ByteBuffer bb = BufferUtils.createByteBuffer(w * h * 4);\r
+ AlphaMask iAlphaMask = alphaMasks.get(alphaMaskIndex);\r
+ iAlphaMask.setImageSize(w, h);\r
+\r
+ for (int x = 0; x < w; ++x) {\r
+ for (int y = 0; y < h; ++y) {\r
+ bb.put(sourceBB.get());\r
+ bb.put(sourceBB.get());\r
+ bb.put(sourceBB.get());\r
+ bb.put(iAlphaMask.getAlpha(x, y));\r
+ }\r
+ }\r
+\r
+ image = new Image(Format.RGBA8, w, h, bb);\r
+ texture.setImage(image);\r
+\r
+ result.setTextureParam("Texture", VarType.Texture2D, texture);\r
+ }\r
+\r
+ // copying glow color\r
+ MatParam glowColor = material.getParam("GlowColor");\r
+ if (glowColor != null) {\r
+ ColorRGBA color = (ColorRGBA) glowColor.getValue();\r
+ result.setParam("GlowColor", VarType.Vector3, color);\r
+ }\r
+ return result;\r
+ }\r
+\r
+ /**\r
+ * This method indicates if the material has a texture of a specified type.\r
+ * \r
+ * @param material\r
+ * the material\r
+ * @param textureType\r
+ * the type of the texture\r
+ * @return <b>true</b> if the texture exists in the material and <B>false</b> otherwise\r
+ */\r
+ public boolean hasTexture(Material material, String textureType) {\r
+ if (material != null) {\r
+ return material.getTextureParam(textureType) != null;\r
+ }\r
+ return false;\r
+ }\r
+\r
+ /**\r
+ * This method returns the diffuse color\r
+ * \r
+ * @param materialStructure\r
+ * @param diffuseShader\r
+ * @return\r
+ */\r
+ public ColorRGBA getDiffuseColor(Structure materialStructure, DiffuseShader diffuseShader) {\r
+ // bitwise 'or' of all textures mappings\r
+ int commonMapto = ((Number) materialStructure.getFieldValue("mapto")).intValue();\r
+\r
+ // diffuse color\r
+ float r = ((Number) materialStructure.getFieldValue("r")).floatValue();\r
+ float g = ((Number) materialStructure.getFieldValue("g")).floatValue();\r
+ float b = ((Number) materialStructure.getFieldValue("b")).floatValue();\r
+ float alpha = ((Number) materialStructure.getFieldValue("alpha")).floatValue();\r
+ if ((commonMapto & 0x01) == 0x01) {// Col\r
+ return new ColorRGBA(r, g, b, alpha);\r
+ } else {\r
+ switch (diffuseShader) {\r
+ case FRESNEL:\r
+ case ORENNAYAR:\r
+ case TOON:\r
+ break;// TODO: find what is the proper modification\r
+ case MINNAERT:\r
+ case LAMBERT:// TODO: check if that is correct\r
+ float ref = ((Number) materialStructure.getFieldValue("ref")).floatValue();\r
+ r *= ref;\r
+ g *= ref;\r
+ b *= ref;\r
+ break;\r
+ default:\r
+ throw new IllegalStateException("Unknown diffuse shader type: " + diffuseShader.toString());\r
+ }\r
+ return new ColorRGBA(r, g, b, alpha);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * This method returns an enum describing the type of a diffuse shader used by this material.\r
+ * \r
+ * @param materialStructure\r
+ * the material structure filled with data\r
+ * @return an enum describing the type of a diffuse shader used by this material\r
+ */\r
+ public DiffuseShader getDiffuseShader(Structure materialStructure) {\r
+ int diff_shader = ((Number) materialStructure.getFieldValue("diff_shader")).intValue();\r
+ return DiffuseShader.values()[diff_shader];\r
+ }\r
+\r
+ /**\r
+ * This method returns an ambient color used by the material.\r
+ * \r
+ * @param materialStructure\r
+ * the material structure filled with data\r
+ * @return an ambient color used by the material\r
+ */\r
+ public ColorRGBA getAmbientColor(Structure materialStructure) {\r
+ float r = ((Number) materialStructure.getFieldValue("ambr")).floatValue();\r
+ float g = ((Number) materialStructure.getFieldValue("ambg")).floatValue();\r
+ float b = ((Number) materialStructure.getFieldValue("ambb")).floatValue();\r
+ float alpha = ((Number) materialStructure.getFieldValue("alpha")).floatValue();\r
+ return new ColorRGBA(r, g, b, alpha);\r
+ }\r
+\r
+ /**\r
+ * This method returns an enum describing the type of a specular shader used by this material.\r
+ * \r
+ * @param materialStructure\r
+ * the material structure filled with data\r
+ * @return an enum describing the type of a specular shader used by this material\r
+ */\r
+ public SpecularShader getSpecularShader(Structure materialStructure) {\r
+ int spec_shader = ((Number) materialStructure.getFieldValue("spec_shader")).intValue();\r
+ return SpecularShader.values()[spec_shader];\r
+ }\r
+\r
+ /**\r
+ * This method returns a specular color used by the material.\r
+ * \r
+ * @param materialStructure\r
+ * the material structure filled with data\r
+ * @return a specular color used by the material\r
+ */\r
+ public ColorRGBA getSpecularColor(Structure materialStructure, SpecularShader specularShader) {\r
+ float r = ((Number) materialStructure.getFieldValue("specr")).floatValue();\r
+ float g = ((Number) materialStructure.getFieldValue("specg")).floatValue();\r
+ float b = ((Number) materialStructure.getFieldValue("specb")).floatValue();\r
+ float alpha = ((Number) materialStructure.getFieldValue("alpha")).floatValue();\r
+ switch (specularShader) {\r
+ case BLINN:\r
+ case COOKTORRENCE:\r
+ case TOON:\r
+ case WARDISO:// TODO: find what is the proper modification\r
+ break;\r
+ case PHONG:// TODO: check if that is correct\r
+ float spec = ((Number) materialStructure.getFieldValue("spec")).floatValue();\r
+ r *= spec * 0.5f;\r
+ g *= spec * 0.5f;\r
+ b *= spec * 0.5f;\r
+ break;\r
+ default:\r
+ throw new IllegalStateException("Unknown specular shader type: " + specularShader.toString());\r
+ }\r
+ return new ColorRGBA(r, g, b, alpha);\r
+ }\r
+\r
+ /**\r
+ * This method returns the sihiness of this material or DEFAULT_SHININESS value if not present.\r
+ * \r
+ * @param materialStructure\r
+ * the material structure filled with data\r
+ * @return the sihiness of this material or DEFAULT_SHININESS value if not present\r
+ */\r
+ public float getShininess(Structure materialStructure) {\r
+ float shininess = ((Number) materialStructure.getFieldValue("emit")).floatValue();\r
+ return shininess > 0.0f ? shininess : DEFAULT_SHININESS;\r
+ }\r
+\r
+ /**\r
+ * This method returns the table of materials connected to the specified structure. The given structure can be of any type (ie. mesh or\r
+ * curve) but needs to have 'mat' field/\r
+ * \r
+ * @param structureWithMaterials\r
+ * the structure containing the mesh data\r
+ * @param dataRepository\r
+ * the data repository\r
+ * @return a list of vertices colors, each color belongs to a single vertex\r
+ * @throws BlenderFileException\r
+ * this exception is thrown when the blend file structure is somehow invalid or corrupted\r
+ */\r
+ public Material[] getMaterials(Structure structureWithMaterials, DataRepository dataRepository) throws BlenderFileException {\r
+ Pointer ppMaterials = (Pointer) structureWithMaterials.getFieldValue("mat");\r
+ Material[] materials = null;\r
+ if (!ppMaterials.isNull()) {\r
+ List<Structure> materialStructures = ppMaterials.fetchData(dataRepository.getInputStream());\r
+ if (materialStructures != null && materialStructures.size() > 0) {\r
+ MaterialHelper materialHelper = dataRepository.getHelper(MaterialHelper.class);\r
+ materials = new Material[materialStructures.size()];\r
+ int i = 0;\r
+ for (Structure s : materialStructures) {\r
+ Material material = (Material) dataRepository.getLoadedFeature(s.getOldMemoryAddress(), LoadedFeatureDataType.LOADED_FEATURE);\r
+ if (material == null) {\r
+ material = materialHelper.toMaterial(s, dataRepository);\r
+ }\r
+ materials[i++] = material;\r
+ }\r
+ }\r
+ }\r
+ return materials;\r
+ }\r
+\r
+ /**\r
+ * This method converts rgb values to hsv values.\r
+ * \r
+ * @param rgb\r
+ * rgb values of the color\r
+ * @param hsv\r
+ * hsv values of a color (this table contains the result of the transformation)\r
+ */\r
+ public void rgbToHsv(float r, float g, float b, float[] hsv) {\r
+ float cmax = r;\r
+ float cmin = r;\r
+ cmax = g > cmax ? g : cmax;\r
+ cmin = g < cmin ? g : cmin;\r
+ cmax = b > cmax ? b : cmax;\r
+ cmin = b < cmin ? b : cmin;\r
+\r
+ hsv[2] = cmax; /* value */\r
+ if (cmax != 0.0) {\r
+ hsv[1] = (cmax - cmin) / cmax;\r
+ } else {\r
+ hsv[1] = 0.0f;\r
+ hsv[0] = 0.0f;\r
+ }\r
+ if (hsv[1] == 0.0) {\r
+ hsv[0] = -1.0f;\r
+ } else {\r
+ float cdelta = cmax - cmin;\r
+ float rc = (cmax - r) / cdelta;\r
+ float gc = (cmax - g) / cdelta;\r
+ float bc = (cmax - b) / cdelta;\r
+ if (r == cmax) {\r
+ hsv[0] = bc - gc;\r
+ } else if (g == cmax) {\r
+ hsv[0] = 2.0f + rc - bc;\r
+ } else {\r
+ hsv[0] = 4.0f + gc - rc;\r
+ }\r
+ hsv[0] *= 60.0f;\r
+ if (hsv[0] < 0.0f) {\r
+ hsv[0] += 360.0f;\r
+ }\r
+ }\r
+\r
+ hsv[0] /= 360.0f;\r
+ if (hsv[0] < 0.0f) {\r
+ hsv[0] = 0.0f;\r
+ }\r
+ }\r
+\r
+ /**\r
+ * This method converts rgb values to hsv values.\r
+ * \r
+ * @param h\r
+ * hue\r
+ * @param s\r
+ * saturation\r
+ * @param v\r
+ * value\r
+ * @param rgb\r
+ * rgb result vector (should have 3 elements)\r
+ */\r
+ public void hsvToRgb(float h, float s, float v, float[] rgb) {\r
+ h *= 360.0f;\r
+ if (s == 0.0) {\r
+ rgb[0] = rgb[1] = rgb[2] = v;\r
+ } else {\r
+ if (h == 360) {\r
+ h = 0;\r
+ } else {\r
+ h /= 60;\r
+ }\r
+ int i = (int) Math.floor(h);\r
+ float f = h - i;\r
+ float p = v * (1.0f - s);\r
+ float q = v * (1.0f - s * f);\r
+ float t = v * (1.0f - s * (1.0f - f));\r
+ switch (i) {\r
+ case 0:\r
+ rgb[0] = v;\r
+ rgb[1] = t;\r
+ rgb[2] = p;\r
+ break;\r
+ case 1:\r
+ rgb[0] = q;\r
+ rgb[1] = v;\r
+ rgb[2] = p;\r
+ break;\r
+ case 2:\r
+ rgb[0] = p;\r
+ rgb[1] = v;\r
+ rgb[2] = t;\r
+ break;\r
+ case 3:\r
+ rgb[0] = p;\r
+ rgb[1] = q;\r
+ rgb[2] = v;\r
+ break;\r
+ case 4:\r
+ rgb[0] = t;\r
+ rgb[1] = p;\r
+ rgb[2] = v;\r
+ break;\r
+ case 5:\r
+ rgb[0] = v;\r
+ rgb[1] = p;\r
+ rgb[2] = q;\r
+ break;\r
+ }\r
+ }\r
+ }\r
+\r
+ /**\r
+ * An interface used in calculating alpha mask during particles' texture calculations.\r
+ * @author Marcin Roguski (Kaelthas)\r
+ */\r
+ protected static interface AlphaMask {\r
+\r
+ /**\r
+ * This method sets the size of the texture's image.\r
+ * @param width\r
+ * the width of the image\r
+ * @param height\r
+ * the height of the image\r
+ */\r
+ void setImageSize(int width, int height);\r
+\r
+ /**\r
+ * This method returns the alpha value for the specified texture position.\r
+ * @param x\r
+ * the X coordinate of the texture position\r
+ * @param y\r
+ * the Y coordinate of the texture position\r
+ * @return the alpha value for the specified texture position\r
+ */\r
+ byte getAlpha(float x, float y);\r
+ }\r
}\r
* @author Marcin Roguski (Kaelthas)\r
*/\r
public class MeshHelper extends AbstractBlenderHelper {\r
- protected static final int MAXIMUM_WEIGHTS_PER_VERTEX = 4; // have no idea why 4, could someone please explain ?\r
-\r
- /**\r
- * This constructor parses the given blender version and stores the result. Some functionalities may differ in different blender\r
- * versions.\r
- * \r
- * @param blenderVersion\r
- * the version read from the blend file\r
- */\r
- public MeshHelper(String blenderVersion) {\r
- super(blenderVersion);\r
- }\r
-\r
- /**\r
- * This method reads converts the given structure into mesh. The given structure needs to be filled with the appropriate data.\r
- * \r
- * @param structure\r
- * the structure we read the mesh from\r
- * @return the mesh feature\r
- * @throws BlenderFileException\r
- */\r
- @SuppressWarnings("unchecked")\r
- public List<Geometry> toMesh(Structure structure, DataRepository dataRepository) throws BlenderFileException {\r
- List<Geometry> geometries = (List<Geometry>) dataRepository.getLoadedFeature(structure.getOldMemoryAddress(),\r
- LoadedFeatureDataType.LOADED_FEATURE);\r
- if (geometries != null) {\r
- List<Geometry> copiedGeometries = new ArrayList<Geometry>(geometries.size());\r
- for (Geometry geometry : geometries) {\r
- copiedGeometries.add(geometry.clone());\r
- }\r
- return copiedGeometries;\r
- }\r
-\r
- // helpers\r
- TextureHelper textureHelper = dataRepository.getHelper(TextureHelper.class);\r
-\r
- // reading mesh data\r
- String name = structure.getName();\r
-\r
- // reading vertices\r
- Vector3f[] vertices = this.getVertices(structure, dataRepository);\r
- int verticesAmount = vertices.length;\r
-\r
- // vertices Colors\r
- List<float[]> verticesColors = this.getVerticesColors(structure, dataRepository);\r
-\r
- // reading faces\r
- // the following map sorts faces by material number (because in jme Mesh can have only one material)\r
- Map<Integer, List<Integer>> meshesMap = new HashMap<Integer, List<Integer>>();\r
- Pointer pMFace = (Pointer) structure.getFieldValue("mface");\r
- List<Structure> mFaces = pMFace.fetchData(dataRepository.getInputStream());\r
-\r
- Pointer pMTFace = (Pointer) structure.getFieldValue("mtface");\r
- List<Vector2f> uvCoordinates = null;\r
- List<Structure> mtFaces = null;\r
-\r
- if (!pMTFace.isNull()) {\r
- mtFaces = pMTFace.fetchData(dataRepository.getInputStream());\r
- int facesAmount = ((Number) structure.getFieldValue("totface")).intValue();\r
- if (mtFaces.size() != facesAmount) {\r
- throw new BlenderFileException("The amount of faces uv coordinates is not equal to faces amount!");\r
- }\r
- uvCoordinates = new ArrayList<Vector2f>();// TODO: calculate the amount of coordinates if possible\r
- }\r
-\r
- // normalMap merges normals of faces that will be rendered smooth\r
- Map<Vector3f, Vector3f> normalMap = new HashMap<Vector3f, Vector3f>(verticesAmount);\r
-\r
- List<Vector3f> normalList = new ArrayList<Vector3f>();\r
- List<Vector3f> vertexList = new ArrayList<Vector3f>();\r
- Map<Integer, Texture> materialNumberToTexture = new HashMap<Integer, Texture>();// indicates if the material with the specified\r
- // number should have a texture attached\r
- // this map's key is the vertex index from 'vertices 'table and the value are indices from 'vertexList'\r
- // positions (it simply tells which vertex is referenced where in the result list)\r
- Map<Integer, List<Integer>> vertexReferenceMap = new HashMap<Integer, List<Integer>>(verticesAmount);\r
- int vertexColorIndex = 0;\r
- for (int i = 0; i < mFaces.size(); ++i) {\r
- Structure mFace = mFaces.get(i);\r
- boolean smooth = (((Number) mFace.getFieldValue("flag")).byteValue() & 0x01) != 0x00;\r
- DynamicArray<Number> uvs = null;\r
- boolean materialWithoutTextures = false;\r
- Pointer pImage = null;\r
- if (mtFaces != null) {\r
- Structure mtFace = mtFaces.get(i);\r
- pImage = (Pointer) mtFace.getFieldValue("tpage");\r
- materialWithoutTextures = pImage.isNull();\r
- // uvs always must be added wheater we have texture or not\r
- uvs = (DynamicArray<Number>) mtFace.getFieldValue("uv");\r
- uvCoordinates.add(new Vector2f(uvs.get(0, 0).floatValue(), uvs.get(0, 1).floatValue()));\r
- uvCoordinates.add(new Vector2f(uvs.get(1, 0).floatValue(), uvs.get(1, 1).floatValue()));\r
- uvCoordinates.add(new Vector2f(uvs.get(2, 0).floatValue(), uvs.get(2, 1).floatValue()));\r
- }\r
- int matNr = ((Number) mFace.getFieldValue("mat_nr")).intValue();\r
- Integer materialNumber = Integer.valueOf(materialWithoutTextures ? -1 * matNr - 1 : matNr);\r
- List<Integer> indexList = meshesMap.get(materialNumber);\r
- if (indexList == null) {\r
- indexList = new ArrayList<Integer>();\r
- meshesMap.put(materialNumber, indexList);\r
- }\r
- if (pImage != null && !pImage.isNull() && !materialNumberToTexture.containsKey(materialNumber)) {// attaching image to texture\r
- // (face can have UV's and\r
- // image whlie its material\r
- // may have no texture\r
- // attached)\r
- Texture texture = textureHelper.getTextureFromImage(pImage.fetchData(dataRepository.getInputStream()).get(0),\r
- dataRepository);\r
- if (texture != null) {\r
- materialNumberToTexture.put(materialNumber, texture);\r
- }\r
- }\r
-\r
- int v1 = ((Number) mFace.getFieldValue("v1")).intValue();\r
- int v2 = ((Number) mFace.getFieldValue("v2")).intValue();\r
- int v3 = ((Number) mFace.getFieldValue("v3")).intValue();\r
- int v4 = ((Number) mFace.getFieldValue("v4")).intValue();\r
-\r
- Vector3f n = FastMath.computeNormal(vertices[v1], vertices[v2], vertices[v3]);\r
- this.addNormal(n, normalMap, smooth, vertices[v1], vertices[v2], vertices[v3]);\r
- normalList.add(normalMap.get(vertices[v1]));\r
- normalList.add(normalMap.get(vertices[v2]));\r
- normalList.add(normalMap.get(vertices[v3]));\r
-\r
- this.appendVertexReference(v1, vertexList.size(), vertexReferenceMap);\r
- indexList.add(vertexList.size());\r
- vertexList.add(vertices[v1]);\r
-\r
- this.appendVertexReference(v2, vertexList.size(), vertexReferenceMap);\r
- indexList.add(vertexList.size());\r
- vertexList.add(vertices[v2]);\r
-\r
- this.appendVertexReference(v3, vertexList.size(), vertexReferenceMap);\r
- indexList.add(vertexList.size());\r
- vertexList.add(vertices[v3]);\r
-\r
- if (v4 > 0) {\r
- if (uvs != null) {\r
- uvCoordinates.add(new Vector2f(uvs.get(0, 0).floatValue(), uvs.get(0, 1).floatValue()));\r
- uvCoordinates.add(new Vector2f(uvs.get(2, 0).floatValue(), uvs.get(2, 1).floatValue()));\r
- uvCoordinates.add(new Vector2f(uvs.get(3, 0).floatValue(), uvs.get(3, 1).floatValue()));\r
- }\r
- this.appendVertexReference(v1, vertexList.size(), vertexReferenceMap);\r
- indexList.add(vertexList.size());\r
- vertexList.add(vertices[v1]);\r
-\r
- this.appendVertexReference(v3, vertexList.size(), vertexReferenceMap);\r
- indexList.add(vertexList.size());\r
- vertexList.add(vertices[v3]);\r
-\r
- this.appendVertexReference(v4, vertexList.size(), vertexReferenceMap);\r
- indexList.add(vertexList.size());\r
- vertexList.add(vertices[v4]);\r
-\r
- this.addNormal(n, normalMap, smooth, vertices[v4]);\r
- normalList.add(normalMap.get(vertices[v1]));\r
- normalList.add(normalMap.get(vertices[v3]));\r
- normalList.add(normalMap.get(vertices[v4]));\r
-\r
- if (verticesColors != null) {\r
- verticesColors.add(vertexColorIndex + 3, verticesColors.get(vertexColorIndex));\r
- verticesColors.add(vertexColorIndex + 4, verticesColors.get(vertexColorIndex + 2));\r
- }\r
- vertexColorIndex += 6;\r
- } else {\r
- if (verticesColors != null) {\r
- verticesColors.remove(vertexColorIndex + 3);\r
- vertexColorIndex += 3;\r
- }\r
- }\r
- }\r
- Vector3f[] normals = normalList.toArray(new Vector3f[normalList.size()]);\r
-\r
- // reading vertices groups (from the parent)\r
- Structure parent = dataRepository.peekParent();\r
- Structure defbase = (Structure) parent.getFieldValue("defbase");\r
- List<Structure> defs = defbase.evaluateListBase(dataRepository);\r
- String[] verticesGroups = new String[defs.size()];\r
- int defIndex = 0;\r
- for (Structure def : defs) {\r
- verticesGroups[defIndex++] = def.getFieldValue("name").toString();\r
- }\r
-\r
- // vertices bone weights and indices\r
- ArmatureHelper armatureHelper = dataRepository.getHelper(ArmatureHelper.class);\r
- Structure defBase = (Structure) parent.getFieldValue("defbase");\r
- Map<Integer, Integer> groupToBoneIndexMap = armatureHelper.getGroupToBoneIndexMap(defBase, dataRepository);\r
-\r
- VertexBuffer verticesWeights = null, verticesWeightsIndices = null;\r
- int[] bonesGroups = new int[] { 0 };\r
- VertexBuffer[] boneWeightsAndIndex = this.getBoneWeightAndIndexBuffer(structure, vertexList.size(), bonesGroups,\r
- vertexReferenceMap, groupToBoneIndexMap, dataRepository);\r
- verticesWeights = boneWeightsAndIndex[0];\r
- verticesWeightsIndices = boneWeightsAndIndex[1];\r
-\r
- // reading materials\r
- MaterialHelper materialHelper = dataRepository.getHelper(MaterialHelper.class);\r
- Material[] materials = null;\r
- Material[] nonTexturedMaterials = null;\r
- if ((dataRepository.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.MATERIALS) != 0) {\r
- materials = materialHelper.getMaterials(structure, dataRepository);\r
- nonTexturedMaterials = materials == null ? null : new Material[materials.length];// fill it when needed\r
- }\r
-\r
- // creating the result meshes\r
- geometries = new ArrayList<Geometry>(meshesMap.size());\r
-\r
- VertexBuffer verticesBuffer = new VertexBuffer(Type.Position);\r
- verticesBuffer.setupData(Usage.Stream, 3, Format.Float,\r
- BufferUtils.createFloatBuffer(vertexList.toArray(new Vector3f[vertexList.size()])));\r
-\r
- // initial vertex position (used with animation)\r
- VertexBuffer verticesBind = new VertexBuffer(Type.BindPosePosition);\r
- verticesBind.setupData(Usage.CpuOnly, 3, Format.Float, BufferUtils.clone(verticesBuffer.getData()));\r
-\r
- VertexBuffer normalsBuffer = new VertexBuffer(Type.Normal);\r
- normalsBuffer.setupData(Usage.Stream, 3, Format.Float, BufferUtils.createFloatBuffer(normals));\r
-\r
- // initial normals position (used with animation)\r
- VertexBuffer normalsBind = new VertexBuffer(Type.BindPoseNormal);\r
- normalsBind.setupData(Usage.CpuOnly, 3, Format.Float, BufferUtils.clone(normalsBuffer.getData()));\r
-\r
- VertexBuffer uvCoordsBuffer = null;\r
- if (uvCoordinates != null) {\r
- uvCoordsBuffer = new VertexBuffer(Type.TexCoord);\r
- uvCoordsBuffer.setupData(Usage.Static, 2, Format.Float,\r
- BufferUtils.createFloatBuffer(uvCoordinates.toArray(new Vector2f[uvCoordinates.size()])));\r
- }\r
-\r
- // generating meshes\r
- FloatBuffer verticesColorsBuffer = this.createFloatBuffer(verticesColors);\r
- for (Entry<Integer, List<Integer>> meshEntry : meshesMap.entrySet()) {\r
- Mesh mesh = new Mesh();\r
-\r
- // creating vertices indices for this mesh\r
- List<Integer> indexList = meshEntry.getValue();\r
- short[] indices = new short[indexList.size()];//TODO: check if the model doesn't have more than 32767 vertices\r
- for (int i = 0; i < indexList.size(); ++i) {//if yes then mesh.getVertices method must be changed to accept other than ShortBuffer\r
- indices[i] = indexList.get(i).shortValue();\r
- }\r
-\r
- // setting vertices\r
- mesh.setBuffer(Type.Index, 1, BufferUtils.createShortBuffer(indices));\r
- mesh.setBuffer(verticesBuffer);\r
- mesh.setBuffer(verticesBind);\r
-\r
- // setting vertices colors\r
- if (verticesColorsBuffer != null) {\r
- mesh.setBuffer(Type.Color, 4, verticesColorsBuffer);\r
- }\r
-\r
- // setting weights for bones\r
- if (verticesWeights != null) {\r
- mesh.setMaxNumWeights(bonesGroups[0]);\r
- mesh.setBuffer(verticesWeights);\r
- mesh.setBuffer(verticesWeightsIndices);\r
- }\r
-\r
- // setting faces' normals\r
- mesh.setBuffer(normalsBuffer);\r
- mesh.setBuffer(normalsBind);\r
-\r
- // setting uvCoords\r
- if (uvCoordsBuffer != null) {\r
- mesh.setBuffer(uvCoordsBuffer);\r
- }\r
-\r
- // creating the result\r
- Geometry geometry = new Geometry(name + (geometries.size() + 1), mesh);\r
- if (materials != null) {\r
- int materialNumber = meshEntry.getKey().intValue();\r
- Material material;\r
- if (materialNumber >= 0) {\r
- material = materials[materialNumber];\r
- if (materialNumberToTexture.containsKey(Integer.valueOf(materialNumber))) {\r
- if (material.getMaterialDef().getAssetName().contains("Lighting")) {\r
- if (!materialHelper.hasTexture(material, MaterialHelper.TEXTURE_TYPE_DIFFUSE)) {\r
- material = material.clone();\r
- material.setTexture(MaterialHelper.TEXTURE_TYPE_DIFFUSE,\r
- materialNumberToTexture.get(Integer.valueOf(materialNumber)));\r
- }\r
- } else {\r
- if (!materialHelper.hasTexture(material, MaterialHelper.TEXTURE_TYPE_COLOR)) {\r
- material = material.clone();\r
- material.setTexture(MaterialHelper.TEXTURE_TYPE_COLOR,\r
- materialNumberToTexture.get(Integer.valueOf(materialNumber)));\r
- }\r
- }\r
- }\r
- } else {\r
- materialNumber = -1 * (materialNumber + 1);\r
- if (nonTexturedMaterials[materialNumber] == null) {\r
- nonTexturedMaterials[materialNumber] = materialHelper.getNonTexturedMaterial(materials[materialNumber],\r
- TextureHelper.TEX_IMAGE);\r
- }\r
- material = nonTexturedMaterials[materialNumber];\r
- }\r
- geometry.setMaterial(material);\r
- } else {\r
- geometry.setMaterial(dataRepository.getDefaultMaterial());\r
- }\r
- geometries.add(geometry);\r
- }\r
- dataRepository.addLoadedFeatures(structure.getOldMemoryAddress(), structure.getName(), structure, geometries);\r
- return geometries;\r
- }\r
-\r
- /**\r
- * This method adds a normal to a normals' map. This map is used to merge normals of a vertor that should be rendered smooth.\r
- * \r
- * @param normalToAdd\r
- * a normal to be added\r
- * @param normalMap\r
- * merges normals of faces that will be rendered smooth; the key is the vertex and the value - its normal vector\r
- * @param smooth\r
- * the variable that indicates wheather to merge normals (creating the smooth mesh) or not\r
- * @param vertices\r
- * a list of vertices read from the blender file\r
- */\r
- protected void addNormal(Vector3f normalToAdd, Map<Vector3f, Vector3f> normalMap, boolean smooth, Vector3f... vertices) {\r
- for (Vector3f v : vertices) {\r
- Vector3f n = normalMap.get(v);\r
- if (!smooth || n == null) {\r
- normalMap.put(v, normalToAdd.clone());\r
- } else {\r
- n.addLocal(normalToAdd).normalizeLocal();\r
- }\r
- }\r
- }\r
-\r
- /**\r
- * This method fills the vertex reference map. The vertices are loaded once and referenced many times in the model. This map is created\r
- * to tell where the basic vertices are referenced in the result vertex lists. The key of the map is the basic vertex index, and its key\r
- * - the reference indices list.\r
- * \r
- * @param basicVertexIndex\r
- * the index of the vertex from its basic table\r
- * @param resultIndex\r
- * the index of the vertex in its result vertex list\r
- * @param vertexReferenceMap\r
- * the reference map\r
- */\r
- protected void appendVertexReference(int basicVertexIndex, int resultIndex, Map<Integer, List<Integer>> vertexReferenceMap) {\r
- List<Integer> referenceList = vertexReferenceMap.get(Integer.valueOf(basicVertexIndex));\r
- if (referenceList == null) {\r
- referenceList = new ArrayList<Integer>();\r
- vertexReferenceMap.put(Integer.valueOf(basicVertexIndex), referenceList);\r
- }\r
- referenceList.add(Integer.valueOf(resultIndex));\r
- }\r
-\r
- /**\r
- * This method returns the vertices colors. Each vertex is stored in float[4] array.\r
- * \r
- * @param meshStructure\r
- * the structure containing the mesh data\r
- * @param dataRepository\r
- * the data repository\r
- * @return a list of vertices colors, each color belongs to a single vertex\r
- * @throws BlenderFileException\r
- * this exception is thrown when the blend file structure is somehow invalid or corrupted\r
- */\r
- public List<float[]> getVerticesColors(Structure meshStructure, DataRepository dataRepository) throws BlenderFileException {\r
- Pointer pMCol = (Pointer) meshStructure.getFieldValue("mcol");\r
- List<float[]> verticesColors = null;\r
- List<Structure> mCol = null;\r
- if (!pMCol.isNull()) {\r
- verticesColors = new LinkedList<float[]>();\r
- mCol = pMCol.fetchData(dataRepository.getInputStream());\r
- for (Structure color : mCol) {\r
- float r = ((Number) color.getFieldValue("r")).byteValue() / 256.0f;\r
- float g = ((Number) color.getFieldValue("g")).byteValue() / 256.0f;\r
- float b = ((Number) color.getFieldValue("b")).byteValue() / 256.0f;\r
- float a = ((Number) color.getFieldValue("a")).byteValue() / 256.0f;\r
- verticesColors.add(new float[] { b, g, r, a });\r
- }\r
- }\r
- return verticesColors;\r
- }\r
-\r
- /**\r
- * This method returns the vertices.\r
- * \r
- * @param meshStructure\r
- * the structure containing the mesh data\r
- * @param dataRepository\r
- * the data repository\r
- * @return a list of vertices colors, each color belongs to a single vertex\r
- * @throws BlenderFileException\r
- * this exception is thrown when the blend file structure is somehow invalid or corrupted\r
- */\r
- @SuppressWarnings("unchecked")\r
- public Vector3f[] getVertices(Structure meshStructure, DataRepository dataRepository) throws BlenderFileException {\r
- int verticesAmount = ((Number) meshStructure.getFieldValue("totvert")).intValue();\r
- Vector3f[] vertices = new Vector3f[verticesAmount];\r
- Pointer pMVert = (Pointer) meshStructure.getFieldValue("mvert");\r
- List<Structure> mVerts = pMVert.fetchData(dataRepository.getInputStream());\r
- for (int i = 0; i < verticesAmount; ++i) {\r
- DynamicArray<Number> coordinates = (DynamicArray<Number>) mVerts.get(i).getFieldValue("co");\r
- vertices[i] = new Vector3f(coordinates.get(0).floatValue(), coordinates.get(1).floatValue(), coordinates.get(2).floatValue());\r
- }\r
- return vertices;\r
- }\r
-\r
- /**\r
- * This method returns an array of size 2. The first element is a vertex buffer holding bone weights for every vertex in the model. The\r
- * second element is a vertex buffer holding bone indices for vertices (the indices of bones the vertices are assigned to).\r
- * \r
- * @param meshStructure\r
- * the mesh structure object\r
- * @param vertexListSize\r
- * a number of vertices in the model\r
- * @param bonesGroups\r
- * this is an output parameter, it should be a one-sized array; the maximum amount of weights per vertex (up to\r
- * MAXIMUM_WEIGHTS_PER_VERTEX) is stored there\r
- * @param vertexReferenceMap\r
- * this reference map allows to map the original vertices read from blender to vertices that are really in the model; one\r
- * vertex may appear several times in the result model\r
- * @param groupToBoneIndexMap\r
- * this object maps the group index (to which a vertices in blender belong) to bone index of the model\r
- * @param dataRepository\r
- * the data repository\r
- * @return arrays of vertices weights and their bone indices and (as an outpot parameter) the maximum amount of weights for a vertex\r
- * @throws BlenderFileException\r
- * this exception is thrown when the blend file structure is somehow invalid or corrupted\r
- */\r
- public VertexBuffer[] getBoneWeightAndIndexBuffer(Structure meshStructure, int vertexListSize, int[] bonesGroups,\r
- Map<Integer, List<Integer>> vertexReferenceMap, Map<Integer, Integer> groupToBoneIndexMap, DataRepository dataRepository)\r
- throws BlenderFileException {\r
- Pointer pDvert = (Pointer) meshStructure.getFieldValue("dvert");// dvert = DeformVERTices\r
- FloatBuffer weightsFloatData = BufferUtils.createFloatBuffer(vertexListSize * MAXIMUM_WEIGHTS_PER_VERTEX);\r
- ByteBuffer indicesData = BufferUtils.createByteBuffer(vertexListSize * MAXIMUM_WEIGHTS_PER_VERTEX);\r
- if (!pDvert.isNull()) {// assigning weights and bone indices\r
- List<Structure> dverts = pDvert.fetchData(dataRepository.getInputStream());// dverts.size() == verticesAmount (one dvert per\r
- // vertex in blender)\r
- int vertexIndex = 0;\r
- for (Structure dvert : dverts) {\r
- int totweight = ((Number) dvert.getFieldValue("totweight")).intValue();// total amount of weights assignet to the vertex\r
- // (max. 4 in JME)\r
- Pointer pDW = (Pointer) dvert.getFieldValue("dw");\r
- List<Integer> vertexIndices = vertexReferenceMap.get(Integer.valueOf(vertexIndex));// we fetch the referenced vertices here\r
- if (totweight > 0 && !pDW.isNull()) {// pDW should never be null here, but I check it just in case :)\r
- int weightIndex = 0;\r
- List<Structure> dw = pDW.fetchData(dataRepository.getInputStream());\r
- for (Structure deformWeight : dw) {\r
- Integer boneIndex = groupToBoneIndexMap.get(((Number) deformWeight.getFieldValue("def_nr")).intValue());\r
- if (boneIndex != null) {// null here means that we came accross group that has no bone attached to\r
- float weight = ((Number) deformWeight.getFieldValue("weight")).floatValue();\r
- if (weight == 0.0f) {\r
- weight = 1;\r
- boneIndex = Integer.valueOf(0);\r
- }\r
- // we apply the weight to all referenced vertices\r
- for (Integer index : vertexIndices) {\r
- // all indices are always assigned to 0-indexed bone\r
- // weightsFloatData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX + weightIndex, 1.0f);\r
- // indicesData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX + weightIndex, (byte)0);\r
- // if(weight != 0.0f) {\r
- weightsFloatData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX + weightIndex, weight);\r
- indicesData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX + weightIndex, boneIndex.byteValue());\r
- // }\r
- }\r
- }\r
- ++weightIndex;\r
- }\r
- } else {\r
- for (Integer index : vertexIndices) {\r
- weightsFloatData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX, 1.0f);\r
- indicesData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX, (byte) 0);\r
- }\r
- }\r
- ++vertexIndex;\r
- }\r
- } else {\r
- // always bind all vertices to 0-indexed bone\r
- // this bone makes the model look normally if vertices have no bone assigned\r
- // and it is used in object animation, so if we come accross object animation\r
- // we can use the 0-indexed bone for this\r
- for (List<Integer> vertexIndexList : vertexReferenceMap.values()) {\r
- // we apply the weight to all referenced vertices\r
- for (Integer index : vertexIndexList) {\r
- weightsFloatData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX, 1.0f);\r
- indicesData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX, (byte) 0);\r
- }\r
- }\r
- }\r
-\r
- bonesGroups[0] = this.endBoneAssigns(vertexListSize, weightsFloatData);\r
- VertexBuffer verticesWeights = new VertexBuffer(Type.BoneWeight);\r
- verticesWeights.setupData(Usage.CpuOnly, bonesGroups[0], Format.Float, weightsFloatData);\r
-\r
- VertexBuffer verticesWeightsIndices = new VertexBuffer(Type.BoneIndex);\r
- verticesWeightsIndices.setupData(Usage.CpuOnly, bonesGroups[0], Format.UnsignedByte, indicesData);\r
- return new VertexBuffer[] { verticesWeights, verticesWeightsIndices };\r
- }\r
-\r
- /**\r
- * Normalizes weights if needed and finds largest amount of weights used for all vertices in the buffer.\r
- */\r
- protected int endBoneAssigns(int vertCount, FloatBuffer weightsFloatData) {\r
- int maxWeightsPerVert = 0;\r
- weightsFloatData.rewind();\r
- for (int v = 0; v < vertCount; ++v) {\r
- float w0 = weightsFloatData.get(), w1 = weightsFloatData.get(), w2 = weightsFloatData.get(), w3 = weightsFloatData.get();\r
-\r
- if (w3 != 0) {\r
- maxWeightsPerVert = Math.max(maxWeightsPerVert, 4);\r
- } else if (w2 != 0) {\r
- maxWeightsPerVert = Math.max(maxWeightsPerVert, 3);\r
- } else if (w1 != 0) {\r
- maxWeightsPerVert = Math.max(maxWeightsPerVert, 2);\r
- } else if (w0 != 0) {\r
- maxWeightsPerVert = Math.max(maxWeightsPerVert, 1);\r
- }\r
-\r
- float sum = w0 + w1 + w2 + w3;\r
- if (sum != 1f && sum != 0.0f) {\r
- weightsFloatData.position(weightsFloatData.position() - 4);\r
- // compute new vals based on sum\r
- float sumToB = 1f / sum;\r
- weightsFloatData.put(w0 * sumToB);\r
- weightsFloatData.put(w1 * sumToB);\r
- weightsFloatData.put(w2 * sumToB);\r
- weightsFloatData.put(w3 * sumToB);\r
- }\r
- }\r
- weightsFloatData.rewind();\r
-\r
- // mesh.setMaxNumWeights(maxWeightsPerVert);\r
- return maxWeightsPerVert;\r
- }\r
+\r
+ protected static final int MAXIMUM_WEIGHTS_PER_VERTEX = 4; // have no idea why 4, could someone please explain ?\r
+\r
+ /**\r
+ * This constructor parses the given blender version and stores the result. Some functionalities may differ in different blender\r
+ * versions.\r
+ * \r
+ * @param blenderVersion\r
+ * the version read from the blend file\r
+ */\r
+ public MeshHelper(String blenderVersion) {\r
+ super(blenderVersion);\r
+ }\r
+\r
+ /**\r
+ * This method reads converts the given structure into mesh. The given structure needs to be filled with the appropriate data.\r
+ * \r
+ * @param structure\r
+ * the structure we read the mesh from\r
+ * @return the mesh feature\r
+ * @throws BlenderFileException\r
+ */\r
+ @SuppressWarnings("unchecked")\r
+ public List<Geometry> toMesh(Structure structure, DataRepository dataRepository) throws BlenderFileException {\r
+ List<Geometry> geometries = (List<Geometry>) dataRepository.getLoadedFeature(structure.getOldMemoryAddress(),\r
+ LoadedFeatureDataType.LOADED_FEATURE);\r
+ if (geometries != null) {\r
+ List<Geometry> copiedGeometries = new ArrayList<Geometry>(geometries.size());\r
+ for (Geometry geometry : geometries) {\r
+ copiedGeometries.add(geometry.clone());\r
+ }\r
+ return copiedGeometries;\r
+ }\r
+\r
+ // helpers\r
+ TextureHelper textureHelper = dataRepository.getHelper(TextureHelper.class);\r
+\r
+ // reading mesh data\r
+ String name = structure.getName();\r
+\r
+ // reading vertices\r
+ Vector3f[] vertices = this.getVertices(structure, dataRepository);\r
+ int verticesAmount = vertices.length;\r
+\r
+ // vertices Colors\r
+ List<float[]> verticesColors = this.getVerticesColors(structure, dataRepository);\r
+\r
+ // reading faces\r
+ // the following map sorts faces by material number (because in jme Mesh can have only one material)\r
+ Map<Integer, List<Integer>> meshesMap = new HashMap<Integer, List<Integer>>();\r
+ Pointer pMFace = (Pointer) structure.getFieldValue("mface");\r
+ List<Structure> mFaces = pMFace.fetchData(dataRepository.getInputStream());\r
+\r
+ Pointer pMTFace = (Pointer) structure.getFieldValue("mtface");\r
+ List<Vector2f> uvCoordinates = null;\r
+ List<Structure> mtFaces = null;\r
+\r
+ if (!pMTFace.isNull()) {\r
+ mtFaces = pMTFace.fetchData(dataRepository.getInputStream());\r
+ int facesAmount = ((Number) structure.getFieldValue("totface")).intValue();\r
+ if (mtFaces.size() != facesAmount) {\r
+ throw new BlenderFileException("The amount of faces uv coordinates is not equal to faces amount!");\r
+ }\r
+ uvCoordinates = new ArrayList<Vector2f>();// TODO: calculate the amount of coordinates if possible\r
+ }\r
+\r
+ // normalMap merges normals of faces that will be rendered smooth\r
+ Map<Vector3f, Vector3f> normalMap = new HashMap<Vector3f, Vector3f>(verticesAmount);\r
+\r
+ List<Vector3f> normalList = new ArrayList<Vector3f>();\r
+ List<Vector3f> vertexList = new ArrayList<Vector3f>();\r
+ Map<Integer, Texture> materialNumberToTexture = new HashMap<Integer, Texture>();// indicates if the material with the specified\r
+ // number should have a texture attached\r
+ // this map's key is the vertex index from 'vertices 'table and the value are indices from 'vertexList'\r
+ // positions (it simply tells which vertex is referenced where in the result list)\r
+ Map<Integer, List<Integer>> vertexReferenceMap = new HashMap<Integer, List<Integer>>(verticesAmount);\r
+ int vertexColorIndex = 0;\r
+ for (int i = 0; i < mFaces.size(); ++i) {\r
+ Structure mFace = mFaces.get(i);\r
+ boolean smooth = (((Number) mFace.getFieldValue("flag")).byteValue() & 0x01) != 0x00;\r
+ DynamicArray<Number> uvs = null;\r
+ boolean materialWithoutTextures = false;\r
+ Pointer pImage = null;\r
+ if (mtFaces != null) {\r
+ Structure mtFace = mtFaces.get(i);\r
+ pImage = (Pointer) mtFace.getFieldValue("tpage");\r
+ materialWithoutTextures = pImage.isNull();\r
+ // uvs always must be added wheater we have texture or not\r
+ uvs = (DynamicArray<Number>) mtFace.getFieldValue("uv");\r
+ uvCoordinates.add(new Vector2f(uvs.get(0, 0).floatValue(), uvs.get(0, 1).floatValue()));\r
+ uvCoordinates.add(new Vector2f(uvs.get(1, 0).floatValue(), uvs.get(1, 1).floatValue()));\r
+ uvCoordinates.add(new Vector2f(uvs.get(2, 0).floatValue(), uvs.get(2, 1).floatValue()));\r
+ }\r
+ int matNr = ((Number) mFace.getFieldValue("mat_nr")).intValue();\r
+ Integer materialNumber = Integer.valueOf(materialWithoutTextures ? -1 * matNr - 1 : matNr);\r
+ List<Integer> indexList = meshesMap.get(materialNumber);\r
+ if (indexList == null) {\r
+ indexList = new ArrayList<Integer>();\r
+ meshesMap.put(materialNumber, indexList);\r
+ }\r
+ if (pImage != null && !pImage.isNull() && !materialNumberToTexture.containsKey(materialNumber)) {// attaching image to texture\r
+ // (face can have UV's and\r
+ // image whlie its material\r
+ // may have no texture\r
+ // attached)\r
+ Texture texture = textureHelper.getTextureFromImage(pImage.fetchData(dataRepository.getInputStream()).get(0),\r
+ dataRepository);\r
+ if (texture != null) {\r
+ materialNumberToTexture.put(materialNumber, texture);\r
+ }\r
+ }\r
+\r
+ int v1 = ((Number) mFace.getFieldValue("v1")).intValue();\r
+ int v2 = ((Number) mFace.getFieldValue("v2")).intValue();\r
+ int v3 = ((Number) mFace.getFieldValue("v3")).intValue();\r
+ int v4 = ((Number) mFace.getFieldValue("v4")).intValue();\r
+\r
+ Vector3f n = FastMath.computeNormal(vertices[v1], vertices[v2], vertices[v3]);\r
+ this.addNormal(n, normalMap, smooth, vertices[v1], vertices[v2], vertices[v3]);\r
+ normalList.add(normalMap.get(vertices[v1]));\r
+ normalList.add(normalMap.get(vertices[v2]));\r
+ normalList.add(normalMap.get(vertices[v3]));\r
+\r
+ this.appendVertexReference(v1, vertexList.size(), vertexReferenceMap);\r
+ indexList.add(vertexList.size());\r
+ vertexList.add(vertices[v1]);\r
+\r
+ this.appendVertexReference(v2, vertexList.size(), vertexReferenceMap);\r
+ indexList.add(vertexList.size());\r
+ vertexList.add(vertices[v2]);\r
+\r
+ this.appendVertexReference(v3, vertexList.size(), vertexReferenceMap);\r
+ indexList.add(vertexList.size());\r
+ vertexList.add(vertices[v3]);\r
+\r
+ if (v4 > 0) {\r
+ if (uvs != null) {\r
+ uvCoordinates.add(new Vector2f(uvs.get(0, 0).floatValue(), uvs.get(0, 1).floatValue()));\r
+ uvCoordinates.add(new Vector2f(uvs.get(2, 0).floatValue(), uvs.get(2, 1).floatValue()));\r
+ uvCoordinates.add(new Vector2f(uvs.get(3, 0).floatValue(), uvs.get(3, 1).floatValue()));\r
+ }\r
+ this.appendVertexReference(v1, vertexList.size(), vertexReferenceMap);\r
+ indexList.add(vertexList.size());\r
+ vertexList.add(vertices[v1]);\r
+\r
+ this.appendVertexReference(v3, vertexList.size(), vertexReferenceMap);\r
+ indexList.add(vertexList.size());\r
+ vertexList.add(vertices[v3]);\r
+\r
+ this.appendVertexReference(v4, vertexList.size(), vertexReferenceMap);\r
+ indexList.add(vertexList.size());\r
+ vertexList.add(vertices[v4]);\r
+\r
+ this.addNormal(n, normalMap, smooth, vertices[v4]);\r
+ normalList.add(normalMap.get(vertices[v1]));\r
+ normalList.add(normalMap.get(vertices[v3]));\r
+ normalList.add(normalMap.get(vertices[v4]));\r
+\r
+ if (verticesColors != null) {\r
+ verticesColors.add(vertexColorIndex + 3, verticesColors.get(vertexColorIndex));\r
+ verticesColors.add(vertexColorIndex + 4, verticesColors.get(vertexColorIndex + 2));\r
+ }\r
+ vertexColorIndex += 6;\r
+ } else {\r
+ if (verticesColors != null) {\r
+ verticesColors.remove(vertexColorIndex + 3);\r
+ vertexColorIndex += 3;\r
+ }\r
+ }\r
+ }\r
+ Vector3f[] normals = normalList.toArray(new Vector3f[normalList.size()]);\r
+\r
+ // reading vertices groups (from the parent)\r
+ Structure parent = dataRepository.peekParent();\r
+ Structure defbase = (Structure) parent.getFieldValue("defbase");\r
+ List<Structure> defs = defbase.evaluateListBase(dataRepository);\r
+ String[] verticesGroups = new String[defs.size()];\r
+ int defIndex = 0;\r
+ for (Structure def : defs) {\r
+ verticesGroups[defIndex++] = def.getFieldValue("name").toString();\r
+ }\r
+\r
+ // vertices bone weights and indices\r
+ ArmatureHelper armatureHelper = dataRepository.getHelper(ArmatureHelper.class);\r
+ Structure defBase = (Structure) parent.getFieldValue("defbase");\r
+ Map<Integer, Integer> groupToBoneIndexMap = armatureHelper.getGroupToBoneIndexMap(defBase, dataRepository);\r
+\r
+ VertexBuffer verticesWeights = null, verticesWeightsIndices = null;\r
+ int[] bonesGroups = new int[]{0};\r
+ VertexBuffer[] boneWeightsAndIndex = this.getBoneWeightAndIndexBuffer(structure, vertexList.size(), bonesGroups,\r
+ vertexReferenceMap, groupToBoneIndexMap, dataRepository);\r
+ verticesWeights = boneWeightsAndIndex[0];\r
+ verticesWeightsIndices = boneWeightsAndIndex[1];\r
+\r
+ // reading materials\r
+ MaterialHelper materialHelper = dataRepository.getHelper(MaterialHelper.class);\r
+ Material[] materials = null;\r
+ Material[] nonTexturedMaterials = null;\r
+ if ((dataRepository.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.MATERIALS) != 0) {\r
+ materials = materialHelper.getMaterials(structure, dataRepository);\r
+ nonTexturedMaterials = materials == null ? null : new Material[materials.length];// fill it when needed\r
+ }\r
+\r
+ // creating the result meshes\r
+ geometries = new ArrayList<Geometry>(meshesMap.size());\r
+\r
+ VertexBuffer verticesBuffer = new VertexBuffer(Type.Position);\r
+ verticesBuffer.setupData(Usage.Stream, 3, Format.Float,\r
+ BufferUtils.createFloatBuffer(vertexList.toArray(new Vector3f[vertexList.size()])));\r
+\r
+ // initial vertex position (used with animation)\r
+ VertexBuffer verticesBind = new VertexBuffer(Type.BindPosePosition);\r
+ verticesBind.setupData(Usage.CpuOnly, 3, Format.Float, BufferUtils.clone(verticesBuffer.getData()));\r
+\r
+ VertexBuffer normalsBuffer = new VertexBuffer(Type.Normal);\r
+ normalsBuffer.setupData(Usage.Stream, 3, Format.Float, BufferUtils.createFloatBuffer(normals));\r
+\r
+ // initial normals position (used with animation)\r
+ VertexBuffer normalsBind = new VertexBuffer(Type.BindPoseNormal);\r
+ normalsBind.setupData(Usage.CpuOnly, 3, Format.Float, BufferUtils.clone(normalsBuffer.getData()));\r
+\r
+ VertexBuffer uvCoordsBuffer = null;\r
+ if (uvCoordinates != null) {\r
+ uvCoordsBuffer = new VertexBuffer(Type.TexCoord);\r
+ uvCoordsBuffer.setupData(Usage.Static, 2, Format.Float,\r
+ BufferUtils.createFloatBuffer(uvCoordinates.toArray(new Vector2f[uvCoordinates.size()])));\r
+ }\r
+\r
+ // generating meshes\r
+ FloatBuffer verticesColorsBuffer = this.createFloatBuffer(verticesColors);\r
+ for (Entry<Integer, List<Integer>> meshEntry : meshesMap.entrySet()) {\r
+ Mesh mesh = new Mesh();\r
+\r
+ // creating vertices indices for this mesh\r
+ List<Integer> indexList = meshEntry.getValue();\r
+ short[] indices = new short[indexList.size()];//TODO: check if the model doesn't have more than 32767 vertices\r
+ for (int i = 0; i < indexList.size(); ++i) {//if yes then mesh.getVertices method must be changed to accept other than ShortBuffer\r
+ indices[i] = indexList.get(i).shortValue();\r
+ }\r
+\r
+ // setting vertices\r
+ mesh.setBuffer(Type.Index, 1, BufferUtils.createShortBuffer(indices));\r
+ mesh.setBuffer(verticesBuffer);\r
+ mesh.setBuffer(verticesBind);\r
+\r
+ // setting vertices colors\r
+ if (verticesColorsBuffer != null) {\r
+ mesh.setBuffer(Type.Color, 4, verticesColorsBuffer);\r
+ }\r
+\r
+ // setting weights for bones\r
+ if (verticesWeights != null) {\r
+ mesh.setMaxNumWeights(bonesGroups[0]);\r
+ mesh.setBuffer(verticesWeights);\r
+ mesh.setBuffer(verticesWeightsIndices);\r
+ }\r
+\r
+ // setting faces' normals\r
+ mesh.setBuffer(normalsBuffer);\r
+ mesh.setBuffer(normalsBind);\r
+\r
+ // setting uvCoords\r
+ if (uvCoordsBuffer != null) {\r
+ mesh.setBuffer(uvCoordsBuffer);\r
+ }\r
+\r
+ // creating the result\r
+ Geometry geometry = new Geometry(name + (geometries.size() + 1), mesh);\r
+ if (materials != null) {\r
+ int materialNumber = meshEntry.getKey().intValue();\r
+ Material material;\r
+ if (materialNumber >= 0) {\r
+ material = materials[materialNumber];\r
+ if (materialNumberToTexture.containsKey(Integer.valueOf(materialNumber))) {\r
+ if (material.getMaterialDef().getAssetName().contains("Lighting")) {\r
+ if (!materialHelper.hasTexture(material, MaterialHelper.TEXTURE_TYPE_DIFFUSE)) {\r
+ material = material.clone();\r
+ material.setTexture(MaterialHelper.TEXTURE_TYPE_DIFFUSE,\r
+ materialNumberToTexture.get(Integer.valueOf(materialNumber)));\r
+ }\r
+ } else {\r
+ if (!materialHelper.hasTexture(material, MaterialHelper.TEXTURE_TYPE_COLOR)) {\r
+ material = material.clone();\r
+ material.setTexture(MaterialHelper.TEXTURE_TYPE_COLOR,\r
+ materialNumberToTexture.get(Integer.valueOf(materialNumber)));\r
+ }\r
+ }\r
+ }\r
+ } else {\r
+ materialNumber = -1 * (materialNumber + 1);\r
+ if (nonTexturedMaterials[materialNumber] == null) {\r
+ nonTexturedMaterials[materialNumber] = materialHelper.getNonTexturedMaterial(materials[materialNumber],\r
+ TextureHelper.TEX_IMAGE);\r
+ }\r
+ material = nonTexturedMaterials[materialNumber];\r
+ }\r
+ geometry.setMaterial(material);\r
+ } else {\r
+ geometry.setMaterial(dataRepository.getDefaultMaterial());\r
+ }\r
+ geometries.add(geometry);\r
+ }\r
+ dataRepository.addLoadedFeatures(structure.getOldMemoryAddress(), structure.getName(), structure, geometries);\r
+ return geometries;\r
+ }\r
+\r
+ /**\r
+ * This method adds a normal to a normals' map. This map is used to merge normals of a vertor that should be rendered smooth.\r
+ * \r
+ * @param normalToAdd\r
+ * a normal to be added\r
+ * @param normalMap\r
+ * merges normals of faces that will be rendered smooth; the key is the vertex and the value - its normal vector\r
+ * @param smooth\r
+ * the variable that indicates wheather to merge normals (creating the smooth mesh) or not\r
+ * @param vertices\r
+ * a list of vertices read from the blender file\r
+ */\r
+ protected void addNormal(Vector3f normalToAdd, Map<Vector3f, Vector3f> normalMap, boolean smooth, Vector3f... vertices) {\r
+ for (Vector3f v : vertices) {\r
+ Vector3f n = normalMap.get(v);\r
+ if (!smooth || n == null) {\r
+ normalMap.put(v, normalToAdd.clone());\r
+ } else {\r
+ n.addLocal(normalToAdd).normalizeLocal();\r
+ }\r
+ }\r
+ }\r
+\r
+ /**\r
+ * This method fills the vertex reference map. The vertices are loaded once and referenced many times in the model. This map is created\r
+ * to tell where the basic vertices are referenced in the result vertex lists. The key of the map is the basic vertex index, and its key\r
+ * - the reference indices list.\r
+ * \r
+ * @param basicVertexIndex\r
+ * the index of the vertex from its basic table\r
+ * @param resultIndex\r
+ * the index of the vertex in its result vertex list\r
+ * @param vertexReferenceMap\r
+ * the reference map\r
+ */\r
+ protected void appendVertexReference(int basicVertexIndex, int resultIndex, Map<Integer, List<Integer>> vertexReferenceMap) {\r
+ List<Integer> referenceList = vertexReferenceMap.get(Integer.valueOf(basicVertexIndex));\r
+ if (referenceList == null) {\r
+ referenceList = new ArrayList<Integer>();\r
+ vertexReferenceMap.put(Integer.valueOf(basicVertexIndex), referenceList);\r
+ }\r
+ referenceList.add(Integer.valueOf(resultIndex));\r
+ }\r
+\r
+ /**\r
+ * This method returns the vertices colors. Each vertex is stored in float[4] array.\r
+ * \r
+ * @param meshStructure\r
+ * the structure containing the mesh data\r
+ * @param dataRepository\r
+ * the data repository\r
+ * @return a list of vertices colors, each color belongs to a single vertex\r
+ * @throws BlenderFileException\r
+ * this exception is thrown when the blend file structure is somehow invalid or corrupted\r
+ */\r
+ public List<float[]> getVerticesColors(Structure meshStructure, DataRepository dataRepository) throws BlenderFileException {\r
+ Pointer pMCol = (Pointer) meshStructure.getFieldValue("mcol");\r
+ List<float[]> verticesColors = null;\r
+ List<Structure> mCol = null;\r
+ if (!pMCol.isNull()) {\r
+ verticesColors = new LinkedList<float[]>();\r
+ mCol = pMCol.fetchData(dataRepository.getInputStream());\r
+ for (Structure color : mCol) {\r
+ float r = ((Number) color.getFieldValue("r")).byteValue() / 256.0f;\r
+ float g = ((Number) color.getFieldValue("g")).byteValue() / 256.0f;\r
+ float b = ((Number) color.getFieldValue("b")).byteValue() / 256.0f;\r
+ float a = ((Number) color.getFieldValue("a")).byteValue() / 256.0f;\r
+ verticesColors.add(new float[]{b, g, r, a});\r
+ }\r
+ }\r
+ return verticesColors;\r
+ }\r
+\r
+ /**\r
+ * This method returns the vertices.\r
+ * \r
+ * @param meshStructure\r
+ * the structure containing the mesh data\r
+ * @param dataRepository\r
+ * the data repository\r
+ * @return a list of vertices colors, each color belongs to a single vertex\r
+ * @throws BlenderFileException\r
+ * this exception is thrown when the blend file structure is somehow invalid or corrupted\r
+ */\r
+ @SuppressWarnings("unchecked")\r
+ public Vector3f[] getVertices(Structure meshStructure, DataRepository dataRepository) throws BlenderFileException {\r
+ int verticesAmount = ((Number) meshStructure.getFieldValue("totvert")).intValue();\r
+ Vector3f[] vertices = new Vector3f[verticesAmount];\r
+ Pointer pMVert = (Pointer) meshStructure.getFieldValue("mvert");\r
+ List<Structure> mVerts = pMVert.fetchData(dataRepository.getInputStream());\r
+ for (int i = 0; i < verticesAmount; ++i) {\r
+ DynamicArray<Number> coordinates = (DynamicArray<Number>) mVerts.get(i).getFieldValue("co");\r
+ vertices[i] = new Vector3f(coordinates.get(0).floatValue(), coordinates.get(1).floatValue(), coordinates.get(2).floatValue());\r
+ }\r
+ return vertices;\r
+ }\r
+\r
+ /**\r
+ * This method returns an array of size 2. The first element is a vertex buffer holding bone weights for every vertex in the model. The\r
+ * second element is a vertex buffer holding bone indices for vertices (the indices of bones the vertices are assigned to).\r
+ * \r
+ * @param meshStructure\r
+ * the mesh structure object\r
+ * @param vertexListSize\r
+ * a number of vertices in the model\r
+ * @param bonesGroups\r
+ * this is an output parameter, it should be a one-sized array; the maximum amount of weights per vertex (up to\r
+ * MAXIMUM_WEIGHTS_PER_VERTEX) is stored there\r
+ * @param vertexReferenceMap\r
+ * this reference map allows to map the original vertices read from blender to vertices that are really in the model; one\r
+ * vertex may appear several times in the result model\r
+ * @param groupToBoneIndexMap\r
+ * this object maps the group index (to which a vertices in blender belong) to bone index of the model\r
+ * @param dataRepository\r
+ * the data repository\r
+ * @return arrays of vertices weights and their bone indices and (as an outpot parameter) the maximum amount of weights for a vertex\r
+ * @throws BlenderFileException\r
+ * this exception is thrown when the blend file structure is somehow invalid or corrupted\r
+ */\r
+ public VertexBuffer[] getBoneWeightAndIndexBuffer(Structure meshStructure, int vertexListSize, int[] bonesGroups,\r
+ Map<Integer, List<Integer>> vertexReferenceMap, Map<Integer, Integer> groupToBoneIndexMap, DataRepository dataRepository)\r
+ throws BlenderFileException {\r
+ Pointer pDvert = (Pointer) meshStructure.getFieldValue("dvert");// dvert = DeformVERTices\r
+ FloatBuffer weightsFloatData = BufferUtils.createFloatBuffer(vertexListSize * MAXIMUM_WEIGHTS_PER_VERTEX);\r
+ ByteBuffer indicesData = BufferUtils.createByteBuffer(vertexListSize * MAXIMUM_WEIGHTS_PER_VERTEX);\r
+ if (!pDvert.isNull()) {// assigning weights and bone indices\r
+ List<Structure> dverts = pDvert.fetchData(dataRepository.getInputStream());// dverts.size() == verticesAmount (one dvert per\r
+ // vertex in blender)\r
+ int vertexIndex = 0;\r
+ for (Structure dvert : dverts) {\r
+ int totweight = ((Number) dvert.getFieldValue("totweight")).intValue();// total amount of weights assignet to the vertex\r
+ // (max. 4 in JME)\r
+ Pointer pDW = (Pointer) dvert.getFieldValue("dw");\r
+ List<Integer> vertexIndices = vertexReferenceMap.get(Integer.valueOf(vertexIndex));// we fetch the referenced vertices here\r
+ if (totweight > 0 && !pDW.isNull()) {// pDW should never be null here, but I check it just in case :)\r
+ int weightIndex = 0;\r
+ List<Structure> dw = pDW.fetchData(dataRepository.getInputStream());\r
+ for (Structure deformWeight : dw) {\r
+ Integer boneIndex = groupToBoneIndexMap.get(((Number) deformWeight.getFieldValue("def_nr")).intValue());\r
+ if (boneIndex != null) {// null here means that we came accross group that has no bone attached to\r
+ float weight = ((Number) deformWeight.getFieldValue("weight")).floatValue();\r
+ if (weight == 0.0f) {\r
+ weight = 1;\r
+ boneIndex = Integer.valueOf(0);\r
+ }\r
+ // we apply the weight to all referenced vertices\r
+ for (Integer index : vertexIndices) {\r
+ // all indices are always assigned to 0-indexed bone\r
+ // weightsFloatData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX + weightIndex, 1.0f);\r
+ // indicesData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX + weightIndex, (byte)0);\r
+ // if(weight != 0.0f) {\r
+ weightsFloatData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX + weightIndex, weight);\r
+ indicesData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX + weightIndex, boneIndex.byteValue());\r
+ // }\r
+ }\r
+ }\r
+ ++weightIndex;\r
+ }\r
+ } else {\r
+ for (Integer index : vertexIndices) {\r
+ weightsFloatData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX, 1.0f);\r
+ indicesData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX, (byte) 0);\r
+ }\r
+ }\r
+ ++vertexIndex;\r
+ }\r
+ } else {\r
+ // always bind all vertices to 0-indexed bone\r
+ // this bone makes the model look normally if vertices have no bone assigned\r
+ // and it is used in object animation, so if we come accross object animation\r
+ // we can use the 0-indexed bone for this\r
+ for (List<Integer> vertexIndexList : vertexReferenceMap.values()) {\r
+ // we apply the weight to all referenced vertices\r
+ for (Integer index : vertexIndexList) {\r
+ weightsFloatData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX, 1.0f);\r
+ indicesData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX, (byte) 0);\r
+ }\r
+ }\r
+ }\r
+\r
+ bonesGroups[0] = this.endBoneAssigns(vertexListSize, weightsFloatData);\r
+ VertexBuffer verticesWeights = new VertexBuffer(Type.BoneWeight);\r
+ verticesWeights.setupData(Usage.CpuOnly, bonesGroups[0], Format.Float, weightsFloatData);\r
+\r
+ VertexBuffer verticesWeightsIndices = new VertexBuffer(Type.BoneIndex);\r
+ verticesWeightsIndices.setupData(Usage.CpuOnly, bonesGroups[0], Format.UnsignedByte, indicesData);\r
+ return new VertexBuffer[]{verticesWeights, verticesWeightsIndices};\r
+ }\r
+\r
+ /**\r
+ * Normalizes weights if needed and finds largest amount of weights used for all vertices in the buffer.\r
+ */\r
+ protected int endBoneAssigns(int vertCount, FloatBuffer weightsFloatData) {\r
+ int maxWeightsPerVert = 0;\r
+ weightsFloatData.rewind();\r
+ for (int v = 0; v < vertCount; ++v) {\r
+ float w0 = weightsFloatData.get(), w1 = weightsFloatData.get(), w2 = weightsFloatData.get(), w3 = weightsFloatData.get();\r
+\r
+ if (w3 != 0) {\r
+ maxWeightsPerVert = Math.max(maxWeightsPerVert, 4);\r
+ } else if (w2 != 0) {\r
+ maxWeightsPerVert = Math.max(maxWeightsPerVert, 3);\r
+ } else if (w1 != 0) {\r
+ maxWeightsPerVert = Math.max(maxWeightsPerVert, 2);\r
+ } else if (w0 != 0) {\r
+ maxWeightsPerVert = Math.max(maxWeightsPerVert, 1);\r
+ }\r
+\r
+ float sum = w0 + w1 + w2 + w3;\r
+ if (sum != 1f && sum != 0.0f) {\r
+ weightsFloatData.position(weightsFloatData.position() - 4);\r
+ // compute new vals based on sum\r
+ float sumToB = 1f / sum;\r
+ weightsFloatData.put(w0 * sumToB);\r
+ weightsFloatData.put(w1 * sumToB);\r
+ weightsFloatData.put(w2 * sumToB);\r
+ weightsFloatData.put(w3 * sumToB);\r
+ }\r
+ }\r
+ weightsFloatData.rewind();\r
+\r
+ // mesh.setMaxNumWeights(maxWeightsPerVert);\r
+ return maxWeightsPerVert;\r
+ }\r
}\r
* @author Marcin Roguski\r
*/\r
public class ModifierHelper extends AbstractBlenderHelper {\r
- private static final Logger LOGGER = Logger.getLogger(ModifierHelper.class.getName());\r
- \r
- /**\r
- * This constructor parses the given blender version and stores the result. Some functionalities may differ in\r
- * different blender versions.\r
- * @param blenderVersion\r
- * the version read from the blend file\r
- */\r
- public ModifierHelper(String blenderVersion) {\r
- super(blenderVersion);\r
- }\r
-\r
- /**\r
- * This method applies modifier to the object.\r
- * @param node\r
- * the loaded object\r
- * @param modifier\r
- * the modifier to apply\r
- * @param dataRepository\r
- * the data repository\r
- * @return the node to whom the modifier was applied\r
- */\r
- public Node applyModifier(Node node, Modifier modifier, DataRepository dataRepository) {\r
- if(Modifier.ARMATURE_MODIFIER_DATA.equals(modifier.getType())) {\r
- return this.applyArmatureModifierData(node, modifier, dataRepository);\r
- } else if(Modifier.ARRAY_MODIFIER_DATA.equals(modifier.getType())) {\r
- return this.applyArrayModifierData(node, modifier, dataRepository);\r
- } else if(Modifier.PARTICLE_MODIFIER_DATA.equals(modifier.getType())) {\r
- return this.applyParticleSystemModifierData(node, modifier, dataRepository);\r
- } else {\r
- LOGGER.warning("Modifier: " + modifier.getType() + " not yet implemented!!!");\r
- return node;\r
- }\r
- }\r
- \r
- /**\r
- * This method reads the given object's modifiers.\r
- * @param objectStructure\r
- * the object structure\r
- * @param dataRepository\r
- * the data repository\r
- * @param converter\r
- * the converter object (in some cases we need to read an object first before loading the modifier)\r
- * @throws BlenderFileException\r
- * this exception is thrown when the blender file is somehow corrupted\r
- */\r
- @SuppressWarnings("unchecked")\r
- public void readModifiers(Structure objectStructure, DataRepository dataRepository) throws BlenderFileException {\r
- Structure modifiersListBase = (Structure)objectStructure.getFieldValue("modifiers");\r
- List<Structure> modifiers = modifiersListBase.evaluateListBase(dataRepository);\r
- for(Structure modifier : modifiers) {\r
- Object loadedModifier = null;\r
- Object modifierAdditionalData = null;\r
- if(Modifier.ARRAY_MODIFIER_DATA.equals(modifier.getType())) {//****************ARRAY MODIFIER\r
- Map<String, Object> params = new HashMap<String, Object>();\r
- \r
- Number fittype = (Number) modifier.getFieldValue("fit_type");\r
- params.put("fittype", fittype);\r
- switch(fittype.intValue()) {\r
- case 0://FIXED COUNT\r
- params.put("count", modifier.getFieldValue("count"));\r
- break;\r
- case 1://FIXED LENGTH\r
- params.put("length", modifier.getFieldValue("length"));\r
- break;\r
- case 2://FITCURVE\r
- //TODO: implement after loading curves is added; warning will be generated during modifier applying\r
- break;\r
- default:\r
- assert false : "Unknown array modifier fit type: " + fittype;\r
- }\r
- \r
- //offset parameters\r
- int offsettype = ((Number) modifier.getFieldValue("offset_type")).intValue();\r
- if((offsettype & 0x01) != 0) {//Constant offset\r
- DynamicArray<Number> offsetArray = (DynamicArray<Number>)modifier.getFieldValue("offset");\r
- float[] offset = new float[] {offsetArray.get(0).floatValue(), offsetArray.get(1).floatValue(), offsetArray.get(2).floatValue()};\r
- params.put("offset", offset);\r
- }\r
- if((offsettype & 0x02) != 0) {//Relative offset\r
- DynamicArray<Number> scaleArray = (DynamicArray<Number>)modifier.getFieldValue("scale");\r
- float[] scale = new float[] {scaleArray.get(0).floatValue(), scaleArray.get(1).floatValue(), scaleArray.get(2).floatValue()};\r
- params.put("scale", scale);\r
- }\r
- if((offsettype & 0x04) != 0) {//Object offset\r
- Pointer pOffsetObject = (Pointer)modifier.getFieldValue("offset_ob");\r
- if(!pOffsetObject.isNull()) {\r
- params.put("offsetob", pOffsetObject);\r
- }\r
- }\r
- \r
- //start cap and end cap\r
- Pointer pStartCap = (Pointer)modifier.getFieldValue("start_cap");\r
- if(!pStartCap.isNull()) {\r
- params.put("startcap", pStartCap);\r
- }\r
- Pointer pEndCap = (Pointer)modifier.getFieldValue("end_cap");\r
- if(!pEndCap.isNull()) {\r
- params.put("endcap", pEndCap);\r
- }\r
- loadedModifier = params;\r
- } else if(Modifier.ARMATURE_MODIFIER_DATA.equals(modifier.getType())) {//****************ARMATURE MODIFIER\r
- Pointer pArmatureObject = (Pointer)modifier.getFieldValue("object");\r
- if(!pArmatureObject.isNull()) {\r
- ObjectHelper objectHelper = dataRepository.getHelper(ObjectHelper.class);\r
- Structure armatureObject = (Structure)dataRepository.getLoadedFeature(pArmatureObject.getOldMemoryAddress(), LoadedFeatureDataType.LOADED_STRUCTURE);\r
- if(armatureObject == null) {//we check this first not to fetch the structure unnecessary\r
- armatureObject = pArmatureObject.fetchData(dataRepository.getInputStream()).get(0);\r
- objectHelper.toObject(armatureObject, dataRepository);\r
- }\r
- modifierAdditionalData = armatureObject.getOldMemoryAddress();\r
- ArmatureHelper armatureHelper = dataRepository.getHelper(ArmatureHelper.class);\r
-\r
- //changing bones matrices so that they fit the current object (taht is why we need a copy of a skeleton)\r
- Matrix4f armatureObjectMatrix = objectHelper.getTransformationMatrix(armatureObject);\r
- Matrix4f inverseMeshObjectMatrix = objectHelper.getTransformationMatrix(objectStructure).invert();\r
- Matrix4f additionalRootBoneTransformation = inverseMeshObjectMatrix.multLocal(armatureObjectMatrix);\r
- Bone[] bones = armatureHelper.buildBonesStructure(Long.valueOf(0L), additionalRootBoneTransformation);\r
-\r
- String objectName = objectStructure.getName();\r
- Set<String> animationNames = dataRepository.getBlenderKey().getAnimationNames(objectName);\r
- if(animationNames != null && animationNames.size() > 0) {\r
- ArrayList<BoneAnimation> animations = new ArrayList<BoneAnimation>();\r
- List<FileBlockHeader> actionHeaders = dataRepository.getFileBlocks(Integer.valueOf(FileBlockHeader.BLOCK_AC00));\r
- for(FileBlockHeader header : actionHeaders) {\r
- Structure actionStructure = header.getStructure(dataRepository);\r
- String actionName = actionStructure.getName();\r
- if(animationNames.contains(actionName)) {\r
- int[] animationFrames = dataRepository.getBlenderKey().getAnimationFrames(objectName, actionName);\r
- int fps = dataRepository.getBlenderKey().getFps();\r
- float start = (float)animationFrames[0] / (float)fps;\r
- float stop = (float)animationFrames[1] / (float)fps;\r
- BoneAnimation boneAnimation = new BoneAnimation(actionName, stop - start);\r
- boneAnimation.setTracks(armatureHelper.getTracks(actionStructure, dataRepository, objectName, actionName));\r
- animations.add(boneAnimation);\r
- }\r
- }\r
- loadedModifier = new AnimData(new Skeleton(bones), animations);\r
- }\r
- } else {\r
- LOGGER.warning("Unsupported modifier type: " + modifier.getType());\r
- }\r
- } else if(Modifier.PARTICLE_MODIFIER_DATA.equals(modifier.getType())) {//****************PARTICLES MODIFIER\r
- Pointer pParticleSystem = (Pointer) modifier.getFieldValue("psys");\r
- if(!pParticleSystem.isNull()) {\r
- ParticlesHelper particlesHelper = dataRepository.getHelper(ParticlesHelper.class);\r
- Structure particleSystem = pParticleSystem.fetchData(dataRepository.getInputStream()).get(0);\r
- loadedModifier = particlesHelper.toParticleEmitter(particleSystem, dataRepository);\r
- }\r
- }\r
- //adding modifier to the modifier's lists\r
- if(loadedModifier != null) {\r
- dataRepository.addModifier(objectStructure.getOldMemoryAddress(), modifier.getType(), loadedModifier, modifierAdditionalData);\r
- modifierAdditionalData = null;\r
- }\r
- }\r
- }\r
- \r
- /**\r
- * This method applies particles emitter to the given node.\r
- * @param node the particles emitter node\r
- * @param modifier the modifier containing the emitter data\r
- * @param dataRepository the data repository\r
- * @return node with particles' emitter applied\r
- */\r
- protected Node applyParticleSystemModifierData(Node node, Modifier modifier, DataRepository dataRepository) {\r
- MaterialHelper materialHelper = dataRepository.getHelper(MaterialHelper.class);\r
- ParticleEmitter emitter = (ParticleEmitter) modifier.getJmeModifierRepresentation();\r
- emitter = emitter.clone();\r
-\r
- //veryfying the alpha function for particles' texture\r
- Integer alphaFunction = MaterialHelper.ALPHA_MASK_HYPERBOLE;\r
- char nameSuffix = emitter.getName().charAt(emitter.getName().length()-1);\r
- if(nameSuffix=='B' || nameSuffix=='N') {\r
- alphaFunction = MaterialHelper.ALPHA_MASK_NONE;\r
- }\r
- //removing the type suffix from the name\r
- emitter.setName(emitter.getName().substring(0, emitter.getName().length()-1));\r
- \r
- //applying emitter shape\r
- EmitterShape emitterShape = emitter.getShape();\r
- List<Mesh> meshes = new ArrayList<Mesh>();\r
- for(Spatial spatial : node.getChildren()) {\r
- if(spatial instanceof Geometry) {\r
- Mesh mesh = ((Geometry) spatial).getMesh();\r
- if(mesh != null) {\r
- meshes.add(mesh);\r
- Material material = materialHelper.getParticlesMaterial(((Geometry) spatial).getMaterial(), alphaFunction, dataRepository);\r
- emitter.setMaterial(material);//TODO: divide into several pieces\r
- }\r
- }\r
- }\r
- if(meshes.size()>0 && emitterShape instanceof EmitterMeshVertexShape) {\r
- ((EmitterMeshVertexShape) emitterShape).setMeshes(meshes);\r
- }\r
- \r
- node.attachChild(emitter);\r
- return node;\r
- }\r
- \r
- /**\r
- * This method applies ArmatureModifierData to the loaded object.\r
- * @param node\r
- * the loaded object\r
- * @param modifier\r
- * the modifier to apply\r
- * @param dataRepository\r
- * the data repository\r
- * @return the node to whom the modifier was applied\r
- */\r
- protected Node applyArmatureModifierData(Node node, Modifier modifier, DataRepository dataRepository) {\r
- AnimData ad = (AnimData)modifier.getJmeModifierRepresentation();\r
- ArrayList<BoneAnimation> animList = ad.anims;\r
- Long modifierArmatureObject = (Long)modifier.getAdditionalData();\r
- if(animList != null && animList.size() > 0) {\r
- ConstraintHelper constraintHelper = dataRepository.getHelper(ConstraintHelper.class);\r
- Constraint[] constraints = constraintHelper.getConstraints(modifierArmatureObject);\r
- HashMap<String, BoneAnimation> anims = new HashMap<String, BoneAnimation>();\r
- for(int i = 0; i < animList.size(); ++i) {\r
- BoneAnimation boneAnimation = this.cloneBoneAnimation(animList.get(i));\r
-\r
- //baking constraints into animations\r
- if(constraints != null && constraints.length > 0) {\r
- for(Constraint constraint : constraints) {\r
- constraint.affectAnimation(ad.skeleton, boneAnimation);\r
- }\r
- }\r
-\r
- anims.put(boneAnimation.getName(), boneAnimation);\r
- }\r
-\r
- //getting meshes\r
- Mesh[] meshes = null;\r
- List<Mesh> meshesList = new ArrayList<Mesh>();\r
- List<Spatial> children = node.getChildren();\r
- for(Spatial child : children) {\r
- if(child instanceof Geometry) {\r
- meshesList.add(((Geometry)child).getMesh());\r
- }\r
- }\r
- if(meshesList.size() > 0) {\r
- meshes = meshesList.toArray(new Mesh[meshesList.size()]);\r
- }\r
-\r
- //applying the control to the node\r
- SkeletonControl skeletonControl = new SkeletonControl(meshes, ad.skeleton);\r
- AnimControl control = node.getControl(AnimControl.class);\r
- \r
- if(control == null) {\r
- control = new AnimControl(ad.skeleton);\r
- } else {\r
- //merging skeletons\r
- Skeleton controlSkeleton = control.getSkeleton();\r
- int boneIndexIncrease = controlSkeleton.getBoneCount();\r
- Skeleton skeleton = this.merge(controlSkeleton, ad.skeleton);\r
-\r
- //merging animations\r
- HashMap<String, BoneAnimation> animations = new HashMap<String, BoneAnimation>();\r
- for(String animationName : control.getAnimationNames()) {\r
- animations.put(animationName, control.getAnim(animationName));\r
- }\r
- for(Entry<String, BoneAnimation> animEntry : anims.entrySet()) {\r
- BoneAnimation ba = animEntry.getValue();\r
- for(int i = 0; i < ba.getTracks().length; ++i) {\r
- BoneTrack bt = ba.getTracks()[i];\r
- int newBoneIndex = bt.getTargetBoneIndex() + boneIndexIncrease;\r
- ba.getTracks()[i] = new BoneTrack(newBoneIndex, bt.getTimes(), bt.getTranslations(), bt.getRotations(), bt.getScales());\r
- }\r
- animations.put(animEntry.getKey(), animEntry.getValue());\r
- }\r
-\r
- //replacing the control\r
- node.removeControl(control);\r
- control = new AnimControl(skeleton);\r
- }\r
- control.setAnimations(anims);\r
- node.addControl(control);\r
- node.addControl(skeletonControl);\r
- }\r
- return node;\r
- }\r
- \r
- /**\r
- * This method applies the array modifier to the node.\r
- * @param node\r
- * the object the modifier will be applied to\r
- * @param modifier\r
- * the modifier to be applied\r
- * @param dataRepository\r
- * the data repository\r
- * @return object node with arry modifier applied\r
- */\r
- @SuppressWarnings("unchecked")\r
- protected Node applyArrayModifierData(Node node, Modifier modifier, DataRepository dataRepository) {\r
- Map<String, Object> modifierData = (Map<String, Object>) modifier.getJmeModifierRepresentation();\r
- int fittype = ((Number)modifierData.get("fittype")).intValue();\r
- float[] offset = (float[])modifierData.get("offset");\r
- if(offset==null) {//the node will be repeated several times in the same place\r
- offset = new float[] {0.0f, 0.0f, 0.0f};\r
- }\r
- float[] scale = (float[])modifierData.get("scale");\r
- if(scale==null) {//the node will be repeated several times in the same place\r
- scale = new float[] {0.0f, 0.0f, 0.0f};\r
- } else {\r
- //getting bounding box\r
- node.updateModelBound();\r
- BoundingVolume boundingVolume = node.getWorldBound();\r
- if(boundingVolume instanceof BoundingBox) {\r
- scale[0] *= ((BoundingBox) boundingVolume).getXExtent() * 2.0f;\r
- scale[1] *= ((BoundingBox) boundingVolume).getYExtent() * 2.0f;\r
- scale[2] *= ((BoundingBox) boundingVolume).getZExtent() * 2.0f;\r
- } else if(boundingVolume instanceof BoundingSphere) {\r
- float radius = ((BoundingSphere) boundingVolume).getRadius();\r
- scale[0] *= radius * 2.0f;\r
- scale[1] *= radius * 2.0f;\r
- scale[2] *= radius * 2.0f;\r
- } else {\r
- throw new IllegalStateException("Unknown bounding volume type: " + boundingVolume.getClass().getName());\r
- }\r
- }\r
- \r
- //adding object's offset\r
- float[] objectOffset = new float[] {0.0f, 0.0f, 0.0f};\r
- Pointer pOffsetObject = (Pointer) modifierData.get("offsetob");\r
- if(pOffsetObject!=null) {\r
- FileBlockHeader offsetObjectBlock = dataRepository.getFileBlock(pOffsetObject.getOldMemoryAddress());\r
- ObjectHelper objectHelper = dataRepository.getHelper(ObjectHelper.class);\r
- try {//we take the structure in case the object was not yet loaded\r
- Structure offsetStructure = offsetObjectBlock.getStructure(dataRepository);\r
- Vector3f translation = objectHelper.getTransformation(offsetStructure).getTranslation();\r
- objectOffset[0] = translation.x;\r
- objectOffset[1] = translation.y;\r
- objectOffset[2] = translation.z;\r
- } catch (BlenderFileException e) {\r
- LOGGER.warning("Problems in blender file structure! Object offset cannot be applied! The problem: " + e.getMessage());\r
- }\r
- }\r
- \r
- //getting start and end caps\r
- Node[] caps = new Node[] {null, null};\r
- Pointer[] pCaps = new Pointer[] {(Pointer) modifierData.get("startcap"), (Pointer) modifierData.get("endcap")};\r
- for(int i=0;i<pCaps.length;++i) {\r
- if(pCaps[i]!=null) {\r
- caps[i] = (Node) dataRepository.getLoadedFeature(pCaps[i].getOldMemoryAddress(), LoadedFeatureDataType.LOADED_FEATURE);\r
- if(caps[i]!=null) {\r
- caps[i] = (Node) caps[i].clone();\r
- } else {\r
- FileBlockHeader capBlock = dataRepository.getFileBlock(pOffsetObject.getOldMemoryAddress());\r
- try {//we take the structure in case the object was not yet loaded\r
- Structure capStructure = capBlock.getStructure(dataRepository);\r
- ObjectHelper objectHelper = dataRepository.getHelper(ObjectHelper.class);\r
- caps[i] = (Node) objectHelper.toObject(capStructure, dataRepository);\r
- if(caps[i]==null) {\r
- LOGGER.warning("Cap object '" + capStructure.getName() + "' couldn't be loaded!");\r
- }\r
- } catch (BlenderFileException e) {\r
- LOGGER.warning("Problems in blender file structure! Cap object cannot be applied! The problem: " + e.getMessage());\r
- }\r
- }\r
- }\r
- }\r
- \r
- Vector3f translationVector = new Vector3f(offset[0] + scale[0] + objectOffset[0], \r
- offset[1] + scale[1] + objectOffset[1], \r
- offset[2] + scale[2] + objectOffset[2]);\r
- \r
- //getting/calculating repeats amount\r
- int count = 0;\r
- if(fittype==0) {//Fixed count\r
- count = ((Number)modifierData.get("count")).intValue() - 1;\r
- } else if(fittype==1) {//Fixed length\r
- float length = ((Number)modifierData.get("length")).floatValue();\r
- if(translationVector.length()>0.0f) {\r
- count = (int)(length / translationVector.length()) - 1;\r
- }\r
- } else if(fittype==2) {//Fit curve\r
- LOGGER.warning("Fit curve mode in array modifier not yet implemented!");//TODO: implement fit curve\r
- } else {\r
- throw new IllegalStateException("Unknown fit type: " + fittype);\r
- }\r
- \r
- //adding translated nodes and caps\r
- if(count>0) {\r
- Node[] arrayNodes = new Node[count];\r
- Vector3f newTranslation = node.getLocalTranslation().clone();\r
- for(int i=0;i<count;++i) {\r
- newTranslation.addLocal(translationVector);\r
- Node nodeClone = (Node) node.clone();\r
- nodeClone.setLocalTranslation(newTranslation);\r
- arrayNodes[i] = nodeClone;\r
- }\r
- for(Node nodeClone : arrayNodes) {\r
- node.attachChild(nodeClone);\r
- }\r
- if(caps[0]!=null) {\r
- caps[0].getLocalTranslation().set(node.getLocalTranslation()).subtractLocal(translationVector);\r
- node.attachChild(caps[0]);\r
- }\r
- if(caps[1]!=null) {\r
- caps[1].getLocalTranslation().set(newTranslation).addLocal(translationVector);\r
- node.attachChild(caps[1]);\r
- }\r
- }\r
- return node;\r
- }\r
- \r
- /**\r
- * This class clones the bone animation data.\r
- * @param source\r
- * the source that is to be cloned\r
- * @return the copy of the given bone animation\r
- */\r
- protected BoneAnimation cloneBoneAnimation(BoneAnimation source) {\r
- BoneAnimation result = new BoneAnimation(source.getName(), source.getLength());\r
-\r
- //copying tracks and applying constraints\r
- BoneTrack[] sourceTracks = source.getTracks();\r
- BoneTrack[] boneTracks = new BoneTrack[sourceTracks.length];\r
- for(int i = 0; i < sourceTracks.length; ++i) {\r
- int tablesLength = sourceTracks[i].getTimes().length;\r
-\r
- Vector3f[] sourceTranslations = sourceTracks[i].getTranslations();\r
- Quaternion[] sourceRotations = sourceTracks[i].getRotations();\r
- Vector3f[] sourceScales = sourceTracks[i].getScales();\r
-\r
- Vector3f[] translations = new Vector3f[tablesLength];\r
- Quaternion[] rotations = new Quaternion[tablesLength];\r
- Vector3f[] scales = sourceScales == null ? null : new Vector3f[tablesLength];\r
- for(int j = 0; j < tablesLength; ++j) {\r
- translations[j] = sourceTranslations[j].clone();\r
- rotations[j] = sourceRotations[j].clone();\r
- if(sourceScales != null) {//only scales may not be applied\r
- scales[j] = sourceScales[j].clone();\r
- }\r
- }\r
- boneTracks[i] = new BoneTrack(sourceTracks[i].getTargetBoneIndex(), sourceTracks[i].getTimes(),//times do not change, no need to clone them,\r
- translations, rotations, scales);\r
- }\r
- result.setTracks(boneTracks);\r
- return result;\r
- }\r
- \r
- /**\r
- * This method merges two skeletons into one. I assume that each skeleton's 0-indexed bone is objectAnimationBone so\r
- * only one such bone should be placed in the result\r
- * @param s1\r
- * first skeleton\r
- * @param s2\r
- * second skeleton\r
- * @return merged skeleton\r
- */\r
- protected Skeleton merge(Skeleton s1, Skeleton s2) {\r
- List<Bone> bones = new ArrayList<Bone>(s1.getBoneCount() + s2.getBoneCount());\r
- for(int i = 0; i < s1.getBoneCount(); ++i) {\r
- bones.add(s1.getBone(i));\r
- }\r
- for(int i = 1; i < s2.getBoneCount(); ++i) {//ommit objectAnimationBone\r
- bones.add(s2.getBone(i));\r
- }\r
- return new Skeleton(bones.toArray(new Bone[bones.size()]));\r
- }\r
+\r
+ private static final Logger LOGGER = Logger.getLogger(ModifierHelper.class.getName());\r
+\r
+ /**\r
+ * This constructor parses the given blender version and stores the result. Some functionalities may differ in\r
+ * different blender versions.\r
+ * @param blenderVersion\r
+ * the version read from the blend file\r
+ */\r
+ public ModifierHelper(String blenderVersion) {\r
+ super(blenderVersion);\r
+ }\r
+\r
+ /**\r
+ * This method applies modifier to the object.\r
+ * @param node\r
+ * the loaded object\r
+ * @param modifier\r
+ * the modifier to apply\r
+ * @param dataRepository\r
+ * the data repository\r
+ * @return the node to whom the modifier was applied\r
+ */\r
+ public Node applyModifier(Node node, Modifier modifier, DataRepository dataRepository) {\r
+ if (Modifier.ARMATURE_MODIFIER_DATA.equals(modifier.getType())) {\r
+ return this.applyArmatureModifierData(node, modifier, dataRepository);\r
+ } else if (Modifier.ARRAY_MODIFIER_DATA.equals(modifier.getType())) {\r
+ return this.applyArrayModifierData(node, modifier, dataRepository);\r
+ } else if (Modifier.PARTICLE_MODIFIER_DATA.equals(modifier.getType())) {\r
+ return this.applyParticleSystemModifierData(node, modifier, dataRepository);\r
+ } else {\r
+ LOGGER.warning("Modifier: " + modifier.getType() + " not yet implemented!!!");\r
+ return node;\r
+ }\r
+ }\r
+\r
+ /**\r
+ * This method reads the given object's modifiers.\r
+ * @param objectStructure\r
+ * the object structure\r
+ * @param dataRepository\r
+ * the data repository\r
+ * @param converter\r
+ * the converter object (in some cases we need to read an object first before loading the modifier)\r
+ * @throws BlenderFileException\r
+ * this exception is thrown when the blender file is somehow corrupted\r
+ */\r
+ @SuppressWarnings("unchecked")\r
+ public void readModifiers(Structure objectStructure, DataRepository dataRepository) throws BlenderFileException {\r
+ Structure modifiersListBase = (Structure) objectStructure.getFieldValue("modifiers");\r
+ List<Structure> modifiers = modifiersListBase.evaluateListBase(dataRepository);\r
+ for (Structure modifier : modifiers) {\r
+ Object loadedModifier = null;\r
+ Object modifierAdditionalData = null;\r
+ if (Modifier.ARRAY_MODIFIER_DATA.equals(modifier.getType())) {//****************ARRAY MODIFIER\r
+ Map<String, Object> params = new HashMap<String, Object>();\r
+\r
+ Number fittype = (Number) modifier.getFieldValue("fit_type");\r
+ params.put("fittype", fittype);\r
+ switch (fittype.intValue()) {\r
+ case 0://FIXED COUNT\r
+ params.put("count", modifier.getFieldValue("count"));\r
+ break;\r
+ case 1://FIXED LENGTH\r
+ params.put("length", modifier.getFieldValue("length"));\r
+ break;\r
+ case 2://FITCURVE\r
+ //TODO: implement after loading curves is added; warning will be generated during modifier applying\r
+ break;\r
+ default:\r
+ assert false : "Unknown array modifier fit type: " + fittype;\r
+ }\r
+\r
+ //offset parameters\r
+ int offsettype = ((Number) modifier.getFieldValue("offset_type")).intValue();\r
+ if ((offsettype & 0x01) != 0) {//Constant offset\r
+ DynamicArray<Number> offsetArray = (DynamicArray<Number>) modifier.getFieldValue("offset");\r
+ float[] offset = new float[]{offsetArray.get(0).floatValue(), offsetArray.get(1).floatValue(), offsetArray.get(2).floatValue()};\r
+ params.put("offset", offset);\r
+ }\r
+ if ((offsettype & 0x02) != 0) {//Relative offset\r
+ DynamicArray<Number> scaleArray = (DynamicArray<Number>) modifier.getFieldValue("scale");\r
+ float[] scale = new float[]{scaleArray.get(0).floatValue(), scaleArray.get(1).floatValue(), scaleArray.get(2).floatValue()};\r
+ params.put("scale", scale);\r
+ }\r
+ if ((offsettype & 0x04) != 0) {//Object offset\r
+ Pointer pOffsetObject = (Pointer) modifier.getFieldValue("offset_ob");\r
+ if (!pOffsetObject.isNull()) {\r
+ params.put("offsetob", pOffsetObject);\r
+ }\r
+ }\r
+\r
+ //start cap and end cap\r
+ Pointer pStartCap = (Pointer) modifier.getFieldValue("start_cap");\r
+ if (!pStartCap.isNull()) {\r
+ params.put("startcap", pStartCap);\r
+ }\r
+ Pointer pEndCap = (Pointer) modifier.getFieldValue("end_cap");\r
+ if (!pEndCap.isNull()) {\r
+ params.put("endcap", pEndCap);\r
+ }\r
+ loadedModifier = params;\r
+ } else if (Modifier.ARMATURE_MODIFIER_DATA.equals(modifier.getType())) {//****************ARMATURE MODIFIER\r
+ Pointer pArmatureObject = (Pointer) modifier.getFieldValue("object");\r
+ if (!pArmatureObject.isNull()) {\r
+ ObjectHelper objectHelper = dataRepository.getHelper(ObjectHelper.class);\r
+ Structure armatureObject = (Structure) dataRepository.getLoadedFeature(pArmatureObject.getOldMemoryAddress(), LoadedFeatureDataType.LOADED_STRUCTURE);\r
+ if (armatureObject == null) {//we check this first not to fetch the structure unnecessary\r
+ armatureObject = pArmatureObject.fetchData(dataRepository.getInputStream()).get(0);\r
+ objectHelper.toObject(armatureObject, dataRepository);\r
+ }\r
+ modifierAdditionalData = armatureObject.getOldMemoryAddress();\r
+ ArmatureHelper armatureHelper = dataRepository.getHelper(ArmatureHelper.class);\r
+\r
+ //changing bones matrices so that they fit the current object (taht is why we need a copy of a skeleton)\r
+ Matrix4f armatureObjectMatrix = objectHelper.getTransformationMatrix(armatureObject);\r
+ Matrix4f inverseMeshObjectMatrix = objectHelper.getTransformationMatrix(objectStructure).invert();\r
+ Matrix4f additionalRootBoneTransformation = inverseMeshObjectMatrix.multLocal(armatureObjectMatrix);\r
+ Bone[] bones = armatureHelper.buildBonesStructure(Long.valueOf(0L), additionalRootBoneTransformation);\r
+\r
+ String objectName = objectStructure.getName();\r
+ Set<String> animationNames = dataRepository.getBlenderKey().getAnimationNames(objectName);\r
+ if (animationNames != null && animationNames.size() > 0) {\r
+ ArrayList<BoneAnimation> animations = new ArrayList<BoneAnimation>();\r
+ List<FileBlockHeader> actionHeaders = dataRepository.getFileBlocks(Integer.valueOf(FileBlockHeader.BLOCK_AC00));\r
+ for (FileBlockHeader header : actionHeaders) {\r
+ Structure actionStructure = header.getStructure(dataRepository);\r
+ String actionName = actionStructure.getName();\r
+ if (animationNames.contains(actionName)) {\r
+ int[] animationFrames = dataRepository.getBlenderKey().getAnimationFrames(objectName, actionName);\r
+ int fps = dataRepository.getBlenderKey().getFps();\r
+ float start = (float) animationFrames[0] / (float) fps;\r
+ float stop = (float) animationFrames[1] / (float) fps;\r
+ BoneAnimation boneAnimation = new BoneAnimation(actionName, stop - start);\r
+ boneAnimation.setTracks(armatureHelper.getTracks(actionStructure, dataRepository, objectName, actionName));\r
+ animations.add(boneAnimation);\r
+ }\r
+ }\r
+ loadedModifier = new AnimData(new Skeleton(bones), animations);\r
+ }\r
+ } else {\r
+ LOGGER.warning("Unsupported modifier type: " + modifier.getType());\r
+ }\r
+ } else if (Modifier.PARTICLE_MODIFIER_DATA.equals(modifier.getType())) {//****************PARTICLES MODIFIER\r
+ Pointer pParticleSystem = (Pointer) modifier.getFieldValue("psys");\r
+ if (!pParticleSystem.isNull()) {\r
+ ParticlesHelper particlesHelper = dataRepository.getHelper(ParticlesHelper.class);\r
+ Structure particleSystem = pParticleSystem.fetchData(dataRepository.getInputStream()).get(0);\r
+ loadedModifier = particlesHelper.toParticleEmitter(particleSystem, dataRepository);\r
+ }\r
+ }\r
+ //adding modifier to the modifier's lists\r
+ if (loadedModifier != null) {\r
+ dataRepository.addModifier(objectStructure.getOldMemoryAddress(), modifier.getType(), loadedModifier, modifierAdditionalData);\r
+ modifierAdditionalData = null;\r
+ }\r
+ }\r
+ }\r
+\r
+ /**\r
+ * This method applies particles emitter to the given node.\r
+ * @param node the particles emitter node\r
+ * @param modifier the modifier containing the emitter data\r
+ * @param dataRepository the data repository\r
+ * @return node with particles' emitter applied\r
+ */\r
+ protected Node applyParticleSystemModifierData(Node node, Modifier modifier, DataRepository dataRepository) {\r
+ MaterialHelper materialHelper = dataRepository.getHelper(MaterialHelper.class);\r
+ ParticleEmitter emitter = (ParticleEmitter) modifier.getJmeModifierRepresentation();\r
+ emitter = emitter.clone();\r
+\r
+ //veryfying the alpha function for particles' texture\r
+ Integer alphaFunction = MaterialHelper.ALPHA_MASK_HYPERBOLE;\r
+ char nameSuffix = emitter.getName().charAt(emitter.getName().length() - 1);\r
+ if (nameSuffix == 'B' || nameSuffix == 'N') {\r
+ alphaFunction = MaterialHelper.ALPHA_MASK_NONE;\r
+ }\r
+ //removing the type suffix from the name\r
+ emitter.setName(emitter.getName().substring(0, emitter.getName().length() - 1));\r
+\r
+ //applying emitter shape\r
+ EmitterShape emitterShape = emitter.getShape();\r
+ List<Mesh> meshes = new ArrayList<Mesh>();\r
+ for (Spatial spatial : node.getChildren()) {\r
+ if (spatial instanceof Geometry) {\r
+ Mesh mesh = ((Geometry) spatial).getMesh();\r
+ if (mesh != null) {\r
+ meshes.add(mesh);\r
+ Material material = materialHelper.getParticlesMaterial(((Geometry) spatial).getMaterial(), alphaFunction, dataRepository);\r
+ emitter.setMaterial(material);//TODO: divide into several pieces\r
+ }\r
+ }\r
+ }\r
+ if (meshes.size() > 0 && emitterShape instanceof EmitterMeshVertexShape) {\r
+ ((EmitterMeshVertexShape) emitterShape).setMeshes(meshes);\r
+ }\r
+\r
+ node.attachChild(emitter);\r
+ return node;\r
+ }\r
+\r
+ /**\r
+ * This method applies ArmatureModifierData to the loaded object.\r
+ * @param node\r
+ * the loaded object\r
+ * @param modifier\r
+ * the modifier to apply\r
+ * @param dataRepository\r
+ * the data repository\r
+ * @return the node to whom the modifier was applied\r
+ */\r
+ protected Node applyArmatureModifierData(Node node, Modifier modifier, DataRepository dataRepository) {\r
+ AnimData ad = (AnimData) modifier.getJmeModifierRepresentation();\r
+ ArrayList<BoneAnimation> animList = ad.anims;\r
+ Long modifierArmatureObject = (Long) modifier.getAdditionalData();\r
+ if (animList != null && animList.size() > 0) {\r
+ ConstraintHelper constraintHelper = dataRepository.getHelper(ConstraintHelper.class);\r
+ Constraint[] constraints = constraintHelper.getConstraints(modifierArmatureObject);\r
+ HashMap<String, BoneAnimation> anims = new HashMap<String, BoneAnimation>();\r
+ for (int i = 0; i < animList.size(); ++i) {\r
+ BoneAnimation boneAnimation = this.cloneBoneAnimation(animList.get(i));\r
+\r
+ //baking constraints into animations\r
+ if (constraints != null && constraints.length > 0) {\r
+ for (Constraint constraint : constraints) {\r
+ constraint.affectAnimation(ad.skeleton, boneAnimation);\r
+ }\r
+ }\r
+\r
+ anims.put(boneAnimation.getName(), boneAnimation);\r
+ }\r
+\r
+ //getting meshes\r
+ Mesh[] meshes = null;\r
+ List<Mesh> meshesList = new ArrayList<Mesh>();\r
+ List<Spatial> children = node.getChildren();\r
+ for (Spatial child : children) {\r
+ if (child instanceof Geometry) {\r
+ meshesList.add(((Geometry) child).getMesh());\r
+ }\r
+ }\r
+ if (meshesList.size() > 0) {\r
+ meshes = meshesList.toArray(new Mesh[meshesList.size()]);\r
+ }\r
+\r
+ //applying the control to the node\r
+ SkeletonControl skeletonControl = new SkeletonControl(meshes, ad.skeleton);\r
+ AnimControl control = node.getControl(AnimControl.class);\r
+\r
+ if (control == null) {\r
+ control = new AnimControl(ad.skeleton);\r
+ } else {\r
+ //merging skeletons\r
+ Skeleton controlSkeleton = control.getSkeleton();\r
+ int boneIndexIncrease = controlSkeleton.getBoneCount();\r
+ Skeleton skeleton = this.merge(controlSkeleton, ad.skeleton);\r
+\r
+ //merging animations\r
+ HashMap<String, BoneAnimation> animations = new HashMap<String, BoneAnimation>();\r
+ for (String animationName : control.getAnimationNames()) {\r
+ animations.put(animationName, control.getAnim(animationName));\r
+ }\r
+ for (Entry<String, BoneAnimation> animEntry : anims.entrySet()) {\r
+ BoneAnimation ba = animEntry.getValue();\r
+ for (int i = 0; i < ba.getTracks().length; ++i) {\r
+ BoneTrack bt = ba.getTracks()[i];\r
+ int newBoneIndex = bt.getTargetBoneIndex() + boneIndexIncrease;\r
+ ba.getTracks()[i] = new BoneTrack(newBoneIndex, bt.getTimes(), bt.getTranslations(), bt.getRotations(), bt.getScales());\r
+ }\r
+ animations.put(animEntry.getKey(), animEntry.getValue());\r
+ }\r
+\r
+ //replacing the control\r
+ node.removeControl(control);\r
+ control = new AnimControl(skeleton);\r
+ }\r
+ control.setAnimations(anims);\r
+ node.addControl(control);\r
+ node.addControl(skeletonControl);\r
+ }\r
+ return node;\r
+ }\r
+\r
+ /**\r
+ * This method applies the array modifier to the node.\r
+ * @param node\r
+ * the object the modifier will be applied to\r
+ * @param modifier\r
+ * the modifier to be applied\r
+ * @param dataRepository\r
+ * the data repository\r
+ * @return object node with arry modifier applied\r
+ */\r
+ @SuppressWarnings("unchecked")\r
+ protected Node applyArrayModifierData(Node node, Modifier modifier, DataRepository dataRepository) {\r
+ Map<String, Object> modifierData = (Map<String, Object>) modifier.getJmeModifierRepresentation();\r
+ int fittype = ((Number) modifierData.get("fittype")).intValue();\r
+ float[] offset = (float[]) modifierData.get("offset");\r
+ if (offset == null) {//the node will be repeated several times in the same place\r
+ offset = new float[]{0.0f, 0.0f, 0.0f};\r
+ }\r
+ float[] scale = (float[]) modifierData.get("scale");\r
+ if (scale == null) {//the node will be repeated several times in the same place\r
+ scale = new float[]{0.0f, 0.0f, 0.0f};\r
+ } else {\r
+ //getting bounding box\r
+ node.updateModelBound();\r
+ BoundingVolume boundingVolume = node.getWorldBound();\r
+ if (boundingVolume instanceof BoundingBox) {\r
+ scale[0] *= ((BoundingBox) boundingVolume).getXExtent() * 2.0f;\r
+ scale[1] *= ((BoundingBox) boundingVolume).getYExtent() * 2.0f;\r
+ scale[2] *= ((BoundingBox) boundingVolume).getZExtent() * 2.0f;\r
+ } else if (boundingVolume instanceof BoundingSphere) {\r
+ float radius = ((BoundingSphere) boundingVolume).getRadius();\r
+ scale[0] *= radius * 2.0f;\r
+ scale[1] *= radius * 2.0f;\r
+ scale[2] *= radius * 2.0f;\r
+ } else {\r
+ throw new IllegalStateException("Unknown bounding volume type: " + boundingVolume.getClass().getName());\r
+ }\r
+ }\r
+\r
+ //adding object's offset\r
+ float[] objectOffset = new float[]{0.0f, 0.0f, 0.0f};\r
+ Pointer pOffsetObject = (Pointer) modifierData.get("offsetob");\r
+ if (pOffsetObject != null) {\r
+ FileBlockHeader offsetObjectBlock = dataRepository.getFileBlock(pOffsetObject.getOldMemoryAddress());\r
+ ObjectHelper objectHelper = dataRepository.getHelper(ObjectHelper.class);\r
+ try {//we take the structure in case the object was not yet loaded\r
+ Structure offsetStructure = offsetObjectBlock.getStructure(dataRepository);\r
+ Vector3f translation = objectHelper.getTransformation(offsetStructure).getTranslation();\r
+ objectOffset[0] = translation.x;\r
+ objectOffset[1] = translation.y;\r
+ objectOffset[2] = translation.z;\r
+ } catch (BlenderFileException e) {\r
+ LOGGER.warning("Problems in blender file structure! Object offset cannot be applied! The problem: " + e.getMessage());\r
+ }\r
+ }\r
+\r
+ //getting start and end caps\r
+ Node[] caps = new Node[]{null, null};\r
+ Pointer[] pCaps = new Pointer[]{(Pointer) modifierData.get("startcap"), (Pointer) modifierData.get("endcap")};\r
+ for (int i = 0; i < pCaps.length; ++i) {\r
+ if (pCaps[i] != null) {\r
+ caps[i] = (Node) dataRepository.getLoadedFeature(pCaps[i].getOldMemoryAddress(), LoadedFeatureDataType.LOADED_FEATURE);\r
+ if (caps[i] != null) {\r
+ caps[i] = (Node) caps[i].clone();\r
+ } else {\r
+ FileBlockHeader capBlock = dataRepository.getFileBlock(pOffsetObject.getOldMemoryAddress());\r
+ try {//we take the structure in case the object was not yet loaded\r
+ Structure capStructure = capBlock.getStructure(dataRepository);\r
+ ObjectHelper objectHelper = dataRepository.getHelper(ObjectHelper.class);\r
+ caps[i] = (Node) objectHelper.toObject(capStructure, dataRepository);\r
+ if (caps[i] == null) {\r
+ LOGGER.warning("Cap object '" + capStructure.getName() + "' couldn't be loaded!");\r
+ }\r
+ } catch (BlenderFileException e) {\r
+ LOGGER.warning("Problems in blender file structure! Cap object cannot be applied! The problem: " + e.getMessage());\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ Vector3f translationVector = new Vector3f(offset[0] + scale[0] + objectOffset[0],\r
+ offset[1] + scale[1] + objectOffset[1],\r
+ offset[2] + scale[2] + objectOffset[2]);\r
+\r
+ //getting/calculating repeats amount\r
+ int count = 0;\r
+ if (fittype == 0) {//Fixed count\r
+ count = ((Number) modifierData.get("count")).intValue() - 1;\r
+ } else if (fittype == 1) {//Fixed length\r
+ float length = ((Number) modifierData.get("length")).floatValue();\r
+ if (translationVector.length() > 0.0f) {\r
+ count = (int) (length / translationVector.length()) - 1;\r
+ }\r
+ } else if (fittype == 2) {//Fit curve\r
+ LOGGER.warning("Fit curve mode in array modifier not yet implemented!");//TODO: implement fit curve\r
+ } else {\r
+ throw new IllegalStateException("Unknown fit type: " + fittype);\r
+ }\r
+\r
+ //adding translated nodes and caps\r
+ if (count > 0) {\r
+ Node[] arrayNodes = new Node[count];\r
+ Vector3f newTranslation = node.getLocalTranslation().clone();\r
+ for (int i = 0; i < count; ++i) {\r
+ newTranslation.addLocal(translationVector);\r
+ Node nodeClone = (Node) node.clone();\r
+ nodeClone.setLocalTranslation(newTranslation);\r
+ arrayNodes[i] = nodeClone;\r
+ }\r
+ for (Node nodeClone : arrayNodes) {\r
+ node.attachChild(nodeClone);\r
+ }\r
+ if (caps[0] != null) {\r
+ caps[0].getLocalTranslation().set(node.getLocalTranslation()).subtractLocal(translationVector);\r
+ node.attachChild(caps[0]);\r
+ }\r
+ if (caps[1] != null) {\r
+ caps[1].getLocalTranslation().set(newTranslation).addLocal(translationVector);\r
+ node.attachChild(caps[1]);\r
+ }\r
+ }\r
+ return node;\r
+ }\r
+\r
+ /**\r
+ * This class clones the bone animation data.\r
+ * @param source\r
+ * the source that is to be cloned\r
+ * @return the copy of the given bone animation\r
+ */\r
+ protected BoneAnimation cloneBoneAnimation(BoneAnimation source) {\r
+ BoneAnimation result = new BoneAnimation(source.getName(), source.getLength());\r
+\r
+ //copying tracks and applying constraints\r
+ BoneTrack[] sourceTracks = source.getTracks();\r
+ BoneTrack[] boneTracks = new BoneTrack[sourceTracks.length];\r
+ for (int i = 0; i < sourceTracks.length; ++i) {\r
+ int tablesLength = sourceTracks[i].getTimes().length;\r
+\r
+ Vector3f[] sourceTranslations = sourceTracks[i].getTranslations();\r
+ Quaternion[] sourceRotations = sourceTracks[i].getRotations();\r
+ Vector3f[] sourceScales = sourceTracks[i].getScales();\r
+\r
+ Vector3f[] translations = new Vector3f[tablesLength];\r
+ Quaternion[] rotations = new Quaternion[tablesLength];\r
+ Vector3f[] scales = sourceScales == null ? null : new Vector3f[tablesLength];\r
+ for (int j = 0; j < tablesLength; ++j) {\r
+ translations[j] = sourceTranslations[j].clone();\r
+ rotations[j] = sourceRotations[j].clone();\r
+ if (sourceScales != null) {//only scales may not be applied\r
+ scales[j] = sourceScales[j].clone();\r
+ }\r
+ }\r
+ boneTracks[i] = new BoneTrack(sourceTracks[i].getTargetBoneIndex(), sourceTracks[i].getTimes(),//times do not change, no need to clone them,\r
+ translations, rotations, scales);\r
+ }\r
+ result.setTracks(boneTracks);\r
+ return result;\r
+ }\r
+\r
+ /**\r
+ * This method merges two skeletons into one. I assume that each skeleton's 0-indexed bone is objectAnimationBone so\r
+ * only one such bone should be placed in the result\r
+ * @param s1\r
+ * first skeleton\r
+ * @param s2\r
+ * second skeleton\r
+ * @return merged skeleton\r
+ */\r
+ protected Skeleton merge(Skeleton s1, Skeleton s2) {\r
+ List<Bone> bones = new ArrayList<Bone>(s1.getBoneCount() + s2.getBoneCount());\r
+ for (int i = 0; i < s1.getBoneCount(); ++i) {\r
+ bones.add(s1.getBone(i));\r
+ }\r
+ for (int i = 1; i < s2.getBoneCount(); ++i) {//ommit objectAnimationBone\r
+ bones.add(s2.getBone(i));\r
+ }\r
+ return new Skeleton(bones.toArray(new Bone[bones.size()]));\r
+ }\r
}\r
* ***** END GPL LICENSE BLOCK *****\r
*\r
*/\r
-\r
package com.jme3.scene.plugins.blender.helpers.v249;\r
\r
import java.io.IOException;\r
* @author Marcin Roguski (Kaelthas)\r
*/\r
public class NoiseHelper extends AbstractBlenderHelper {\r
- private static final Logger LOGGER = Logger.getLogger(NoiseHelper.class.getName());\r
-\r
- /* return value */\r
- protected static final int TEX_INT = 0;\r
- protected static final int TEX_RGB = 1;\r
- protected static final int TEX_NOR = 2;\r
-\r
- /* noisetype */\r
- protected static final int TEX_NOISESOFT = 0;\r
- protected static final int TEX_NOISEPERL = 1;\r
-\r
- /* tex->stype in texture.c - cloud types */\r
- protected static final int TEX_DEFAULT = 0;\r
- protected static final int TEX_COLOR = 1;\r
-\r
- /* flag */\r
- protected static final int TEX_COLORBAND = 1;\r
- protected static final int TEX_FLIPBLEND = 2;\r
- protected static final int TEX_NEGALPHA = 4;\r
- protected static final int TEX_CHECKER_ODD = 8;\r
- protected static final int TEX_CHECKER_EVEN = 16;\r
- protected static final int TEX_PRV_ALPHA = 32;\r
- protected static final int TEX_PRV_NOR = 64;\r
- protected static final int TEX_REPEAT_XMIR = 128;\r
- protected static final int TEX_REPEAT_YMIR = 256;\r
- protected static final int TEX_FLAG_MASK = TEX_COLORBAND | TEX_FLIPBLEND | TEX_NEGALPHA | TEX_CHECKER_ODD | TEX_CHECKER_EVEN | TEX_PRV_ALPHA | TEX_PRV_NOR | TEX_REPEAT_XMIR | TEX_REPEAT_YMIR;\r
-\r
- /* tex->noisebasis2 in texture.c - wood waveforms */\r
- protected static final int TEX_SIN = 0;\r
- protected static final int TEX_SAW = 1;\r
- protected static final int TEX_TRI = 2;\r
-\r
- /* tex->stype in texture.c - marble types */\r
- protected static final int TEX_SOFT = 0;\r
- protected static final int TEX_SHARP = 1;\r
- protected static final int TEX_SHARPER = 2;\r
-\r
- /* tex->stype in texture.c - wood types */\r
- protected static final int TEX_BAND = 0;\r
- protected static final int TEX_RING = 1;\r
- protected static final int TEX_BANDNOISE = 2;\r
- protected static final int TEX_RINGNOISE = 3;\r
-\r
- /* tex->stype in texture.c - blend types */\r
- protected static final int TEX_LIN = 0;\r
- protected static final int TEX_QUAD = 1;\r
- protected static final int TEX_EASE = 2;\r
- protected static final int TEX_DIAG = 3;\r
- protected static final int TEX_SPHERE = 4;\r
- protected static final int TEX_HALO = 5;\r
- protected static final int TEX_RAD = 6;\r
-\r
- /* tex->stype in texture.c - stucci types */\r
- protected static final int TEX_PLASTIC = 0;\r
- protected static final int TEX_WALLIN = 1;\r
- protected static final int TEX_WALLOUT = 2;\r
-\r
- /* musgrave stype */\r
- protected static final int TEX_MFRACTAL = 0;\r
- protected static final int TEX_RIDGEDMF = 1;\r
- protected static final int TEX_HYBRIDMF = 2;\r
- protected static final int TEX_FBM = 3;\r
- protected static final int TEX_HTERRAIN = 4;\r
-\r
- /* keyblock->type */\r
- protected static final int KEY_LINEAR = 0;\r
- protected static final int KEY_CARDINAL = 1;\r
- protected static final int KEY_BSPLINE = 2;\r
-\r
- /* CONSTANTS (read from file) */\r
- protected static float[] hashpntf;\r
- protected static short[] hash;\r
- protected static float[] hashvectf;\r
- protected static short[] p;\r
- protected static float[][] g;\r
-\r
- /**\r
- * Constructor. Stores the blender version number and loads the constants needed for computations.\r
- * @param blenderVersion\r
- * the number of blender version\r
- */\r
- public NoiseHelper(String blenderVersion) {\r
- super(blenderVersion);\r
- this.loadConstants();\r
- }\r
-\r
- /**\r
- * This method loads the constants needed for computations. They are exactly like the ones the blender uses. Each\r
- * deriving class should override this method and load its own constraints. Be carefult with overriding though, if\r
- * an exception will be thrown the class will not be instantiated.\r
- */\r
- protected void loadConstants() {\r
- InputStream is = NoiseHelper.class.getResourceAsStream("noiseconstants.dat");\r
- try {\r
- ObjectInputStream ois = new ObjectInputStream(is);\r
- hashpntf = (float[])ois.readObject();\r
- hash = (short[])ois.readObject();\r
- hashvectf = (float[])ois.readObject();\r
- p = (short[])ois.readObject();\r
- g = (float[][])ois.readObject();\r
- } catch(IOException e) {\r
- LOGGER.log(Level.SEVERE, e.getLocalizedMessage(), e);\r
- } catch(ClassNotFoundException e) {\r
- assert false : "Constants' classes should be arrays of primitive types, so they are ALWAYS known!";\r
- } finally {\r
- if(is != null) {\r
- try {\r
- is.close();\r
- } catch(IOException e) {\r
- LOGGER.log(Level.WARNING, e.getLocalizedMessage());\r
- }\r
- }\r
- }\r
- }\r
-\r
- protected static Map<Integer, AbstractNoiseFunc> noiseFunctions = new HashMap<Integer, AbstractNoiseFunc>();\r
- static {\r
- // orgBlenderNoise (*Was BLI_hnoise(), removed noisesize, so other functions can call it without scaling.*)\r
- noiseFunctions.put(Integer.valueOf(0), new AbstractNoiseFunc() {\r
- @Override\r
- public float execute(float x, float y, float z) {\r
- return this.orgBlenderNoise(x, y, z);\r
- }\r
-\r
- @Override\r
- public float executeS(float x, float y, float z) {\r
- return 2.0f * this.orgBlenderNoise(x, y, z) - 1.0f;\r
- }\r
- });\r
- // orgPerlinNoise (*For use with BLI_gNoise/gTurbulence, returns signed noise.*)\r
- noiseFunctions.put(Integer.valueOf(1), new AbstractNoiseFunc() {\r
- @Override\r
- public float execute(float x, float y, float z) {\r
- return 0.5f + 0.5f * this.noise3Perlin(new float[] {x, y, z});\r
- }\r
-\r
- @Override\r
- public float executeS(float x, float y, float z) {\r
- return this.noise3Perlin(new float[] {x, y, z});\r
- }\r
- });\r
- // newPerlin (* for use with BLI_gNoise()/BLI_gTurbulence(), returns unsigned improved perlin noise *)\r
- noiseFunctions.put(Integer.valueOf(2), new AbstractNoiseFunc() {\r
- @Override\r
- public float execute(float x, float y, float z) {\r
- return 0.5f + 0.5f * this.newPerlin(x, y, z);\r
- }\r
-\r
- @Override\r
- public float executeS(float x, float y, float z) {\r
- return this.execute(x, y, z);\r
- }\r
- });\r
- // voronoi_F1\r
- noiseFunctions.put(Integer.valueOf(3), new AbstractNoiseFunc() {\r
- @Override\r
- public float execute(float x, float y, float z) {\r
- float[] da = new float[4], pa = new float[12];\r
- AbstractNoiseFunc.voronoi(x, y, z, da, pa, 1, 0);\r
- return da[0];\r
- }\r
-\r
- @Override\r
- public float executeS(float x, float y, float z) {\r
- float[] da = new float[4], pa = new float[12];\r
- AbstractNoiseFunc.voronoi(x, y, z, da, pa, 1, 0);\r
- return 2.0f * da[0] - 1.0f;\r
- }\r
- });\r
- // voronoi_F2\r
- noiseFunctions.put(Integer.valueOf(4), new AbstractNoiseFunc() {\r
- @Override\r
- public float execute(float x, float y, float z) {\r
- float[] da = new float[4], pa = new float[12];\r
- AbstractNoiseFunc.voronoi(x, y, z, da, pa, 1, 0);\r
- return da[1];\r
- }\r
-\r
- @Override\r
- public float executeS(float x, float y, float z) {\r
- float[] da = new float[4], pa = new float[12];\r
- AbstractNoiseFunc.voronoi(x, y, z, da, pa, 1, 0);\r
- return 2.0f * da[1] - 1.0f;\r
- }\r
- });\r
- // voronoi_F3\r
- noiseFunctions.put(Integer.valueOf(5), new AbstractNoiseFunc() {\r
- @Override\r
- public float execute(float x, float y, float z) {\r
- float[] da = new float[4], pa = new float[12];\r
- AbstractNoiseFunc.voronoi(x, y, z, da, pa, 1, 0);\r
- return da[2];\r
- }\r
-\r
- @Override\r
- public float executeS(float x, float y, float z) {\r
- float[] da = new float[4], pa = new float[12];\r
- AbstractNoiseFunc.voronoi(x, y, z, da, pa, 1, 0);\r
- return 2.0f * da[2] - 1.0f;\r
- }\r
- });\r
- // voronoi_F4\r
- noiseFunctions.put(Integer.valueOf(6), new AbstractNoiseFunc() {\r
- @Override\r
- public float execute(float x, float y, float z) {\r
- float[] da = new float[4], pa = new float[12];\r
- AbstractNoiseFunc.voronoi(x, y, z, da, pa, 1, 0);\r
- return da[3];\r
- }\r
-\r
- @Override\r
- public float executeS(float x, float y, float z) {\r
- float[] da = new float[4], pa = new float[12];\r
- AbstractNoiseFunc.voronoi(x, y, z, da, pa, 1, 0);\r
- return 2.0f * da[3] - 1.0f;\r
- }\r
- });\r
- // voronoi_F1F2\r
- noiseFunctions.put(Integer.valueOf(7), new AbstractNoiseFunc() {\r
- @Override\r
- public float execute(float x, float y, float z) {\r
- float[] da = new float[4], pa = new float[12];\r
- AbstractNoiseFunc.voronoi(x, y, z, da, pa, 1, 0);\r
- return da[1] - da[0];\r
- }\r
-\r
- @Override\r
- public float executeS(float x, float y, float z) {\r
- float[] da = new float[4], pa = new float[12];\r
- AbstractNoiseFunc.voronoi(x, y, z, da, pa, 1, 0);\r
- return 2.0f * (da[1] - da[0]) - 1.0f;\r
- }\r
- });\r
- // voronoi_Cr\r
- noiseFunctions.put(Integer.valueOf(8), new AbstractNoiseFunc() {\r
- @Override\r
- public float execute(float x, float y, float z) {\r
- float t = 10 * noiseFunctions.get(Integer.valueOf(7)).execute(x, y, z);// voronoi_F1F2\r
- return t > 1.0f ? 1.0f : t;\r
- }\r
-\r
- @Override\r
- public float executeS(float x, float y, float z) {\r
- float t = 10.0f * noiseFunctions.get(Integer.valueOf(7)).execute(x, y, z);// voronoi_F1F2\r
- return t > 1.0f ? 1.0f : 2.0f * t - 1.0f;\r
- }\r
- });\r
- // cellNoise\r
- noiseFunctions.put(Integer.valueOf(14), new AbstractNoiseFunc() {\r
- @Override\r
- public float execute(float x, float y, float z) {\r
- int xi = (int)Math.floor(x);\r
- int yi = (int)Math.floor(y);\r
- int zi = (int)Math.floor(z);\r
- long n = xi + yi * 1301 + zi * 314159;\r
- n ^= n << 13;\r
- return (n * (n * n * 15731 + 789221) + 1376312589) / 4294967296.0f;\r
- }\r
-\r
- @Override\r
- public float executeS(float x, float y, float z) {\r
- return 2.0f * this.execute(x, y, z) - 1.0f;\r
- }\r
- });\r
- }\r
-\r
- /** Distance metrics for voronoi. e parameter only used in Minkovsky. */\r
- protected static Map<Integer, IDistanceFunc> distanceFunctions = new HashMap<Integer, NoiseHelper.IDistanceFunc>();\r
- static {\r
- // real distance\r
- distanceFunctions.put(Integer.valueOf(0), new IDistanceFunc() {\r
- @Override\r
- public float execute(float x, float y, float z, float e) {\r
- return (float)Math.sqrt(x * x + y * y + z * z);\r
- }\r
- });\r
- // distance squared\r
- distanceFunctions.put(Integer.valueOf(1), new IDistanceFunc() {\r
- @Override\r
- public float execute(float x, float y, float z, float e) {\r
- return x * x + y * y + z * z;\r
- }\r
- });\r
- // manhattan/taxicab/cityblock distance\r
- distanceFunctions.put(Integer.valueOf(2), new IDistanceFunc() {\r
- @Override\r
- public float execute(float x, float y, float z, float e) {\r
- return FastMath.abs(x) + FastMath.abs(y) + FastMath.abs(z);\r
- }\r
- });\r
- // Chebychev\r
- distanceFunctions.put(Integer.valueOf(3), new IDistanceFunc() {\r
- @Override\r
- public float execute(float x, float y, float z, float e) {\r
- x = FastMath.abs(x);\r
- y = FastMath.abs(y);\r
- z = FastMath.abs(z);\r
- float t = x > y ? x : y;\r
- return z > t ? z : t;\r
- }\r
- });\r
- // minkovsky preset exponent 0.5 (MinkovskyH)\r
- distanceFunctions.put(Integer.valueOf(4), new IDistanceFunc() {\r
- @Override\r
- public float execute(float x, float y, float z, float e) {\r
- float d = (float)(Math.sqrt(FastMath.abs(x)) + Math.sqrt(FastMath.abs(y)) + Math.sqrt(FastMath.abs(z)));\r
- return d * d;\r
- }\r
- });\r
- // minkovsky preset exponent 4 (Minkovsky4)\r
- distanceFunctions.put(Integer.valueOf(5), new IDistanceFunc() {\r
- @Override\r
- public float execute(float x, float y, float z, float e) {\r
- x *= x;\r
- y *= y;\r
- z *= z;\r
- return (float)Math.sqrt(Math.sqrt(x * x + y * y + z * z));\r
- }\r
- });\r
- // Minkovsky, general case, slow, maybe too slow to be useful\r
- distanceFunctions.put(Integer.valueOf(6), new IDistanceFunc() {\r
- @Override\r
- public float execute(float x, float y, float z, float e) {\r
- return (float)Math.pow(Math.pow(FastMath.abs(x), e) + Math.pow(FastMath.abs(y), e) + Math.pow(FastMath.abs(z), e), 1.0f / e);\r
- }\r
- });\r
- }\r
-\r
- protected static Map<Integer, IMusgraveFunction> musgraveFunctions = new HashMap<Integer, NoiseHelper.IMusgraveFunction>();\r
- static {\r
- musgraveFunctions.put(Integer.valueOf(TEX_MFRACTAL), new IMusgraveFunction() {\r
- @Override\r
- public float execute(Structure tex, float x, float y, float z) {\r
- float mg_H = ((Number)tex.getFieldValue("mg_H")).floatValue();\r
- float mg_lacunarity = ((Number)tex.getFieldValue("mg_lacunarity")).floatValue();\r
- float mg_octaves = ((Number)tex.getFieldValue("mg_octaves")).floatValue();\r
- int noisebasis = ((Number)tex.getFieldValue("noisebasis")).intValue();\r
-\r
- float rmd, value = 1.0f, pwr = 1.0f, pwHL = (float)Math.pow(mg_lacunarity, -mg_H);\r
- AbstractNoiseFunc abstractNoiseFunc = noiseFunctions.get(Integer.valueOf(noisebasis));\r
- if(abstractNoiseFunc == null) {\r
- abstractNoiseFunc = noiseFunctions.get(Integer.valueOf(0));\r
- }\r
-\r
- for(int i = 0; i < (int)mg_octaves; ++i) {\r
- value *= pwr * abstractNoiseFunc.executeS(x, y, z) + 1.0f;\r
- pwr *= pwHL;\r
- x *= mg_lacunarity;\r
- y *= mg_lacunarity;\r
- z *= mg_lacunarity;\r
- }\r
- rmd = (float)(mg_octaves - Math.floor(mg_octaves));\r
- if(rmd != 0.0f) {\r
- value *= rmd * abstractNoiseFunc.executeS(x, y, z) * pwr + 1.0f;\r
- }\r
- return value;\r
- }\r
- });\r
- musgraveFunctions.put(Integer.valueOf(TEX_RIDGEDMF), new IMusgraveFunction() {\r
- @Override\r
- public float execute(Structure tex, float x, float y, float z) {\r
- float mg_H = ((Number)tex.getFieldValue("mg_H")).floatValue();\r
- float mg_lacunarity = ((Number)tex.getFieldValue("mg_lacunarity")).floatValue();\r
- float mg_octaves = ((Number)tex.getFieldValue("mg_octaves")).floatValue();\r
- float mg_offset = ((Number)tex.getFieldValue("mg_offset")).floatValue();\r
- int noisebasis = ((Number)tex.getFieldValue("noisebasis")).intValue();\r
- float mg_gain = ((Number)tex.getFieldValue("mg_gain")).floatValue();\r
- float result, signal, weight;\r
- float pwHL = (float)Math.pow(mg_lacunarity, -mg_H);\r
- float pwr = pwHL; /* starts with i=1 instead of 0 */\r
-\r
- AbstractNoiseFunc abstractNoiseFunc = noiseFunctions.get(Integer.valueOf(noisebasis));\r
- if(abstractNoiseFunc == null) {\r
- abstractNoiseFunc = noiseFunctions.get(Integer.valueOf(0));\r
- }\r
-\r
- signal = mg_offset - FastMath.abs(abstractNoiseFunc.executeS(x, y, z));\r
- signal *= signal;\r
- result = signal;\r
- weight = 1.0f;\r
-\r
- for(int i = 1; i < (int)mg_octaves; ++i) {\r
- x *= mg_lacunarity;\r
- y *= mg_lacunarity;\r
- z *= mg_lacunarity;\r
- weight = signal * mg_gain;\r
- if(weight > 1.0f) {\r
- weight = 1.0f;\r
- } else if(weight < 0.0) {\r
- weight = 0.0f;\r
- }\r
- signal = mg_offset - FastMath.abs(abstractNoiseFunc.executeS(x, y, z));\r
- signal *= signal;\r
- signal *= weight;\r
- result += signal * pwr;\r
- pwr *= pwHL;\r
- }\r
- return result;\r
- }\r
- });\r
- musgraveFunctions.put(Integer.valueOf(TEX_HYBRIDMF), new IMusgraveFunction() {\r
- @Override\r
- public float execute(Structure tex, float x, float y, float z) {\r
- float mg_H = ((Number)tex.getFieldValue("mg_H")).floatValue();\r
- float mg_lacunarity = ((Number)tex.getFieldValue("mg_lacunarity")).floatValue();\r
- float mg_octaves = ((Number)tex.getFieldValue("mg_octaves")).floatValue();\r
- float mg_offset = ((Number)tex.getFieldValue("mg_offset")).floatValue();\r
- int noisebasis = ((Number)tex.getFieldValue("noisebasis")).intValue();\r
- float mg_gain = ((Number)tex.getFieldValue("mg_gain")).floatValue();\r
- float result, signal, weight, rmd;\r
- float pwHL = (float)Math.pow(mg_lacunarity, -mg_H);\r
- float pwr = pwHL; /* starts with i=1 instead of 0 */\r
- AbstractNoiseFunc abstractNoiseFunc = noiseFunctions.get(Integer.valueOf(noisebasis));\r
- if(abstractNoiseFunc == null) {\r
- abstractNoiseFunc = noiseFunctions.get(Integer.valueOf(0));\r
- }\r
-\r
- result = abstractNoiseFunc.executeS(x, y, z) + mg_offset;\r
- weight = mg_gain * result;\r
- x *= mg_lacunarity;\r
- y *= mg_lacunarity;\r
- z *= mg_lacunarity;\r
-\r
- for(int i = 1; weight > 0.001f && i < (int)mg_octaves; ++i) {\r
- if(weight > 1.0f) {\r
- weight = 1.0f;\r
- }\r
- signal = (abstractNoiseFunc.executeS(x, y, z) + mg_offset) * pwr;\r
- pwr *= pwHL;\r
- result += weight * signal;\r
- weight *= mg_gain * signal;\r
- x *= mg_lacunarity;\r
- y *= mg_lacunarity;\r
- z *= mg_lacunarity;\r
- }\r
-\r
- rmd = mg_octaves - (float)Math.floor(mg_octaves);\r
- if(rmd != 0.0f) {\r
- result += rmd * (abstractNoiseFunc.executeS(x, y, z) + mg_offset) * pwr;\r
- }\r
- return result;\r
- }\r
- });\r
- musgraveFunctions.put(Integer.valueOf(TEX_FBM), new IMusgraveFunction() {\r
- @Override\r
- public float execute(Structure tex, float x, float y, float z) {\r
- float mg_H = ((Number)tex.getFieldValue("mg_H")).floatValue();\r
- float mg_lacunarity = ((Number)tex.getFieldValue("mg_lacunarity")).floatValue();\r
- float mg_octaves = ((Number)tex.getFieldValue("mg_octaves")).floatValue();\r
- int noisebasis = ((Number)tex.getFieldValue("noisebasis")).intValue();\r
- float rmd, value = 0.0f, pwr = 1.0f, pwHL = (float)Math.pow(mg_lacunarity, -mg_H);\r
-\r
- AbstractNoiseFunc abstractNoiseFunc = noiseFunctions.get(Integer.valueOf(noisebasis));\r
- if(abstractNoiseFunc == null) {\r
- abstractNoiseFunc = noiseFunctions.get(Integer.valueOf(0));\r
- }\r
-\r
- for(int i = 0; i < (int)mg_octaves; ++i) {\r
- value += abstractNoiseFunc.executeS(x, y, z) * pwr;\r
- pwr *= pwHL;\r
- x *= mg_lacunarity;\r
- y *= mg_lacunarity;\r
- z *= mg_lacunarity;\r
- }\r
-\r
- rmd = (float)(mg_octaves - Math.floor(mg_octaves));\r
- if(rmd != 0.f) {\r
- value += rmd * abstractNoiseFunc.executeS(x, y, z) * pwr;\r
- }\r
- return value;\r
- }\r
- });\r
- musgraveFunctions.put(Integer.valueOf(TEX_HTERRAIN), new IMusgraveFunction() {\r
- @Override\r
- public float execute(Structure tex, float x, float y, float z) {\r
- float mg_H = ((Number)tex.getFieldValue("mg_H")).floatValue();\r
- float mg_lacunarity = ((Number)tex.getFieldValue("mg_lacunarity")).floatValue();\r
- float mg_octaves = ((Number)tex.getFieldValue("mg_octaves")).floatValue();\r
- int noisebasis = ((Number)tex.getFieldValue("noisebasis")).intValue();\r
- float mg_offset = ((Number)tex.getFieldValue("mg_offset")).floatValue();\r
- float value, increment, rmd;\r
- float pwHL = (float)Math.pow(mg_lacunarity, -mg_H);\r
- float pwr = pwHL; /* starts with i=1 instead of 0 */\r
- AbstractNoiseFunc abstractNoiseFunc = noiseFunctions.get(Integer.valueOf(noisebasis));\r
- if(abstractNoiseFunc == null) {\r
- abstractNoiseFunc = noiseFunctions.get(Integer.valueOf(0));\r
- }\r
-\r
- /* first unscaled octave of function; later octaves are scaled */\r
- value = mg_offset + abstractNoiseFunc.executeS(x, y, z);\r
- x *= mg_lacunarity;\r
- y *= mg_lacunarity;\r
- z *= mg_lacunarity;\r
-\r
- for(int i = 1; i < (int)mg_octaves; ++i) {\r
- increment = (abstractNoiseFunc.executeS(x, y, z) + mg_offset) * pwr * value;\r
- value += increment;\r
- pwr *= pwHL;\r
- x *= mg_lacunarity;\r
- y *= mg_lacunarity;\r
- z *= mg_lacunarity;\r
- }\r
-\r
- rmd = mg_octaves - (float)Math.floor(mg_octaves);\r
- if(rmd != 0.0) {\r
- increment = (abstractNoiseFunc.executeS(x, y, z) + mg_offset) * pwr * value;\r
- value += rmd * increment;\r
- }\r
- return value;\r
- }\r
- });\r
- }\r
-\r
- /**\r
- * THE FOLLOWING METHODS HELP IN COMPUTATION OF THE TEXTURES.\r
- */\r
- protected void brightnesAndContrast(TexResult texres, float contrast, float brightness) {\r
- texres.tin = (texres.tin - 0.5f) * contrast + brightness - 0.5f;\r
- if(texres.tin < 0.0f) {\r
- texres.tin = 0.0f;\r
- } else if(texres.tin > 1.0f) {\r
- texres.tin = 1.0f;\r
- }\r
- }\r
-\r
- protected void brightnesAndContrastRGB(Structure tex, TexResult texres) {\r
- float contrast = ((Number)tex.getFieldValue("contrast")).floatValue();\r
- float bright = ((Number)tex.getFieldValue("bright")).floatValue();\r
- float rfac = ((Number)tex.getFieldValue("rfac")).floatValue();\r
- float gfac = ((Number)tex.getFieldValue("gfac")).floatValue();\r
- float bfac = ((Number)tex.getFieldValue("bfac")).floatValue();\r
-\r
- texres.tr = rfac * ((texres.tr - 0.5f) * contrast + bright - 0.5f);\r
- if(texres.tr < 0.0f) {\r
- texres.tr = 0.0f;\r
- }\r
- texres.tg = gfac * ((texres.tg - 0.5f) * contrast + bright - 0.5f);\r
- if(texres.tg < 0.0f) {\r
- texres.tg = 0.0f;\r
- }\r
- texres.tb = bfac * ((texres.tb - 0.5f) * contrast + bright - 0.5f);\r
- if(texres.tb < 0.0f) {\r
- texres.tb = 0.0f;\r
- }\r
- }\r
-\r
- /* this allows colorbanded textures to control normals as well */\r
- public void texNormalDerivate(ColorBand colorBand, TexResult texres, DataRepository dataRepository) {\r
- if(texres.nor != null) {\r
- TexResult fakeTexresult;\r
- try {\r
- fakeTexresult = (TexResult)texres.clone();\r
- } catch(CloneNotSupportedException e) {\r
- throw new IllegalStateException("Texture result class MUST support cloning!", e);\r
- }\r
-\r
- float fac0 = fakeTexresult.tr + fakeTexresult.tg + fakeTexresult.tb;\r
- fakeTexresult.tin = texres.nor[0];\r
- this.doColorband(colorBand, fakeTexresult, dataRepository);\r
-\r
- float fac1 = fakeTexresult.tr + fakeTexresult.tg + fakeTexresult.tb;\r
- fakeTexresult.tin = texres.nor[1];\r
- this.doColorband(colorBand, fakeTexresult, dataRepository);\r
-\r
- float fac2 = fakeTexresult.tr + fakeTexresult.tg + fakeTexresult.tb;\r
- fakeTexresult.tin = texres.nor[2];\r
- this.doColorband(colorBand, fakeTexresult, dataRepository);\r
-\r
- float fac3 = fakeTexresult.tr + fakeTexresult.tg + fakeTexresult.tb;\r
-\r
- texres.nor[0] = 0.3333f * (fac0 - fac1);\r
- texres.nor[1] = 0.3333f * (fac0 - fac2);\r
- texres.nor[2] = 0.3333f * (fac0 - fac3);\r
-\r
- texres.nor[0] = texres.tin - texres.nor[0];\r
- texres.nor[1] = texres.tin - texres.nor[1];\r
- texres.nor[2] = texres.tin - texres.nor[2];\r
- }\r
- }\r
-\r
- /**\r
- * This method calculates the colorband for the texture.\r
- * @param colorBand\r
- * the colorband data\r
- * @param texres\r
- * the texture pixel result\r
- * @param dataRepository\r
- * the data repository\r
- * @return <b>true</b> if calculation suceedess and <b>false</b> otherwise\r
- */\r
- public boolean doColorband(ColorBand colorBand, TexResult texres, DataRepository dataRepository) {\r
- CBData cbd1, cbd2, cbd0, cbd3;\r
- int i1 = 0, i2 = 0, a;\r
- float fac, mfac;\r
- float[] t = new float[4];\r
-\r
- if(colorBand == null || colorBand.tot == 0) {\r
- return true;\r
- }\r
-\r
- cbd1 = colorBand.data[0];\r
- if(colorBand.tot == 1) {\r
- texres.tr = cbd1.r;\r
- texres.tg = cbd1.g;\r
- texres.tb = cbd1.b;\r
- texres.ta = cbd1.a;\r
- } else {\r
- if(texres.tin <= cbd1.pos && colorBand.ipotype < 2) {\r
- texres.tr = cbd1.r;\r
- texres.tg = cbd1.g;\r
- texres.tb = cbd1.b;\r
- texres.ta = cbd1.a;\r
- } else {\r
- /* we're looking for first pos > in */\r
- for(a = 0; a < colorBand.tot; ++a, ++i1) {\r
- cbd1 = colorBand.data[i1];\r
- if(cbd1.pos > texres.tin) {\r
- break;\r
- }\r
- }\r
-\r
- if(a == colorBand.tot) {\r
- cbd2 = colorBand.data[i1 - 1];\r
- try {\r
- cbd1 = (CBData)cbd2.clone();\r
- } catch(CloneNotSupportedException e) {\r
- throw new IllegalStateException("Clone not supported for " + CBData.class.getName() + " class! Fix that!");\r
- }\r
- cbd1.pos = 1.0f;\r
- } else if(a == 0) {\r
- try {\r
- cbd2 = (CBData)cbd1.clone();\r
- } catch(CloneNotSupportedException e) {\r
- throw new IllegalStateException("Clone not supported for " + CBData.class.getName() + " class! Fix that!");\r
- }\r
- cbd2.pos = 0.0f;\r
- } else {\r
- cbd2 = colorBand.data[i1 - 1];\r
- }\r
-\r
- if(texres.tin >= cbd1.pos && colorBand.ipotype < 2) {\r
- texres.tr = cbd1.r;\r
- texres.tg = cbd1.g;\r
- texres.tb = cbd1.b;\r
- texres.ta = cbd1.a;\r
- } else {\r
-\r
- if(cbd2.pos != cbd1.pos) {\r
- fac = (texres.tin - cbd1.pos) / (cbd2.pos - cbd1.pos);\r
- } else {\r
- fac = 0.0f;\r
- }\r
-\r
- if(colorBand.ipotype == 4) {\r
- /* constant */\r
- texres.tr = cbd2.r;\r
- texres.tg = cbd2.g;\r
- texres.tb = cbd2.b;\r
- texres.ta = cbd2.a;\r
- return true;\r
- }\r
-\r
- if(colorBand.ipotype >= 2) {\r
- /* ipo from right to left: 3 2 1 0 */\r
-\r
- if(a >= colorBand.tot - 1) {\r
- cbd0 = cbd1;\r
- } else {\r
- cbd0 = colorBand.data[i1 + 1];\r
- }\r
- if(a < 2) {\r
- cbd3 = cbd2;\r
- } else {\r
- cbd3 = colorBand.data[i2 - 1];\r
- }\r
-\r
- fac = FastMath.clamp(fac, 0.0f, 1.0f);\r
-\r
- if(colorBand.ipotype == 3) {\r
- this.setFourIpo(fac, t, KEY_CARDINAL);\r
- } else {\r
- this.setFourIpo(fac, t, KEY_BSPLINE);\r
- }\r
-\r
- texres.tr = t[3] * cbd3.r + t[2] * cbd2.r + t[1] * cbd1.r + t[0] * cbd0.r;\r
- texres.tg = t[3] * cbd3.g + t[2] * cbd2.g + t[1] * cbd1.g + t[0] * cbd0.g;\r
- texres.tb = t[3] * cbd3.b + t[2] * cbd2.b + t[1] * cbd1.b + t[0] * cbd0.b;\r
- texres.ta = t[3] * cbd3.a + t[2] * cbd2.a + t[1] * cbd1.a + t[0] * cbd0.a;\r
- texres.tr = FastMath.clamp(texres.tr, 0.0f, 1.0f);\r
- texres.tg = FastMath.clamp(texres.tg, 0.0f, 1.0f);\r
- texres.tb = FastMath.clamp(texres.tb, 0.0f, 1.0f);\r
- texres.ta = FastMath.clamp(texres.ta, 0.0f, 1.0f);\r
- } else {\r
-\r
- if(colorBand.ipotype == 1) { /* EASE */\r
- mfac = fac * fac;\r
- fac = 3.0f * mfac - 2.0f * mfac * fac;\r
- }\r
- mfac = 1.0f - fac;\r
-\r
- texres.tr = mfac * cbd1.r + fac * cbd2.r;\r
- texres.tg = mfac * cbd1.g + fac * cbd2.g;\r
- texres.tb = mfac * cbd1.b + fac * cbd2.b;\r
- texres.ta = mfac * cbd1.a + fac * cbd2.a;\r
- }\r
- }\r
- }\r
- }\r
- return true;\r
- }\r
-\r
- protected void setFourIpo(float d, float[] data, int type) {\r
- if(type == KEY_LINEAR) {\r
- data[0] = 0.0f;\r
- data[1] = 1.0f - d;\r
- data[2] = d;\r
- data[3] = 0.0f;\r
- } else {\r
- float d2 = d * d;\r
- float d3 = d2 * d;\r
- if(type == KEY_CARDINAL) {\r
- float fc = 0.71f;\r
- data[0] = -fc * d3 + 2.0f * fc * d2 - fc * d;\r
- data[1] = (2.0f - fc) * d3 + (fc - 3.0f) * d2 + 1.0f;\r
- data[2] = (fc - 2.0f) * d3 + (3.0f - 2.0f * fc) * d2 + fc * d;\r
- data[3] = fc * d3 - fc * d2;\r
- } else if(type == KEY_BSPLINE) {\r
- data[0] = -0.16666666f * d3 + 0.5f * d2 - 0.5f * d + 0.16666666f;\r
- data[1] = 0.5f * d3 - d2 + 0.6666666f;\r
- data[2] = -0.5f * d3 + 0.5f * d2 + 0.5f * d + 0.16666666f;\r
- data[3] = 0.16666666f * d3;\r
- }\r
- }\r
- }\r
-\r
- interface IWaveForm {\r
- float execute(float x);\r
- }\r
-\r
- protected static IWaveForm[] waveformFunctions = new IWaveForm[3];\r
- static {\r
- waveformFunctions[0] = new IWaveForm() {// tex_sin\r
- @Override\r
- public float execute(float x) {\r
- return 0.5f + 0.5f * (float)Math.sin(x);\r
- }\r
- };\r
- waveformFunctions[1] = new IWaveForm() {// tex_saw\r
- @Override\r
- public float execute(float x) {\r
- int n = (int)(x / FastMath.TWO_PI);\r
- x -= n * FastMath.TWO_PI;\r
- if(x < 0.0f) {\r
- x += FastMath.TWO_PI;\r
- }\r
- return x / FastMath.TWO_PI;\r
- }\r
- };\r
- waveformFunctions[2] = new IWaveForm() {// tex_tri\r
- @Override\r
- public float execute(float x) {\r
- return 1.0f - 2.0f * FastMath.abs((float)Math.floor(x * 1.0f / FastMath.TWO_PI + 0.5f) - x * 1.0f / FastMath.TWO_PI);\r
- }\r
- };\r
- }\r
-\r
- /* computes basic wood intensity value at x,y,z */\r
- public float woodInt(Structure tex, float x, float y, float z, DataRepository dataRepository) {\r
- int noisebasis2 = ((Number)tex.getFieldValue("noisebasis2")).intValue();\r
- int noisebasis = ((Number)tex.getFieldValue("noisebasis")).intValue();\r
- int stype = ((Number)tex.getFieldValue("stype")).intValue();\r
- float noisesize = ((Number)tex.getFieldValue("noisesize")).floatValue();\r
- float turbul = ((Number)tex.getFieldValue("turbul")).floatValue();\r
- int noiseType = ((Number)tex.getFieldValue("noisetype")).intValue();\r
- float wi = 0;\r
- int waveform = noisebasis2; /* wave form: TEX_SIN=0, TEX_SAW=1, TEX_TRI=2 */\r
- int wt = stype; /* wood type: TEX_BAND=0, TEX_RING=1, TEX_BANDNOISE=2, TEX_RINGNOISE=3 */\r
-\r
- if(waveform > TEX_TRI || waveform < TEX_SIN) {\r
- waveform = 0; /* check to be sure noisebasis2 is initialized ahead of time */\r
- }\r
-\r
- if(wt == TEX_BAND) {\r
- wi = waveformFunctions[waveform].execute((x + y + z) * 10.0f);\r
- } else if(wt == TEX_RING) {\r
- wi = waveformFunctions[waveform].execute((float)Math.sqrt(x * x + y * y + z * z) * 20.0f);\r
- } else if(wt == TEX_BANDNOISE) {\r
- wi = turbul * this.bliGNoise(noisesize, x, y, z, noiseType != TEX_NOISESOFT, noisebasis);\r
- wi = waveformFunctions[waveform].execute((x + y + z) * 10.0f + wi);\r
- } else if(wt == TEX_RINGNOISE) {\r
- wi = turbul * this.bliGNoise(noisesize, x, y, z, noiseType != TEX_NOISESOFT, noisebasis);\r
- wi = waveformFunctions[waveform].execute((float)Math.sqrt(x * x + y * y + z * z) * 20.0f + wi);\r
- }\r
- return wi;\r
- }\r
-\r
- /* computes basic marble intensity at x,y,z */\r
- public float marbleInt(Structure tex, float x, float y, float z, DataRepository dataRepository) {\r
- float noisesize = ((Number)tex.getFieldValue("noisesize")).floatValue();\r
- int noisebasis = ((Number)tex.getFieldValue("noisebasis")).intValue();\r
- int noisedepth = ((Number)tex.getFieldValue("noisedepth")).intValue();\r
- int stype = ((Number)tex.getFieldValue("stype")).intValue();/* marble type: TEX_SOFT=0, TEX_SHARP=1,TEX_SHAPER=2 */\r
- float turbul = ((Number)tex.getFieldValue("turbul")).floatValue();\r
- int noisetype = ((Number)tex.getFieldValue("noisetype")).intValue();\r
- int waveform = ((Number)tex.getFieldValue("noisebasis2")).intValue(); /* wave form: TEX_SIN=0, TEX_SAW=1, TEX_TRI=2 */\r
-\r
- if(waveform > TEX_TRI || waveform < TEX_SIN) {\r
- waveform = 0; /* check to be sure noisebasis2 isn't initialized ahead of time */\r
- }\r
-\r
- float n = 5.0f * (x + y + z);\r
- float mi = n + turbul * this.bliGTurbulence(noisesize, x, y, z, noisedepth, noisetype != TEX_NOISESOFT, noisebasis);\r
-\r
- if(stype >= NoiseHelper.TEX_SOFT) { /* TEX_SOFT always true */\r
- mi = waveformFunctions[waveform].execute(mi);\r
- if(stype == TEX_SHARP) {\r
- mi = (float)Math.sqrt(mi);\r
- } else if(stype == TEX_SHARPER) {\r
- mi = (float)Math.sqrt(Math.sqrt(mi));\r
- }\r
- }\r
- return mi;\r
- }\r
-\r
- public void voronoi(float x, float y, float z, float[] da, float[] pa, float me, int dtype) {\r
- AbstractNoiseFunc.voronoi(x, y, z, da, pa, me, dtype);\r
- }\r
-\r
- public void cellNoiseV(float x, float y, float z, float[] ca) {\r
- AbstractNoiseFunc.cellNoiseV(x, y, z, ca);\r
- }\r
-\r
- /**\r
- * THE FOLLOWING METHODS HELP IN NOISE COMPUTATIONS\r
- */\r
-\r
- /**\r
- * Separated from orgBlenderNoise above, with scaling.\r
- * @param noisesize\r
- * @param x\r
- * @param y\r
- * @param z\r
- * @return\r
- */\r
- private float bliHnoise(float noisesize, float x, float y, float z) {\r
- if(noisesize == 0.0) {\r
- return 0.0f;\r
- }\r
- x = (1.0f + x) / noisesize;\r
- y = (1.0f + y) / noisesize;\r
- z = (1.0f + z) / noisesize;\r
- return noiseFunctions.get(0).execute(x, y, z);\r
- }\r
-\r
- /**\r
- * @param noisesize\r
- * @param x\r
- * @param y\r
- * @param z\r
- * @param nr\r
- * @return\r
- */\r
- public float bliTurbulence(float noisesize, float x, float y, float z, int nr) {\r
- float d = 0.5f, div = 1.0f;\r
-\r
- float s = this.bliHnoise(noisesize, x, y, z);\r
- while(nr > 0) {\r
- s += d * this.bliHnoise(noisesize * d, x, y, z);\r
- div += d;\r
- d *= 0.5;\r
- --nr;\r
- }\r
- return s / div;\r
- }\r
-\r
- /**\r
- * @param noisesize\r
- * @param x\r
- * @param y\r
- * @param z\r
- * @param nr\r
- * @return\r
- */\r
- public float bliTurbulence1(float noisesize, float x, float y, float z, int nr) {\r
- float s, d = 0.5f, div = 1.0f;\r
-\r
- s = FastMath.abs((-1.0f + 2.0f * this.bliHnoise(noisesize, x, y, z)));\r
- while(nr > 0) {\r
- s += Math.abs(d * (-1.0f + 2.0f * this.bliHnoise(noisesize * d, x, y, z)));\r
- div += d;\r
- d *= 0.5;\r
- --nr;\r
- }\r
- return s / div;\r
- }\r
-\r
- /**\r
- * @param noisesize\r
- * @param x\r
- * @param y\r
- * @param z\r
- * @return\r
- */\r
- public float bliHnoisep(float noisesize, float x, float y, float z) {\r
- return noiseFunctions.get(Integer.valueOf(0)).noise3Perlin(new float[] {x / noisesize, y / noisesize, z / noisesize});\r
- }\r
-\r
- /**\r
- * @param point\r
- * @param lofreq\r
- * @param hifreq\r
- * @return\r
- */\r
- public float turbulencePerlin(float[] point, float lofreq, float hifreq) {\r
- float freq, t = 0, p[] = new float[] {point[0] + 123.456f, point[1], point[2]};\r
- for(freq = lofreq; freq < hifreq; freq *= 2.) {\r
- t += Math.abs(noiseFunctions.get(Integer.valueOf(0)).noise3Perlin(p)) / freq;\r
- p[0] *= 2.0f;\r
- p[1] *= 2.0f;\r
- p[2] *= 2.0f;\r
- }\r
- return t - 0.3f; /* readjust to make mean value = 0.0 */\r
- }\r
-\r
- /**\r
- * @param noisesize\r
- * @param x\r
- * @param y\r
- * @param z\r
- * @param nr\r
- * @return\r
- */\r
- public float turbulencep(float noisesize, float x, float y, float z, int nr) {\r
- float[] vec = new float[] {x / noisesize, y / noisesize, z / noisesize};\r
- ++nr;\r
- return this.turbulencePerlin(vec, 1.0f, (1 << nr));\r
- }\r
-\r
- /**\r
- * Newnoise: generic noise function for use with different noisebases\r
- * @param x\r
- * @param y\r
- * @param z\r
- * @param oct\r
- * @param isHard\r
- * @param noisebasis\r
- * @return\r
- */\r
- public float bliGNoise(float noisesize, float x, float y, float z, boolean isHard, int noisebasis) {\r
- AbstractNoiseFunc abstractNoiseFunc = noiseFunctions.get(Integer.valueOf(noisebasis));\r
- if(abstractNoiseFunc == null) {\r
- abstractNoiseFunc = noiseFunctions.get(0);\r
- noisebasis = 0;\r
- }\r
- if(noisebasis == 0) {// add one to make return value same as BLI_hnoise\r
- x += 1;\r
- y += 1;\r
- z += 1;\r
- }\r
-\r
- if(noisesize != 0.0) {\r
- noisesize = 1.0f / noisesize;\r
- x *= noisesize;\r
- y *= noisesize;\r
- z *= noisesize;\r
- }\r
- if(isHard) {\r
- return Math.abs(2.0f * abstractNoiseFunc.execute(x, y, z) - 1.0f);\r
- }\r
- return abstractNoiseFunc.execute(x, y, z);\r
- }\r
-\r
- /**\r
- * Newnoise: generic turbulence function for use with different noisebasis\r
- * @param x\r
- * @param y\r
- * @param z\r
- * @param oct\r
- * @param isHard\r
- * @param noisebasis\r
- * @return\r
- */\r
- public float bliGTurbulence(float noisesize, float x, float y, float z, int oct, boolean isHard, int noisebasis) {\r
- AbstractNoiseFunc abstractNoiseFunc = noiseFunctions.get(Integer.valueOf(noisebasis));\r
- if(abstractNoiseFunc == null) {\r
- abstractNoiseFunc = noiseFunctions.get(0);\r
- noisebasis = 0;\r
- }\r
- if(noisebasis == 0) {// add one to make return value same as BLI_hnoise\r
- x += 1;\r
- y += 1;\r
- z += 1;\r
- }\r
- float sum = 0, t, amp = 1, fscale = 1;\r
-\r
- if(noisesize != 0.0) {\r
- noisesize = 1.0f / noisesize;\r
- x *= noisesize;\r
- y *= noisesize;\r
- z *= noisesize;\r
- }\r
- for(int i = 0; i <= oct; ++i, amp *= 0.5, fscale *= 2) {\r
- t = abstractNoiseFunc.execute(fscale * x, fscale * y, fscale * z);\r
- if(isHard) {\r
- t = FastMath.abs(2.0f * t - 1.0f);\r
- }\r
- sum += t * amp;\r
- }\r
-\r
- sum *= (float)(1 << oct) / (float)((1 << oct + 1) - 1);\r
- return sum;\r
- }\r
-\r
- /**\r
- * "Variable Lacunarity Noise" A distorted variety of Perlin noise. This method is used to calculate distorted noise\r
- * texture.\r
- * @param x\r
- * @param y\r
- * @param z\r
- * @param distortion\r
- * @param nbas1\r
- * @param nbas2\r
- * @return\r
- */\r
- public float mgVLNoise(float x, float y, float z, float distortion, int nbas1, int nbas2) {\r
- AbstractNoiseFunc abstractNoiseFunc1 = noiseFunctions.get(Integer.valueOf(nbas1));\r
- if(abstractNoiseFunc1 == null) {\r
- abstractNoiseFunc1 = noiseFunctions.get(Integer.valueOf(0));\r
- }\r
- AbstractNoiseFunc abstractNoiseFunc2 = noiseFunctions.get(Integer.valueOf(nbas2));\r
- if(abstractNoiseFunc2 == null) {\r
- abstractNoiseFunc2 = noiseFunctions.get(Integer.valueOf(0));\r
- }\r
- // get a random vector and scale the randomization\r
- float rx = abstractNoiseFunc1.execute(x + 13.5f, y + 13.5f, z + 13.5f) * distortion;\r
- float ry = abstractNoiseFunc1.execute(x, y, z) * distortion;\r
- float rz = abstractNoiseFunc1.execute(x - 13.5f, y - 13.5f, z - 13.5f) * distortion;\r
- return abstractNoiseFunc2.executeS(x + rx, y + ry, z + rz); //distorted-domain noise\r
- }\r
-\r
- public void mgMFractalOrfBmTex(Structure tex, float[] texvec, ColorBand colorBand, TexResult texres, DataRepository dataRepository) {\r
- int stype = ((Number)tex.getFieldValue("stype")).intValue();\r
- float nsOutscale = ((Number)tex.getFieldValue("ns_outscale")).floatValue();\r
- float nabla = ((Number)tex.getFieldValue("nabla")).floatValue();\r
- float noisesize = ((Number)tex.getFieldValue("noisesize")).floatValue();\r
- float contrast = ((Number)tex.getFieldValue("contrast")).floatValue();\r
- float brightness = ((Number)tex.getFieldValue("bright")).floatValue();\r
-\r
- IMusgraveFunction mgravefunc = stype == TEX_MFRACTAL ? musgraveFunctions.get(Integer.valueOf(stype)) : musgraveFunctions.get(Integer.valueOf(TEX_FBM));\r
-\r
- texres.tin = nsOutscale * mgravefunc.execute(tex, texvec[0], texvec[1], texvec[2]);\r
- if(texres.nor != null) {\r
- float offs = nabla / noisesize; // also scaling of texvec\r
- // calculate bumpnormal\r
- texres.nor[0] = nsOutscale * mgravefunc.execute(tex, texvec[0] + offs, texvec[1], texvec[2]);\r
- texres.nor[1] = nsOutscale * mgravefunc.execute(tex, texvec[0], texvec[1] + offs, texvec[2]);\r
- texres.nor[2] = nsOutscale * mgravefunc.execute(tex, texvec[0], texvec[1], texvec[2] + offs);\r
- this.texNormalDerivate(colorBand, texres, dataRepository);\r
- }\r
- this.brightnesAndContrast(texres, contrast, brightness);\r
- }\r
-\r
- public void mgRidgedOrHybridMFTex(Structure tex, float[] texvec, ColorBand colorBand, TexResult texres, DataRepository dataRepository) {\r
- int stype = ((Number)tex.getFieldValue("stype")).intValue();\r
- float nsOutscale = ((Number)tex.getFieldValue("ns_outscale")).floatValue();\r
- float nabla = ((Number)tex.getFieldValue("nabla")).floatValue();\r
- float noisesize = ((Number)tex.getFieldValue("noisesize")).floatValue();\r
- float contrast = ((Number)tex.getFieldValue("contrast")).floatValue();\r
- float brightness = ((Number)tex.getFieldValue("bright")).floatValue();\r
-\r
- IMusgraveFunction mgravefunc = stype == TEX_RIDGEDMF ? musgraveFunctions.get(Integer.valueOf(stype)) : musgraveFunctions.get(Integer.valueOf(TEX_HYBRIDMF));\r
-\r
- texres.tin = nsOutscale * mgravefunc.execute(tex, texvec[0], texvec[1], texvec[2]);\r
- if(texres.nor != null) {\r
- float offs = nabla / noisesize; // also scaling of texvec\r
- // calculate bumpnormal\r
- texres.nor[0] = nsOutscale * mgravefunc.execute(tex, texvec[0] + offs, texvec[1], texvec[2]);\r
- texres.nor[1] = nsOutscale * mgravefunc.execute(tex, texvec[0], texvec[1] + offs, texvec[2]);\r
- texres.nor[2] = nsOutscale * mgravefunc.execute(tex, texvec[0], texvec[1], texvec[2] + offs);\r
- this.texNormalDerivate(colorBand, texres, dataRepository);\r
- }\r
- this.brightnesAndContrast(texres, contrast, brightness);\r
- }\r
-\r
- public void mgHTerrainTex(Structure tex, float[] texvec, ColorBand colorBand, TexResult texres, DataRepository dataRepository) {\r
- float nsOutscale = ((Number)tex.getFieldValue("ns_outscale")).floatValue();\r
- float nabla = ((Number)tex.getFieldValue("nabla")).floatValue();\r
- float noisesize = ((Number)tex.getFieldValue("noisesize")).floatValue();\r
- float contrast = ((Number)tex.getFieldValue("contrast")).floatValue();\r
- float brightness = ((Number)tex.getFieldValue("bright")).floatValue();\r
-\r
- IMusgraveFunction musgraveFunction = musgraveFunctions.get(Integer.valueOf(TEX_HTERRAIN));\r
- texres.tin = nsOutscale * musgraveFunction.execute(tex, texvec[0], texvec[1], texvec[2]);\r
- if(texres.nor != null) {\r
- float offs = nabla / noisesize; // also scaling of texvec\r
- // calculate bumpnormal\r
- texres.nor[0] = nsOutscale * musgraveFunction.execute(tex, texvec[0] + offs, texvec[1], texvec[2]);\r
- texres.nor[1] = nsOutscale * musgraveFunction.execute(tex, texvec[0], texvec[1] + offs, texvec[2]);\r
- texres.nor[2] = nsOutscale * musgraveFunction.execute(tex, texvec[0], texvec[1], texvec[2] + offs);\r
- this.texNormalDerivate(colorBand, texres, dataRepository);\r
- }\r
- this.brightnesAndContrast(texres, contrast, brightness);\r
- }\r
-\r
- /**\r
- * This class is abstract to the noise functions computations. It has two methods. One calculates the Signed (with\r
- * 'S' at the end) and the other Unsigned value.\r
- * @author Marcin Roguski (Kaelthas)\r
- */\r
- protected static abstract class AbstractNoiseFunc {\r
- /**\r
- * This method calculates the unsigned value of the noise.\r
- * @param x\r
- * the x texture coordinate\r
- * @param y\r
- * the y texture coordinate\r
- * @param z\r
- * the z texture coordinate\r
- * @return value of the noise\r
- */\r
- public abstract float execute(float x, float y, float z);\r
-\r
- /**\r
- * This method calculates the signed value of the noise.\r
- * @param x\r
- * the x texture coordinate\r
- * @param y\r
- * the y texture coordinate\r
- * @param z\r
- * the z texture coordinate\r
- * @return value of the noise\r
- */\r
- public abstract float executeS(float x, float y, float z);\r
-\r
- /*\r
- * Not 'pure' Worley, but the results are virtually the same. Returns distances in da and point coords in pa\r
- */\r
- protected static void voronoi(float x, float y, float z, float[] da, float[] pa, float me, int dtype) {\r
- float xd, yd, zd, d, p[];\r
-\r
- IDistanceFunc distanceFunc = distanceFunctions.get(Integer.valueOf(dtype));\r
- if(distanceFunc == null) {\r
- distanceFunc = distanceFunctions.get(Integer.valueOf(0));\r
- }\r
-\r
- int xi = (int)FastMath.floor(x);\r
- int yi = (int)FastMath.floor(y);\r
- int zi = (int)FastMath.floor(z);\r
- da[0] = da[1] = da[2] = da[3] = 1e10f;\r
- for(int xx = xi - 1; xx <= xi + 1; ++xx) {\r
- for(int yy = yi - 1; yy <= yi + 1; ++yy) {\r
- for(int zz = zi - 1; zz <= zi + 1; ++zz) {\r
- p = AbstractNoiseFunc.hashPoint(xx, yy, zz);\r
- xd = x - (p[0] + xx);\r
- yd = y - (p[1] + yy);\r
- zd = z - (p[2] + zz);\r
- d = distanceFunc.execute(xd, yd, zd, me);\r
- if(d < da[0]) {\r
- da[3] = da[2];\r
- da[2] = da[1];\r
- da[1] = da[0];\r
- da[0] = d;\r
- pa[9] = pa[6];\r
- pa[10] = pa[7];\r
- pa[11] = pa[8];\r
- pa[6] = pa[3];\r
- pa[7] = pa[4];\r
- pa[8] = pa[5];\r
- pa[3] = pa[0];\r
- pa[4] = pa[1];\r
- pa[5] = pa[2];\r
- pa[0] = p[0] + xx;\r
- pa[1] = p[1] + yy;\r
- pa[2] = p[2] + zz;\r
- } else if(d < da[1]) {\r
- da[3] = da[2];\r
- da[2] = da[1];\r
- da[1] = d;\r
- pa[9] = pa[6];\r
- pa[10] = pa[7];\r
- pa[11] = pa[8];\r
- pa[6] = pa[3];\r
- pa[7] = pa[4];\r
- pa[8] = pa[5];\r
- pa[3] = p[0] + xx;\r
- pa[4] = p[1] + yy;\r
- pa[5] = p[2] + zz;\r
- } else if(d < da[2]) {\r
- da[3] = da[2];\r
- da[2] = d;\r
- pa[9] = pa[6];\r
- pa[10] = pa[7];\r
- pa[11] = pa[8];\r
- pa[6] = p[0] + xx;\r
- pa[7] = p[1] + yy;\r
- pa[8] = p[2] + zz;\r
- } else if(d < da[3]) {\r
- da[3] = d;\r
- pa[9] = p[0] + xx;\r
- pa[10] = p[1] + yy;\r
- pa[11] = p[2] + zz;\r
- }\r
- }\r
- }\r
- }\r
- }\r
-\r
- // #define HASHVEC(x,y,z) hashvectf+3*hash[ (hash[ (hash[(z) & 255]+(y)) & 255]+(x)) & 255]\r
-\r
- /* needed for voronoi */\r
- // #define HASHPNT(x,y,z) hashpntf+3*hash[ (hash[ (hash[(z) & 255]+(y)) & 255]+(x)) & 255]\r
- protected static float[] hashPoint(int x, int y, int z) {\r
- float[] result = new float[3];\r
- result[0] = hashpntf[3 * hash[hash[hash[z & 255] + y & 255] + x & 255]];\r
- result[1] = hashpntf[3 * hash[hash[hash[z & 255] + y & 255] + x & 255] + 1];\r
- result[2] = hashpntf[3 * hash[hash[hash[z & 255] + y & 255] + x & 255] + 2];\r
- return result;\r
- }\r
-\r
- // #define setup(i,b0,b1,r0,r1) \\r
- // t = vec[i] + 10000.; \\r
- // b0 = ((int)t) & 255; \\r
- // b1 = (b0+1) & 255; \\r
- // r0 = t - (int)t; \\r
- // r1 = r0 - 1.;\r
-\r
- // vec[3]\r
- public float noise3Perlin(float[] vec) {\r
- int bx0, bx1, by0, by1, bz0, bz1, b00, b10, b01, b11;\r
- float rx0, rx1, ry0, ry1, rz0, rz1, sx, sy, sz, a, b, c, d, t, u, v;\r
- int i, j;\r
-\r
- // setup(0, bx0,bx1, rx0,rx1);\r
- t = vec[0] + 10000.0f;\r
- bx0 = (int)t & 255;\r
- bx1 = bx0 + 1 & 255;\r
- rx0 = t - (int)t;\r
- rx1 = rx0 - 1.0f;\r
- // setup(1, by0,by1, ry0,ry1);\r
- t = vec[0] + 10000.0f;\r
- by0 = (int)t & 255;\r
- by1 = by0 + 1 & 255;\r
- ry0 = t - (int)t;\r
- ry1 = ry0 - 1.0f;\r
- // setup(2, bz0,bz1, rz0,rz1);\r
- t = vec[0] + 10000.0f;\r
- bz0 = (int)t & 255;\r
- bz1 = bz0 + 1 & 255;\r
- rz0 = t - (int)t;\r
- rz1 = rz0 - 1.0f;\r
-\r
- i = p[bx0];\r
- j = p[bx1];\r
-\r
- b00 = p[i + by0];\r
- b10 = p[j + by0];\r
- b01 = p[i + by1];\r
- b11 = p[j + by1];\r
-\r
- /* lerp moved to improved perlin above */\r
-\r
- sx = this.surve(rx0);\r
- sy = this.surve(ry0);\r
- sz = this.surve(rz0);\r
-\r
- float[] q = new float[3];\r
- q = g[b00 + bz0];\r
- u = this.at(rx0, ry0, rz0, q);\r
- q = g[b10 + bz0];\r
- v = this.at(rx1, ry0, rz0, q);\r
- a = this.lerp(sx, u, v);\r
-\r
- q = g[b01 + bz0];\r
- u = this.at(rx0, ry1, rz0, q);\r
- q = g[b11 + bz0];\r
- v = this.at(rx1, ry1, rz0, q);\r
- b = this.lerp(sx, u, v);\r
-\r
- c = this.lerp(sy, a, b); /* interpolate in y at lo x */\r
-\r
- q = g[b00 + bz1];\r
- u = this.at(rx0, ry0, rz1, q);\r
- q = g[b10 + bz1];\r
- v = this.at(rx1, ry0, rz1, q);\r
- a = this.lerp(sx, u, v);\r
-\r
- q = g[b01 + bz1];\r
- u = this.at(rx0, ry1, rz1, q);\r
- q = g[b11 + bz1];\r
- v = this.at(rx1, ry1, rz1, q);\r
- b = this.lerp(sx, u, v);\r
-\r
- d = this.lerp(sy, a, b); /* interpolate in y at hi x */\r
-\r
- return 1.5f * this.lerp(sz, c, d); /* interpolate in z */\r
- }\r
-\r
- public float orgBlenderNoise(float x, float y, float z) {\r
- float cn1, cn2, cn3, cn4, cn5, cn6, i;\r
- float ox, oy, oz, jx, jy, jz;\r
- float n = 0.5f;\r
- int ix, iy, iz, b00, b01, b10, b11, b20, b21;\r
-\r
- ox = x - (ix = (int)Math.floor(x));\r
- oy = y - (iy = (int)Math.floor(y));\r
- oz = z - (iz = (int)Math.floor(z));\r
-\r
- jx = ox - 1;\r
- jy = oy - 1;\r
- jz = oz - 1;\r
-\r
- cn1 = ox * ox;\r
- cn2 = oy * oy;\r
- cn3 = oz * oz;\r
- cn4 = jx * jx;\r
- cn5 = jy * jy;\r
- cn6 = jz * jz;\r
-\r
- cn1 = 1.0f - 3.0f * cn1 + 2.0f * cn1 * ox;\r
- cn2 = 1.0f - 3.0f * cn2 + 2.0f * cn2 * oy;\r
- cn3 = 1.0f - 3.0f * cn3 + 2.0f * cn3 * oz;\r
- cn4 = 1.0f - 3.0f * cn4 - 2.0f * cn4 * jx;\r
- cn5 = 1.0f - 3.0f * cn5 - 2.0f * cn5 * jy;\r
- cn6 = 1.0f - 3.0f * cn6 - 2.0f * cn6 * jz;\r
-\r
- b00 = hash[hash[ix & 255] + (iy & 255)];\r
- b10 = hash[hash[ix + 1 & 255] + (iy & 255)];\r
- b01 = hash[hash[ix & 255] + (iy + 1 & 255)];\r
- b11 = hash[hash[ix + 1 & 255] + (iy + 1 & 255)];\r
-\r
- b20 = iz & 255;\r
- b21 = iz + 1 & 255;\r
-\r
- /* 0 */\r
- i = cn1 * cn2 * cn3;\r
- int hIndex = 3 * hash[b20 + b00];\r
- n += i * (hashvectf[hIndex] * ox + hashvectf[hIndex + 1] * oy + hashvectf[hIndex + 2] * oz);\r
- /* 1 */\r
- i = cn1 * cn2 * cn6;\r
- hIndex = 3 * hash[b21 + b00];\r
- n += i * (hashvectf[hIndex] * ox + hashvectf[hIndex + 1] * oy + hashvectf[hIndex + 2] * jz);\r
- /* 2 */\r
- i = cn1 * cn5 * cn3;\r
- hIndex = 3 * hash[b20 + b01];\r
- n += i * (hashvectf[hIndex] * ox + hashvectf[hIndex + 1] * jy + hashvectf[hIndex + 2] * oz);\r
- /* 3 */\r
- i = cn1 * cn5 * cn6;\r
- hIndex = 3 * hash[b21 + b01];\r
- n += i * (hashvectf[hIndex] * ox + hashvectf[hIndex + 1] * jy + hashvectf[hIndex + 2] * jz);\r
- /* 4 */\r
- i = cn4 * cn2 * cn3;\r
- hIndex = 3 * hash[b20 + b10];\r
- n += i * (hashvectf[hIndex] * jx + hashvectf[hIndex + 1] * oy + hashvectf[hIndex + 2] * oz);\r
- /* 5 */\r
- i = cn4 * cn2 * cn6;\r
- hIndex = 3 * hash[b21 + b10];\r
- n += i * (hashvectf[hIndex] * jx + hashvectf[hIndex + 1] * oy + hashvectf[hIndex + 2] * jz);\r
- /* 6 */\r
- i = cn4 * cn5 * cn3;\r
- hIndex = 3 * hash[b20 + b11];\r
- n += i * (hashvectf[hIndex] * jx + hashvectf[hIndex + 1] * jy + hashvectf[hIndex + 2] * oz);\r
- /* 7 */\r
- i = cn4 * cn5 * cn6;\r
- hIndex = 3 * hash[b21 + b11];\r
- n += i * (hashvectf[hIndex] * jx + hashvectf[hIndex + 1] * jy + hashvectf[hIndex + 2] * jz);\r
-\r
- if(n < 0.0f) {\r
- n = 0.0f;\r
- } else if(n > 1.0f) {\r
- n = 1.0f;\r
- }\r
- return n;\r
- }\r
-\r
- /* instead of adding another permutation array, just use hash table defined above */\r
- public float newPerlin(float x, float y, float z) {\r
- int A, AA, AB, B, BA, BB;\r
- float u = (float)Math.floor(x), v = (float)Math.floor(y), w = (float)Math.floor(z);\r
- int X = (int)u & 255, Y = (int)v & 255, Z = (int)w & 255; // FIND UNIT CUBE THAT CONTAINS POINT\r
- x -= u; // FIND RELATIVE X,Y,Z\r
- y -= v; // OF POINT IN CUBE.\r
- z -= w;\r
- u = this.npfade(x); // COMPUTE FADE CURVES\r
- v = this.npfade(y); // FOR EACH OF X,Y,Z.\r
- w = this.npfade(z);\r
- A = hash[X] + Y;\r
- AA = hash[A] + Z;\r
- AB = hash[A + 1] + Z; // HASH COORDINATES OF\r
- B = hash[X + 1] + Y;\r
- BA = hash[B] + Z;\r
- BB = hash[B + 1] + Z; // THE 8 CUBE CORNERS,\r
- return this.lerp(w, this.lerp(v, this.lerp(u, this.grad(hash[AA], x, y, z), // AND ADD\r
- this.grad(hash[BA], x - 1, y, z)), // BLENDED\r
- this.lerp(u, this.grad(hash[AB], x, y - 1, z), // RESULTS\r
- this.grad(hash[BB], x - 1, y - 1, z))),// FROM 8\r
- this.lerp(v, this.lerp(u, this.grad(hash[AA + 1], x, y, z - 1), // CORNERS\r
- this.grad(hash[BA + 1], x - 1, y, z - 1)), // OF CUBE\r
- this.lerp(u, this.grad(hash[AB + 1], x, y - 1, z - 1), this.grad(hash[BB + 1], x - 1, y - 1, z - 1))));\r
- }\r
-\r
- /**\r
- * Returns a vector/point/color in ca, using point hasharray directly\r
- */\r
- protected static void cellNoiseV(float x, float y, float z, float[] ca) {\r
- int xi = (int)Math.floor(x);\r
- int yi = (int)Math.floor(y);\r
- int zi = (int)Math.floor(z);\r
- float[] p = AbstractNoiseFunc.hashPoint(xi, yi, zi);\r
- ca[0] = p[0];\r
- ca[1] = p[1];\r
- ca[2] = p[2];\r
- }\r
-\r
- protected float lerp(float t, float a, float b) {\r
- return a + t * (b - a);\r
- }\r
-\r
- protected float npfade(float t) {\r
- return t * t * t * (t * (t * 6.0f - 15.0f) + 10.0f);\r
- }\r
-\r
- protected float grad(int hash, float x, float y, float z) {\r
- int h = hash & 0x0F; // CONVERT LO 4 BITS OF HASH CODE\r
- float u = h < 8 ? x : y, // INTO 12 GRADIENT DIRECTIONS.\r
- v = h < 4 ? y : h == 12 || h == 14 ? x : z;\r
- return ((h & 1) == 0 ? u : -u) + ((h & 2) == 0 ? v : -v);\r
- }\r
-\r
- /**\r
- * Dot product of two vectors.\r
- * @param a\r
- * the first vector\r
- * @param b\r
- * the second vector\r
- * @return the dot product of two vectors\r
- */\r
- protected float dot(float[] a, float[] b) {\r
- return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];\r
- }\r
-\r
- protected float surve(float t) {\r
- return t * t * (3.0f - 2.0f * t);\r
- }\r
-\r
- protected float at(float rx, float ry, float rz, float[] q) {\r
- return rx * q[0] + ry * q[1] + rz * q[2];\r
- }\r
- }\r
-\r
- /**\r
- * This interface is used for distance calculation classes. Distance metrics for voronoi. e parameter only used in\r
- * Minkovsky.\r
- */\r
- interface IDistanceFunc {\r
- /**\r
- * This method calculates the distance for voronoi algorithms.\r
- * @param x\r
- * the x coordinate\r
- * @param y\r
- * the y coordinate\r
- * @param z\r
- * the z coordinate\r
- * @param e\r
- * this parameter used in Monkovsky (no idea what it really is ;)\r
- * @return\r
- */\r
- float execute(float x, float y, float z, float e);\r
- }\r
-\r
- interface IMusgraveFunction {\r
- float execute(Structure tex, float x, float y, float z);\r
- }\r
+\r
+ private static final Logger LOGGER = Logger.getLogger(NoiseHelper.class.getName());\r
+\r
+ /* return value */\r
+ protected static final int TEX_INT = 0;\r
+ protected static final int TEX_RGB = 1;\r
+ protected static final int TEX_NOR = 2;\r
+\r
+ /* noisetype */\r
+ protected static final int TEX_NOISESOFT = 0;\r
+ protected static final int TEX_NOISEPERL = 1;\r
+\r
+ /* tex->stype in texture.c - cloud types */\r
+ protected static final int TEX_DEFAULT = 0;\r
+ protected static final int TEX_COLOR = 1;\r
+\r
+ /* flag */\r
+ protected static final int TEX_COLORBAND = 1;\r
+ protected static final int TEX_FLIPBLEND = 2;\r
+ protected static final int TEX_NEGALPHA = 4;\r
+ protected static final int TEX_CHECKER_ODD = 8;\r
+ protected static final int TEX_CHECKER_EVEN = 16;\r
+ protected static final int TEX_PRV_ALPHA = 32;\r
+ protected static final int TEX_PRV_NOR = 64;\r
+ protected static final int TEX_REPEAT_XMIR = 128;\r
+ protected static final int TEX_REPEAT_YMIR = 256;\r
+ protected static final int TEX_FLAG_MASK = TEX_COLORBAND | TEX_FLIPBLEND | TEX_NEGALPHA | TEX_CHECKER_ODD | TEX_CHECKER_EVEN | TEX_PRV_ALPHA | TEX_PRV_NOR | TEX_REPEAT_XMIR | TEX_REPEAT_YMIR;\r
+\r
+ /* tex->noisebasis2 in texture.c - wood waveforms */\r
+ protected static final int TEX_SIN = 0;\r
+ protected static final int TEX_SAW = 1;\r
+ protected static final int TEX_TRI = 2;\r
+\r
+ /* tex->stype in texture.c - marble types */\r
+ protected static final int TEX_SOFT = 0;\r
+ protected static final int TEX_SHARP = 1;\r
+ protected static final int TEX_SHARPER = 2;\r
+\r
+ /* tex->stype in texture.c - wood types */\r
+ protected static final int TEX_BAND = 0;\r
+ protected static final int TEX_RING = 1;\r
+ protected static final int TEX_BANDNOISE = 2;\r
+ protected static final int TEX_RINGNOISE = 3;\r
+\r
+ /* tex->stype in texture.c - blend types */\r
+ protected static final int TEX_LIN = 0;\r
+ protected static final int TEX_QUAD = 1;\r
+ protected static final int TEX_EASE = 2;\r
+ protected static final int TEX_DIAG = 3;\r
+ protected static final int TEX_SPHERE = 4;\r
+ protected static final int TEX_HALO = 5;\r
+ protected static final int TEX_RAD = 6;\r
+\r
+ /* tex->stype in texture.c - stucci types */\r
+ protected static final int TEX_PLASTIC = 0;\r
+ protected static final int TEX_WALLIN = 1;\r
+ protected static final int TEX_WALLOUT = 2;\r
+\r
+ /* musgrave stype */\r
+ protected static final int TEX_MFRACTAL = 0;\r
+ protected static final int TEX_RIDGEDMF = 1;\r
+ protected static final int TEX_HYBRIDMF = 2;\r
+ protected static final int TEX_FBM = 3;\r
+ protected static final int TEX_HTERRAIN = 4;\r
+\r
+ /* keyblock->type */\r
+ protected static final int KEY_LINEAR = 0;\r
+ protected static final int KEY_CARDINAL = 1;\r
+ protected static final int KEY_BSPLINE = 2;\r
+\r
+ /* CONSTANTS (read from file) */\r
+ protected static float[] hashpntf;\r
+ protected static short[] hash;\r
+ protected static float[] hashvectf;\r
+ protected static short[] p;\r
+ protected static float[][] g;\r
+\r
+ /**\r
+ * Constructor. Stores the blender version number and loads the constants needed for computations.\r
+ * @param blenderVersion\r
+ * the number of blender version\r
+ */\r
+ public NoiseHelper(String blenderVersion) {\r
+ super(blenderVersion);\r
+ this.loadConstants();\r
+ }\r
+\r
+ /**\r
+ * This method loads the constants needed for computations. They are exactly like the ones the blender uses. Each\r
+ * deriving class should override this method and load its own constraints. Be carefult with overriding though, if\r
+ * an exception will be thrown the class will not be instantiated.\r
+ */\r
+ protected void loadConstants() {\r
+ InputStream is = NoiseHelper.class.getResourceAsStream("noiseconstants.dat");\r
+ try {\r
+ ObjectInputStream ois = new ObjectInputStream(is);\r
+ hashpntf = (float[]) ois.readObject();\r
+ hash = (short[]) ois.readObject();\r
+ hashvectf = (float[]) ois.readObject();\r
+ p = (short[]) ois.readObject();\r
+ g = (float[][]) ois.readObject();\r
+ } catch (IOException e) {\r
+ LOGGER.log(Level.SEVERE, e.getLocalizedMessage(), e);\r
+ } catch (ClassNotFoundException e) {\r
+ assert false : "Constants' classes should be arrays of primitive types, so they are ALWAYS known!";\r
+ } finally {\r
+ if (is != null) {\r
+ try {\r
+ is.close();\r
+ } catch (IOException e) {\r
+ LOGGER.log(Level.WARNING, e.getLocalizedMessage());\r
+ }\r
+ }\r
+ }\r
+ }\r
+ protected static Map<Integer, AbstractNoiseFunc> noiseFunctions = new HashMap<Integer, AbstractNoiseFunc>();\r
+\r
+ static {\r
+ // orgBlenderNoise (*Was BLI_hnoise(), removed noisesize, so other functions can call it without scaling.*)\r
+ noiseFunctions.put(Integer.valueOf(0), new AbstractNoiseFunc() {\r
+\r
+ @Override\r
+ public float execute(float x, float y, float z) {\r
+ return this.orgBlenderNoise(x, y, z);\r
+ }\r
+\r
+ @Override\r
+ public float executeS(float x, float y, float z) {\r
+ return 2.0f * this.orgBlenderNoise(x, y, z) - 1.0f;\r
+ }\r
+ });\r
+ // orgPerlinNoise (*For use with BLI_gNoise/gTurbulence, returns signed noise.*)\r
+ noiseFunctions.put(Integer.valueOf(1), new AbstractNoiseFunc() {\r
+\r
+ @Override\r
+ public float execute(float x, float y, float z) {\r
+ return 0.5f + 0.5f * this.noise3Perlin(new float[]{x, y, z});\r
+ }\r
+\r
+ @Override\r
+ public float executeS(float x, float y, float z) {\r
+ return this.noise3Perlin(new float[]{x, y, z});\r
+ }\r
+ });\r
+ // newPerlin (* for use with BLI_gNoise()/BLI_gTurbulence(), returns unsigned improved perlin noise *)\r
+ noiseFunctions.put(Integer.valueOf(2), new AbstractNoiseFunc() {\r
+\r
+ @Override\r
+ public float execute(float x, float y, float z) {\r
+ return 0.5f + 0.5f * this.newPerlin(x, y, z);\r
+ }\r
+\r
+ @Override\r
+ public float executeS(float x, float y, float z) {\r
+ return this.execute(x, y, z);\r
+ }\r
+ });\r
+ // voronoi_F1\r
+ noiseFunctions.put(Integer.valueOf(3), new AbstractNoiseFunc() {\r
+\r
+ @Override\r
+ public float execute(float x, float y, float z) {\r
+ float[] da = new float[4], pa = new float[12];\r
+ AbstractNoiseFunc.voronoi(x, y, z, da, pa, 1, 0);\r
+ return da[0];\r
+ }\r
+\r
+ @Override\r
+ public float executeS(float x, float y, float z) {\r
+ float[] da = new float[4], pa = new float[12];\r
+ AbstractNoiseFunc.voronoi(x, y, z, da, pa, 1, 0);\r
+ return 2.0f * da[0] - 1.0f;\r
+ }\r
+ });\r
+ // voronoi_F2\r
+ noiseFunctions.put(Integer.valueOf(4), new AbstractNoiseFunc() {\r
+\r
+ @Override\r
+ public float execute(float x, float y, float z) {\r
+ float[] da = new float[4], pa = new float[12];\r
+ AbstractNoiseFunc.voronoi(x, y, z, da, pa, 1, 0);\r
+ return da[1];\r
+ }\r
+\r
+ @Override\r
+ public float executeS(float x, float y, float z) {\r
+ float[] da = new float[4], pa = new float[12];\r
+ AbstractNoiseFunc.voronoi(x, y, z, da, pa, 1, 0);\r
+ return 2.0f * da[1] - 1.0f;\r
+ }\r
+ });\r
+ // voronoi_F3\r
+ noiseFunctions.put(Integer.valueOf(5), new AbstractNoiseFunc() {\r
+\r
+ @Override\r
+ public float execute(float x, float y, float z) {\r
+ float[] da = new float[4], pa = new float[12];\r
+ AbstractNoiseFunc.voronoi(x, y, z, da, pa, 1, 0);\r
+ return da[2];\r
+ }\r
+\r
+ @Override\r
+ public float executeS(float x, float y, float z) {\r
+ float[] da = new float[4], pa = new float[12];\r
+ AbstractNoiseFunc.voronoi(x, y, z, da, pa, 1, 0);\r
+ return 2.0f * da[2] - 1.0f;\r
+ }\r
+ });\r
+ // voronoi_F4\r
+ noiseFunctions.put(Integer.valueOf(6), new AbstractNoiseFunc() {\r
+\r
+ @Override\r
+ public float execute(float x, float y, float z) {\r
+ float[] da = new float[4], pa = new float[12];\r
+ AbstractNoiseFunc.voronoi(x, y, z, da, pa, 1, 0);\r
+ return da[3];\r
+ }\r
+\r
+ @Override\r
+ public float executeS(float x, float y, float z) {\r
+ float[] da = new float[4], pa = new float[12];\r
+ AbstractNoiseFunc.voronoi(x, y, z, da, pa, 1, 0);\r
+ return 2.0f * da[3] - 1.0f;\r
+ }\r
+ });\r
+ // voronoi_F1F2\r
+ noiseFunctions.put(Integer.valueOf(7), new AbstractNoiseFunc() {\r
+\r
+ @Override\r
+ public float execute(float x, float y, float z) {\r
+ float[] da = new float[4], pa = new float[12];\r
+ AbstractNoiseFunc.voronoi(x, y, z, da, pa, 1, 0);\r
+ return da[1] - da[0];\r
+ }\r
+\r
+ @Override\r
+ public float executeS(float x, float y, float z) {\r
+ float[] da = new float[4], pa = new float[12];\r
+ AbstractNoiseFunc.voronoi(x, y, z, da, pa, 1, 0);\r
+ return 2.0f * (da[1] - da[0]) - 1.0f;\r
+ }\r
+ });\r
+ // voronoi_Cr\r
+ noiseFunctions.put(Integer.valueOf(8), new AbstractNoiseFunc() {\r
+\r
+ @Override\r
+ public float execute(float x, float y, float z) {\r
+ float t = 10 * noiseFunctions.get(Integer.valueOf(7)).execute(x, y, z);// voronoi_F1F2\r
+ return t > 1.0f ? 1.0f : t;\r
+ }\r
+\r
+ @Override\r
+ public float executeS(float x, float y, float z) {\r
+ float t = 10.0f * noiseFunctions.get(Integer.valueOf(7)).execute(x, y, z);// voronoi_F1F2\r
+ return t > 1.0f ? 1.0f : 2.0f * t - 1.0f;\r
+ }\r
+ });\r
+ // cellNoise\r
+ noiseFunctions.put(Integer.valueOf(14), new AbstractNoiseFunc() {\r
+\r
+ @Override\r
+ public float execute(float x, float y, float z) {\r
+ int xi = (int) Math.floor(x);\r
+ int yi = (int) Math.floor(y);\r
+ int zi = (int) Math.floor(z);\r
+ long n = xi + yi * 1301 + zi * 314159;\r
+ n ^= n << 13;\r
+ return (n * (n * n * 15731 + 789221) + 1376312589) / 4294967296.0f;\r
+ }\r
+\r
+ @Override\r
+ public float executeS(float x, float y, float z) {\r
+ return 2.0f * this.execute(x, y, z) - 1.0f;\r
+ }\r
+ });\r
+ }\r
+ /** Distance metrics for voronoi. e parameter only used in Minkovsky. */\r
+ protected static Map<Integer, DistanceFunc> distanceFunctions = new HashMap<Integer, NoiseHelper.DistanceFunc>();\r
+\r
+ static {\r
+ // real distance\r
+ distanceFunctions.put(Integer.valueOf(0), new DistanceFunc() {\r
+\r
+ @Override\r
+ public float execute(float x, float y, float z, float e) {\r
+ return (float) Math.sqrt(x * x + y * y + z * z);\r
+ }\r
+ });\r
+ // distance squared\r
+ distanceFunctions.put(Integer.valueOf(1), new DistanceFunc() {\r
+\r
+ @Override\r
+ public float execute(float x, float y, float z, float e) {\r
+ return x * x + y * y + z * z;\r
+ }\r
+ });\r
+ // manhattan/taxicab/cityblock distance\r
+ distanceFunctions.put(Integer.valueOf(2), new DistanceFunc() {\r
+\r
+ @Override\r
+ public float execute(float x, float y, float z, float e) {\r
+ return FastMath.abs(x) + FastMath.abs(y) + FastMath.abs(z);\r
+ }\r
+ });\r
+ // Chebychev\r
+ distanceFunctions.put(Integer.valueOf(3), new DistanceFunc() {\r
+\r
+ @Override\r
+ public float execute(float x, float y, float z, float e) {\r
+ x = FastMath.abs(x);\r
+ y = FastMath.abs(y);\r
+ z = FastMath.abs(z);\r
+ float t = x > y ? x : y;\r
+ return z > t ? z : t;\r
+ }\r
+ });\r
+ // minkovsky preset exponent 0.5 (MinkovskyH)\r
+ distanceFunctions.put(Integer.valueOf(4), new DistanceFunc() {\r
+\r
+ @Override\r
+ public float execute(float x, float y, float z, float e) {\r
+ float d = (float) (Math.sqrt(FastMath.abs(x)) + Math.sqrt(FastMath.abs(y)) + Math.sqrt(FastMath.abs(z)));\r
+ return d * d;\r
+ }\r
+ });\r
+ // minkovsky preset exponent 4 (Minkovsky4)\r
+ distanceFunctions.put(Integer.valueOf(5), new DistanceFunc() {\r
+\r
+ @Override\r
+ public float execute(float x, float y, float z, float e) {\r
+ x *= x;\r
+ y *= y;\r
+ z *= z;\r
+ return (float) Math.sqrt(Math.sqrt(x * x + y * y + z * z));\r
+ }\r
+ });\r
+ // Minkovsky, general case, slow, maybe too slow to be useful\r
+ distanceFunctions.put(Integer.valueOf(6), new DistanceFunc() {\r
+\r
+ @Override\r
+ public float execute(float x, float y, float z, float e) {\r
+ return (float) Math.pow(Math.pow(FastMath.abs(x), e) + Math.pow(FastMath.abs(y), e) + Math.pow(FastMath.abs(z), e), 1.0f / e);\r
+ }\r
+ });\r
+ }\r
+ protected static Map<Integer, MusgraveFunction> musgraveFunctions = new HashMap<Integer, NoiseHelper.MusgraveFunction>();\r
+\r
+ static {\r
+ musgraveFunctions.put(Integer.valueOf(TEX_MFRACTAL), new MusgraveFunction() {\r
+\r
+ @Override\r
+ public float execute(Structure tex, float x, float y, float z) {\r
+ float mg_H = ((Number) tex.getFieldValue("mg_H")).floatValue();\r
+ float mg_lacunarity = ((Number) tex.getFieldValue("mg_lacunarity")).floatValue();\r
+ float mg_octaves = ((Number) tex.getFieldValue("mg_octaves")).floatValue();\r
+ int noisebasis = ((Number) tex.getFieldValue("noisebasis")).intValue();\r
+\r
+ float rmd, value = 1.0f, pwr = 1.0f, pwHL = (float) Math.pow(mg_lacunarity, -mg_H);\r
+ AbstractNoiseFunc abstractNoiseFunc = noiseFunctions.get(Integer.valueOf(noisebasis));\r
+ if (abstractNoiseFunc == null) {\r
+ abstractNoiseFunc = noiseFunctions.get(Integer.valueOf(0));\r
+ }\r
+\r
+ for (int i = 0; i < (int) mg_octaves; ++i) {\r
+ value *= pwr * abstractNoiseFunc.executeS(x, y, z) + 1.0f;\r
+ pwr *= pwHL;\r
+ x *= mg_lacunarity;\r
+ y *= mg_lacunarity;\r
+ z *= mg_lacunarity;\r
+ }\r
+ rmd = (float) (mg_octaves - Math.floor(mg_octaves));\r
+ if (rmd != 0.0f) {\r
+ value *= rmd * abstractNoiseFunc.executeS(x, y, z) * pwr + 1.0f;\r
+ }\r
+ return value;\r
+ }\r
+ });\r
+ musgraveFunctions.put(Integer.valueOf(TEX_RIDGEDMF), new MusgraveFunction() {\r
+\r
+ @Override\r
+ public float execute(Structure tex, float x, float y, float z) {\r
+ float mg_H = ((Number) tex.getFieldValue("mg_H")).floatValue();\r
+ float mg_lacunarity = ((Number) tex.getFieldValue("mg_lacunarity")).floatValue();\r
+ float mg_octaves = ((Number) tex.getFieldValue("mg_octaves")).floatValue();\r
+ float mg_offset = ((Number) tex.getFieldValue("mg_offset")).floatValue();\r
+ int noisebasis = ((Number) tex.getFieldValue("noisebasis")).intValue();\r
+ float mg_gain = ((Number) tex.getFieldValue("mg_gain")).floatValue();\r
+ float result, signal, weight;\r
+ float pwHL = (float) Math.pow(mg_lacunarity, -mg_H);\r
+ float pwr = pwHL; /* starts with i=1 instead of 0 */\r
+\r
+ AbstractNoiseFunc abstractNoiseFunc = noiseFunctions.get(Integer.valueOf(noisebasis));\r
+ if (abstractNoiseFunc == null) {\r
+ abstractNoiseFunc = noiseFunctions.get(Integer.valueOf(0));\r
+ }\r
+\r
+ signal = mg_offset - FastMath.abs(abstractNoiseFunc.executeS(x, y, z));\r
+ signal *= signal;\r
+ result = signal;\r
+ weight = 1.0f;\r
+\r
+ for (int i = 1; i < (int) mg_octaves; ++i) {\r
+ x *= mg_lacunarity;\r
+ y *= mg_lacunarity;\r
+ z *= mg_lacunarity;\r
+ weight = signal * mg_gain;\r
+ if (weight > 1.0f) {\r
+ weight = 1.0f;\r
+ } else if (weight < 0.0) {\r
+ weight = 0.0f;\r
+ }\r
+ signal = mg_offset - FastMath.abs(abstractNoiseFunc.executeS(x, y, z));\r
+ signal *= signal;\r
+ signal *= weight;\r
+ result += signal * pwr;\r
+ pwr *= pwHL;\r
+ }\r
+ return result;\r
+ }\r
+ });\r
+ musgraveFunctions.put(Integer.valueOf(TEX_HYBRIDMF), new MusgraveFunction() {\r
+\r
+ @Override\r
+ public float execute(Structure tex, float x, float y, float z) {\r
+ float mg_H = ((Number) tex.getFieldValue("mg_H")).floatValue();\r
+ float mg_lacunarity = ((Number) tex.getFieldValue("mg_lacunarity")).floatValue();\r
+ float mg_octaves = ((Number) tex.getFieldValue("mg_octaves")).floatValue();\r
+ float mg_offset = ((Number) tex.getFieldValue("mg_offset")).floatValue();\r
+ int noisebasis = ((Number) tex.getFieldValue("noisebasis")).intValue();\r
+ float mg_gain = ((Number) tex.getFieldValue("mg_gain")).floatValue();\r
+ float result, signal, weight, rmd;\r
+ float pwHL = (float) Math.pow(mg_lacunarity, -mg_H);\r
+ float pwr = pwHL; /* starts with i=1 instead of 0 */\r
+ AbstractNoiseFunc abstractNoiseFunc = noiseFunctions.get(Integer.valueOf(noisebasis));\r
+ if (abstractNoiseFunc == null) {\r
+ abstractNoiseFunc = noiseFunctions.get(Integer.valueOf(0));\r
+ }\r
+\r
+ result = abstractNoiseFunc.executeS(x, y, z) + mg_offset;\r
+ weight = mg_gain * result;\r
+ x *= mg_lacunarity;\r
+ y *= mg_lacunarity;\r
+ z *= mg_lacunarity;\r
+\r
+ for (int i = 1; weight > 0.001f && i < (int) mg_octaves; ++i) {\r
+ if (weight > 1.0f) {\r
+ weight = 1.0f;\r
+ }\r
+ signal = (abstractNoiseFunc.executeS(x, y, z) + mg_offset) * pwr;\r
+ pwr *= pwHL;\r
+ result += weight * signal;\r
+ weight *= mg_gain * signal;\r
+ x *= mg_lacunarity;\r
+ y *= mg_lacunarity;\r
+ z *= mg_lacunarity;\r
+ }\r
+\r
+ rmd = mg_octaves - (float) Math.floor(mg_octaves);\r
+ if (rmd != 0.0f) {\r
+ result += rmd * (abstractNoiseFunc.executeS(x, y, z) + mg_offset) * pwr;\r
+ }\r
+ return result;\r
+ }\r
+ });\r
+ musgraveFunctions.put(Integer.valueOf(TEX_FBM), new MusgraveFunction() {\r
+\r
+ @Override\r
+ public float execute(Structure tex, float x, float y, float z) {\r
+ float mg_H = ((Number) tex.getFieldValue("mg_H")).floatValue();\r
+ float mg_lacunarity = ((Number) tex.getFieldValue("mg_lacunarity")).floatValue();\r
+ float mg_octaves = ((Number) tex.getFieldValue("mg_octaves")).floatValue();\r
+ int noisebasis = ((Number) tex.getFieldValue("noisebasis")).intValue();\r
+ float rmd, value = 0.0f, pwr = 1.0f, pwHL = (float) Math.pow(mg_lacunarity, -mg_H);\r
+\r
+ AbstractNoiseFunc abstractNoiseFunc = noiseFunctions.get(Integer.valueOf(noisebasis));\r
+ if (abstractNoiseFunc == null) {\r
+ abstractNoiseFunc = noiseFunctions.get(Integer.valueOf(0));\r
+ }\r
+\r
+ for (int i = 0; i < (int) mg_octaves; ++i) {\r
+ value += abstractNoiseFunc.executeS(x, y, z) * pwr;\r
+ pwr *= pwHL;\r
+ x *= mg_lacunarity;\r
+ y *= mg_lacunarity;\r
+ z *= mg_lacunarity;\r
+ }\r
+\r
+ rmd = (float) (mg_octaves - Math.floor(mg_octaves));\r
+ if (rmd != 0.f) {\r
+ value += rmd * abstractNoiseFunc.executeS(x, y, z) * pwr;\r
+ }\r
+ return value;\r
+ }\r
+ });\r
+ musgraveFunctions.put(Integer.valueOf(TEX_HTERRAIN), new MusgraveFunction() {\r
+\r
+ @Override\r
+ public float execute(Structure tex, float x, float y, float z) {\r
+ float mg_H = ((Number) tex.getFieldValue("mg_H")).floatValue();\r
+ float mg_lacunarity = ((Number) tex.getFieldValue("mg_lacunarity")).floatValue();\r
+ float mg_octaves = ((Number) tex.getFieldValue("mg_octaves")).floatValue();\r
+ int noisebasis = ((Number) tex.getFieldValue("noisebasis")).intValue();\r
+ float mg_offset = ((Number) tex.getFieldValue("mg_offset")).floatValue();\r
+ float value, increment, rmd;\r
+ float pwHL = (float) Math.pow(mg_lacunarity, -mg_H);\r
+ float pwr = pwHL; /* starts with i=1 instead of 0 */\r
+ AbstractNoiseFunc abstractNoiseFunc = noiseFunctions.get(Integer.valueOf(noisebasis));\r
+ if (abstractNoiseFunc == null) {\r
+ abstractNoiseFunc = noiseFunctions.get(Integer.valueOf(0));\r
+ }\r
+\r
+ /* first unscaled octave of function; later octaves are scaled */\r
+ value = mg_offset + abstractNoiseFunc.executeS(x, y, z);\r
+ x *= mg_lacunarity;\r
+ y *= mg_lacunarity;\r
+ z *= mg_lacunarity;\r
+\r
+ for (int i = 1; i < (int) mg_octaves; ++i) {\r
+ increment = (abstractNoiseFunc.executeS(x, y, z) + mg_offset) * pwr * value;\r
+ value += increment;\r
+ pwr *= pwHL;\r
+ x *= mg_lacunarity;\r
+ y *= mg_lacunarity;\r
+ z *= mg_lacunarity;\r
+ }\r
+\r
+ rmd = mg_octaves - (float) Math.floor(mg_octaves);\r
+ if (rmd != 0.0) {\r
+ increment = (abstractNoiseFunc.executeS(x, y, z) + mg_offset) * pwr * value;\r
+ value += rmd * increment;\r
+ }\r
+ return value;\r
+ }\r
+ });\r
+ }\r
+\r
+ /**\r
+ * THE FOLLOWING METHODS HELP IN COMPUTATION OF THE TEXTURES.\r
+ */\r
+ protected void brightnesAndContrast(TexResult texres, float contrast, float brightness) {\r
+ texres.tin = (texres.tin - 0.5f) * contrast + brightness - 0.5f;\r
+ if (texres.tin < 0.0f) {\r
+ texres.tin = 0.0f;\r
+ } else if (texres.tin > 1.0f) {\r
+ texres.tin = 1.0f;\r
+ }\r
+ }\r
+\r
+ protected void brightnesAndContrastRGB(Structure tex, TexResult texres) {\r
+ float contrast = ((Number) tex.getFieldValue("contrast")).floatValue();\r
+ float bright = ((Number) tex.getFieldValue("bright")).floatValue();\r
+ float rfac = ((Number) tex.getFieldValue("rfac")).floatValue();\r
+ float gfac = ((Number) tex.getFieldValue("gfac")).floatValue();\r
+ float bfac = ((Number) tex.getFieldValue("bfac")).floatValue();\r
+\r
+ texres.tr = rfac * ((texres.tr - 0.5f) * contrast + bright - 0.5f);\r
+ if (texres.tr < 0.0f) {\r
+ texres.tr = 0.0f;\r
+ }\r
+ texres.tg = gfac * ((texres.tg - 0.5f) * contrast + bright - 0.5f);\r
+ if (texres.tg < 0.0f) {\r
+ texres.tg = 0.0f;\r
+ }\r
+ texres.tb = bfac * ((texres.tb - 0.5f) * contrast + bright - 0.5f);\r
+ if (texres.tb < 0.0f) {\r
+ texres.tb = 0.0f;\r
+ }\r
+ }\r
+\r
+ /* this allows colorbanded textures to control normals as well */\r
+ public void texNormalDerivate(ColorBand colorBand, TexResult texres, DataRepository dataRepository) {\r
+ if (texres.nor != null) {\r
+ TexResult fakeTexresult;\r
+ try {\r
+ fakeTexresult = (TexResult) texres.clone();\r
+ } catch (CloneNotSupportedException e) {\r
+ throw new IllegalStateException("Texture result class MUST support cloning!", e);\r
+ }\r
+\r
+ float fac0 = fakeTexresult.tr + fakeTexresult.tg + fakeTexresult.tb;\r
+ fakeTexresult.tin = texres.nor[0];\r
+ this.doColorband(colorBand, fakeTexresult, dataRepository);\r
+\r
+ float fac1 = fakeTexresult.tr + fakeTexresult.tg + fakeTexresult.tb;\r
+ fakeTexresult.tin = texres.nor[1];\r
+ this.doColorband(colorBand, fakeTexresult, dataRepository);\r
+\r
+ float fac2 = fakeTexresult.tr + fakeTexresult.tg + fakeTexresult.tb;\r
+ fakeTexresult.tin = texres.nor[2];\r
+ this.doColorband(colorBand, fakeTexresult, dataRepository);\r
+\r
+ float fac3 = fakeTexresult.tr + fakeTexresult.tg + fakeTexresult.tb;\r
+\r
+ texres.nor[0] = 0.3333f * (fac0 - fac1);\r
+ texres.nor[1] = 0.3333f * (fac0 - fac2);\r
+ texres.nor[2] = 0.3333f * (fac0 - fac3);\r
+\r
+ texres.nor[0] = texres.tin - texres.nor[0];\r
+ texres.nor[1] = texres.tin - texres.nor[1];\r
+ texres.nor[2] = texres.tin - texres.nor[2];\r
+ }\r
+ }\r
+\r
+ /**\r
+ * This method calculates the colorband for the texture.\r
+ * @param colorBand\r
+ * the colorband data\r
+ * @param texres\r
+ * the texture pixel result\r
+ * @param dataRepository\r
+ * the data repository\r
+ * @return <b>true</b> if calculation suceedess and <b>false</b> otherwise\r
+ */\r
+ public boolean doColorband(ColorBand colorBand, TexResult texres, DataRepository dataRepository) {\r
+ CBData cbd1, cbd2, cbd0, cbd3;\r
+ int i1 = 0, i2 = 0, a;\r
+ float fac, mfac;\r
+ float[] t = new float[4];\r
+\r
+ if (colorBand == null || colorBand.tot == 0) {\r
+ return true;\r
+ }\r
+\r
+ cbd1 = colorBand.data[0];\r
+ if (colorBand.tot == 1) {\r
+ texres.tr = cbd1.r;\r
+ texres.tg = cbd1.g;\r
+ texres.tb = cbd1.b;\r
+ texres.ta = cbd1.a;\r
+ } else {\r
+ if (texres.tin <= cbd1.pos && colorBand.ipotype < 2) {\r
+ texres.tr = cbd1.r;\r
+ texres.tg = cbd1.g;\r
+ texres.tb = cbd1.b;\r
+ texres.ta = cbd1.a;\r
+ } else {\r
+ /* we're looking for first pos > in */\r
+ for (a = 0; a < colorBand.tot; ++a, ++i1) {\r
+ cbd1 = colorBand.data[i1];\r
+ if (cbd1.pos > texres.tin) {\r
+ break;\r
+ }\r
+ }\r
+\r
+ if (a == colorBand.tot) {\r
+ cbd2 = colorBand.data[i1 - 1];\r
+ try {\r
+ cbd1 = (CBData) cbd2.clone();\r
+ } catch (CloneNotSupportedException e) {\r
+ throw new IllegalStateException("Clone not supported for " + CBData.class.getName() + " class! Fix that!");\r
+ }\r
+ cbd1.pos = 1.0f;\r
+ } else if (a == 0) {\r
+ try {\r
+ cbd2 = (CBData) cbd1.clone();\r
+ } catch (CloneNotSupportedException e) {\r
+ throw new IllegalStateException("Clone not supported for " + CBData.class.getName() + " class! Fix that!");\r
+ }\r
+ cbd2.pos = 0.0f;\r
+ } else {\r
+ cbd2 = colorBand.data[i1 - 1];\r
+ }\r
+\r
+ if (texres.tin >= cbd1.pos && colorBand.ipotype < 2) {\r
+ texres.tr = cbd1.r;\r
+ texres.tg = cbd1.g;\r
+ texres.tb = cbd1.b;\r
+ texres.ta = cbd1.a;\r
+ } else {\r
+\r
+ if (cbd2.pos != cbd1.pos) {\r
+ fac = (texres.tin - cbd1.pos) / (cbd2.pos - cbd1.pos);\r
+ } else {\r
+ fac = 0.0f;\r
+ }\r
+\r
+ if (colorBand.ipotype == 4) {\r
+ /* constant */\r
+ texres.tr = cbd2.r;\r
+ texres.tg = cbd2.g;\r
+ texres.tb = cbd2.b;\r
+ texres.ta = cbd2.a;\r
+ return true;\r
+ }\r
+\r
+ if (colorBand.ipotype >= 2) {\r
+ /* ipo from right to left: 3 2 1 0 */\r
+\r
+ if (a >= colorBand.tot - 1) {\r
+ cbd0 = cbd1;\r
+ } else {\r
+ cbd0 = colorBand.data[i1 + 1];\r
+ }\r
+ if (a < 2) {\r
+ cbd3 = cbd2;\r
+ } else {\r
+ cbd3 = colorBand.data[i2 - 1];\r
+ }\r
+\r
+ fac = FastMath.clamp(fac, 0.0f, 1.0f);\r
+\r
+ if (colorBand.ipotype == 3) {\r
+ this.setFourIpo(fac, t, KEY_CARDINAL);\r
+ } else {\r
+ this.setFourIpo(fac, t, KEY_BSPLINE);\r
+ }\r
+\r
+ texres.tr = t[3] * cbd3.r + t[2] * cbd2.r + t[1] * cbd1.r + t[0] * cbd0.r;\r
+ texres.tg = t[3] * cbd3.g + t[2] * cbd2.g + t[1] * cbd1.g + t[0] * cbd0.g;\r
+ texres.tb = t[3] * cbd3.b + t[2] * cbd2.b + t[1] * cbd1.b + t[0] * cbd0.b;\r
+ texres.ta = t[3] * cbd3.a + t[2] * cbd2.a + t[1] * cbd1.a + t[0] * cbd0.a;\r
+ texres.tr = FastMath.clamp(texres.tr, 0.0f, 1.0f);\r
+ texres.tg = FastMath.clamp(texres.tg, 0.0f, 1.0f);\r
+ texres.tb = FastMath.clamp(texres.tb, 0.0f, 1.0f);\r
+ texres.ta = FastMath.clamp(texres.ta, 0.0f, 1.0f);\r
+ } else {\r
+\r
+ if (colorBand.ipotype == 1) { /* EASE */\r
+ mfac = fac * fac;\r
+ fac = 3.0f * mfac - 2.0f * mfac * fac;\r
+ }\r
+ mfac = 1.0f - fac;\r
+\r
+ texres.tr = mfac * cbd1.r + fac * cbd2.r;\r
+ texres.tg = mfac * cbd1.g + fac * cbd2.g;\r
+ texres.tb = mfac * cbd1.b + fac * cbd2.b;\r
+ texres.ta = mfac * cbd1.a + fac * cbd2.a;\r
+ }\r
+ }\r
+ }\r
+ }\r
+ return true;\r
+ }\r
+\r
+ protected void setFourIpo(float d, float[] data, int type) {\r
+ if (type == KEY_LINEAR) {\r
+ data[0] = 0.0f;\r
+ data[1] = 1.0f - d;\r
+ data[2] = d;\r
+ data[3] = 0.0f;\r
+ } else {\r
+ float d2 = d * d;\r
+ float d3 = d2 * d;\r
+ if (type == KEY_CARDINAL) {\r
+ float fc = 0.71f;\r
+ data[0] = -fc * d3 + 2.0f * fc * d2 - fc * d;\r
+ data[1] = (2.0f - fc) * d3 + (fc - 3.0f) * d2 + 1.0f;\r
+ data[2] = (fc - 2.0f) * d3 + (3.0f - 2.0f * fc) * d2 + fc * d;\r
+ data[3] = fc * d3 - fc * d2;\r
+ } else if (type == KEY_BSPLINE) {\r
+ data[0] = -0.16666666f * d3 + 0.5f * d2 - 0.5f * d + 0.16666666f;\r
+ data[1] = 0.5f * d3 - d2 + 0.6666666f;\r
+ data[2] = -0.5f * d3 + 0.5f * d2 + 0.5f * d + 0.16666666f;\r
+ data[3] = 0.16666666f * d3;\r
+ }\r
+ }\r
+ }\r
+\r
+ interface IWaveForm {\r
+\r
+ float execute(float x);\r
+ }\r
+ protected static IWaveForm[] waveformFunctions = new IWaveForm[3];\r
+\r
+ static {\r
+ waveformFunctions[0] = new IWaveForm() {// tex_sin\r
+\r
+ @Override\r
+ public float execute(float x) {\r
+ return 0.5f + 0.5f * (float) Math.sin(x);\r
+ }\r
+ };\r
+ waveformFunctions[1] = new IWaveForm() {// tex_saw\r
+\r
+ @Override\r
+ public float execute(float x) {\r
+ int n = (int) (x / FastMath.TWO_PI);\r
+ x -= n * FastMath.TWO_PI;\r
+ if (x < 0.0f) {\r
+ x += FastMath.TWO_PI;\r
+ }\r
+ return x / FastMath.TWO_PI;\r
+ }\r
+ };\r
+ waveformFunctions[2] = new IWaveForm() {// tex_tri\r
+\r
+ @Override\r
+ public float execute(float x) {\r
+ return 1.0f - 2.0f * FastMath.abs((float) Math.floor(x * 1.0f / FastMath.TWO_PI + 0.5f) - x * 1.0f / FastMath.TWO_PI);\r
+ }\r
+ };\r
+ }\r
+\r
+ /* computes basic wood intensity value at x,y,z */\r
+ public float woodInt(Structure tex, float x, float y, float z, DataRepository dataRepository) {\r
+ int noisebasis2 = ((Number) tex.getFieldValue("noisebasis2")).intValue();\r
+ int noisebasis = ((Number) tex.getFieldValue("noisebasis")).intValue();\r
+ int stype = ((Number) tex.getFieldValue("stype")).intValue();\r
+ float noisesize = ((Number) tex.getFieldValue("noisesize")).floatValue();\r
+ float turbul = ((Number) tex.getFieldValue("turbul")).floatValue();\r
+ int noiseType = ((Number) tex.getFieldValue("noisetype")).intValue();\r
+ float wi = 0;\r
+ int waveform = noisebasis2; /* wave form: TEX_SIN=0, TEX_SAW=1, TEX_TRI=2 */\r
+ int wt = stype; /* wood type: TEX_BAND=0, TEX_RING=1, TEX_BANDNOISE=2, TEX_RINGNOISE=3 */\r
+\r
+ if (waveform > TEX_TRI || waveform < TEX_SIN) {\r
+ waveform = 0; /* check to be sure noisebasis2 is initialized ahead of time */\r
+ }\r
+\r
+ if (wt == TEX_BAND) {\r
+ wi = waveformFunctions[waveform].execute((x + y + z) * 10.0f);\r
+ } else if (wt == TEX_RING) {\r
+ wi = waveformFunctions[waveform].execute((float) Math.sqrt(x * x + y * y + z * z) * 20.0f);\r
+ } else if (wt == TEX_BANDNOISE) {\r
+ wi = turbul * this.bliGNoise(noisesize, x, y, z, noiseType != TEX_NOISESOFT, noisebasis);\r
+ wi = waveformFunctions[waveform].execute((x + y + z) * 10.0f + wi);\r
+ } else if (wt == TEX_RINGNOISE) {\r
+ wi = turbul * this.bliGNoise(noisesize, x, y, z, noiseType != TEX_NOISESOFT, noisebasis);\r
+ wi = waveformFunctions[waveform].execute((float) Math.sqrt(x * x + y * y + z * z) * 20.0f + wi);\r
+ }\r
+ return wi;\r
+ }\r
+\r
+ /* computes basic marble intensity at x,y,z */\r
+ public float marbleInt(Structure tex, float x, float y, float z, DataRepository dataRepository) {\r
+ float noisesize = ((Number) tex.getFieldValue("noisesize")).floatValue();\r
+ int noisebasis = ((Number) tex.getFieldValue("noisebasis")).intValue();\r
+ int noisedepth = ((Number) tex.getFieldValue("noisedepth")).intValue();\r
+ int stype = ((Number) tex.getFieldValue("stype")).intValue();/* marble type: TEX_SOFT=0, TEX_SHARP=1,TEX_SHAPER=2 */\r
+ float turbul = ((Number) tex.getFieldValue("turbul")).floatValue();\r
+ int noisetype = ((Number) tex.getFieldValue("noisetype")).intValue();\r
+ int waveform = ((Number) tex.getFieldValue("noisebasis2")).intValue(); /* wave form: TEX_SIN=0, TEX_SAW=1, TEX_TRI=2 */\r
+\r
+ if (waveform > TEX_TRI || waveform < TEX_SIN) {\r
+ waveform = 0; /* check to be sure noisebasis2 isn't initialized ahead of time */\r
+ }\r
+\r
+ float n = 5.0f * (x + y + z);\r
+ float mi = n + turbul * this.bliGTurbulence(noisesize, x, y, z, noisedepth, noisetype != TEX_NOISESOFT, noisebasis);\r
+\r
+ if (stype >= NoiseHelper.TEX_SOFT) { /* TEX_SOFT always true */\r
+ mi = waveformFunctions[waveform].execute(mi);\r
+ if (stype == TEX_SHARP) {\r
+ mi = (float) Math.sqrt(mi);\r
+ } else if (stype == TEX_SHARPER) {\r
+ mi = (float) Math.sqrt(Math.sqrt(mi));\r
+ }\r
+ }\r
+ return mi;\r
+ }\r
+\r
+ public void voronoi(float x, float y, float z, float[] da, float[] pa, float me, int dtype) {\r
+ AbstractNoiseFunc.voronoi(x, y, z, da, pa, me, dtype);\r
+ }\r
+\r
+ public void cellNoiseV(float x, float y, float z, float[] ca) {\r
+ AbstractNoiseFunc.cellNoiseV(x, y, z, ca);\r
+ }\r
+\r
+ /**\r
+ * THE FOLLOWING METHODS HELP IN NOISE COMPUTATIONS\r
+ */\r
+ /**\r
+ * Separated from orgBlenderNoise above, with scaling.\r
+ * @param noisesize\r
+ * @param x\r
+ * @param y\r
+ * @param z\r
+ * @return\r
+ */\r
+ private float bliHnoise(float noisesize, float x, float y, float z) {\r
+ if (noisesize == 0.0) {\r
+ return 0.0f;\r
+ }\r
+ x = (1.0f + x) / noisesize;\r
+ y = (1.0f + y) / noisesize;\r
+ z = (1.0f + z) / noisesize;\r
+ return noiseFunctions.get(0).execute(x, y, z);\r
+ }\r
+\r
+ /**\r
+ * @param noisesize\r
+ * @param x\r
+ * @param y\r
+ * @param z\r
+ * @param nr\r
+ * @return\r
+ */\r
+ public float bliTurbulence(float noisesize, float x, float y, float z, int nr) {\r
+ float d = 0.5f, div = 1.0f;\r
+\r
+ float s = this.bliHnoise(noisesize, x, y, z);\r
+ while (nr > 0) {\r
+ s += d * this.bliHnoise(noisesize * d, x, y, z);\r
+ div += d;\r
+ d *= 0.5;\r
+ --nr;\r
+ }\r
+ return s / div;\r
+ }\r
+\r
+ /**\r
+ * @param noisesize\r
+ * @param x\r
+ * @param y\r
+ * @param z\r
+ * @param nr\r
+ * @return\r
+ */\r
+ public float bliTurbulence1(float noisesize, float x, float y, float z, int nr) {\r
+ float s, d = 0.5f, div = 1.0f;\r
+\r
+ s = FastMath.abs((-1.0f + 2.0f * this.bliHnoise(noisesize, x, y, z)));\r
+ while (nr > 0) {\r
+ s += Math.abs(d * (-1.0f + 2.0f * this.bliHnoise(noisesize * d, x, y, z)));\r
+ div += d;\r
+ d *= 0.5;\r
+ --nr;\r
+ }\r
+ return s / div;\r
+ }\r
+\r
+ /**\r
+ * @param noisesize\r
+ * @param x\r
+ * @param y\r
+ * @param z\r
+ * @return\r
+ */\r
+ public float bliHnoisep(float noisesize, float x, float y, float z) {\r
+ return noiseFunctions.get(Integer.valueOf(0)).noise3Perlin(new float[]{x / noisesize, y / noisesize, z / noisesize});\r
+ }\r
+\r
+ /**\r
+ * @param point\r
+ * @param lofreq\r
+ * @param hifreq\r
+ * @return\r
+ */\r
+ public float turbulencePerlin(float[] point, float lofreq, float hifreq) {\r
+ float freq, t = 0, p[] = new float[]{point[0] + 123.456f, point[1], point[2]};\r
+ for (freq = lofreq; freq < hifreq; freq *= 2.) {\r
+ t += Math.abs(noiseFunctions.get(Integer.valueOf(0)).noise3Perlin(p)) / freq;\r
+ p[0] *= 2.0f;\r
+ p[1] *= 2.0f;\r
+ p[2] *= 2.0f;\r
+ }\r
+ return t - 0.3f; /* readjust to make mean value = 0.0 */\r
+ }\r
+\r
+ /**\r
+ * @param noisesize\r
+ * @param x\r
+ * @param y\r
+ * @param z\r
+ * @param nr\r
+ * @return\r
+ */\r
+ public float turbulencep(float noisesize, float x, float y, float z, int nr) {\r
+ float[] vec = new float[]{x / noisesize, y / noisesize, z / noisesize};\r
+ ++nr;\r
+ return this.turbulencePerlin(vec, 1.0f, (1 << nr));\r
+ }\r
+\r
+ /**\r
+ * Newnoise: generic noise function for use with different noisebases\r
+ * @param x\r
+ * @param y\r
+ * @param z\r
+ * @param oct\r
+ * @param isHard\r
+ * @param noisebasis\r
+ * @return\r
+ */\r
+ public float bliGNoise(float noisesize, float x, float y, float z, boolean isHard, int noisebasis) {\r
+ AbstractNoiseFunc abstractNoiseFunc = noiseFunctions.get(Integer.valueOf(noisebasis));\r
+ if (abstractNoiseFunc == null) {\r
+ abstractNoiseFunc = noiseFunctions.get(0);\r
+ noisebasis = 0;\r
+ }\r
+ if (noisebasis == 0) {// add one to make return value same as BLI_hnoise\r
+ x += 1;\r
+ y += 1;\r
+ z += 1;\r
+ }\r
+\r
+ if (noisesize != 0.0) {\r
+ noisesize = 1.0f / noisesize;\r
+ x *= noisesize;\r
+ y *= noisesize;\r
+ z *= noisesize;\r
+ }\r
+ if (isHard) {\r
+ return Math.abs(2.0f * abstractNoiseFunc.execute(x, y, z) - 1.0f);\r
+ }\r
+ return abstractNoiseFunc.execute(x, y, z);\r
+ }\r
+\r
+ /**\r
+ * Newnoise: generic turbulence function for use with different noisebasis\r
+ * @param x\r
+ * @param y\r
+ * @param z\r
+ * @param oct\r
+ * @param isHard\r
+ * @param noisebasis\r
+ * @return\r
+ */\r
+ public float bliGTurbulence(float noisesize, float x, float y, float z, int oct, boolean isHard, int noisebasis) {\r
+ AbstractNoiseFunc abstractNoiseFunc = noiseFunctions.get(Integer.valueOf(noisebasis));\r
+ if (abstractNoiseFunc == null) {\r
+ abstractNoiseFunc = noiseFunctions.get(0);\r
+ noisebasis = 0;\r
+ }\r
+ if (noisebasis == 0) {// add one to make return value same as BLI_hnoise\r
+ x += 1;\r
+ y += 1;\r
+ z += 1;\r
+ }\r
+ float sum = 0, t, amp = 1, fscale = 1;\r
+\r
+ if (noisesize != 0.0) {\r
+ noisesize = 1.0f / noisesize;\r
+ x *= noisesize;\r
+ y *= noisesize;\r
+ z *= noisesize;\r
+ }\r
+ for (int i = 0; i <= oct; ++i, amp *= 0.5, fscale *= 2) {\r
+ t = abstractNoiseFunc.execute(fscale * x, fscale * y, fscale * z);\r
+ if (isHard) {\r
+ t = FastMath.abs(2.0f * t - 1.0f);\r
+ }\r
+ sum += t * amp;\r
+ }\r
+\r
+ sum *= (float) (1 << oct) / (float) ((1 << oct + 1) - 1);\r
+ return sum;\r
+ }\r
+\r
+ /**\r
+ * "Variable Lacunarity Noise" A distorted variety of Perlin noise. This method is used to calculate distorted noise\r
+ * texture.\r
+ * @param x\r
+ * @param y\r
+ * @param z\r
+ * @param distortion\r
+ * @param nbas1\r
+ * @param nbas2\r
+ * @return\r
+ */\r
+ public float mgVLNoise(float x, float y, float z, float distortion, int nbas1, int nbas2) {\r
+ AbstractNoiseFunc abstractNoiseFunc1 = noiseFunctions.get(Integer.valueOf(nbas1));\r
+ if (abstractNoiseFunc1 == null) {\r
+ abstractNoiseFunc1 = noiseFunctions.get(Integer.valueOf(0));\r
+ }\r
+ AbstractNoiseFunc abstractNoiseFunc2 = noiseFunctions.get(Integer.valueOf(nbas2));\r
+ if (abstractNoiseFunc2 == null) {\r
+ abstractNoiseFunc2 = noiseFunctions.get(Integer.valueOf(0));\r
+ }\r
+ // get a random vector and scale the randomization\r
+ float rx = abstractNoiseFunc1.execute(x + 13.5f, y + 13.5f, z + 13.5f) * distortion;\r
+ float ry = abstractNoiseFunc1.execute(x, y, z) * distortion;\r
+ float rz = abstractNoiseFunc1.execute(x - 13.5f, y - 13.5f, z - 13.5f) * distortion;\r
+ return abstractNoiseFunc2.executeS(x + rx, y + ry, z + rz); //distorted-domain noise\r
+ }\r
+\r
+ public void mgMFractalOrfBmTex(Structure tex, float[] texvec, ColorBand colorBand, TexResult texres, DataRepository dataRepository) {\r
+ int stype = ((Number) tex.getFieldValue("stype")).intValue();\r
+ float nsOutscale = ((Number) tex.getFieldValue("ns_outscale")).floatValue();\r
+ float nabla = ((Number) tex.getFieldValue("nabla")).floatValue();\r
+ float noisesize = ((Number) tex.getFieldValue("noisesize")).floatValue();\r
+ float contrast = ((Number) tex.getFieldValue("contrast")).floatValue();\r
+ float brightness = ((Number) tex.getFieldValue("bright")).floatValue();\r
+\r
+ MusgraveFunction mgravefunc = stype == TEX_MFRACTAL ? musgraveFunctions.get(Integer.valueOf(stype)) : musgraveFunctions.get(Integer.valueOf(TEX_FBM));\r
+\r
+ texres.tin = nsOutscale * mgravefunc.execute(tex, texvec[0], texvec[1], texvec[2]);\r
+ if (texres.nor != null) {\r
+ float offs = nabla / noisesize; // also scaling of texvec\r
+ // calculate bumpnormal\r
+ texres.nor[0] = nsOutscale * mgravefunc.execute(tex, texvec[0] + offs, texvec[1], texvec[2]);\r
+ texres.nor[1] = nsOutscale * mgravefunc.execute(tex, texvec[0], texvec[1] + offs, texvec[2]);\r
+ texres.nor[2] = nsOutscale * mgravefunc.execute(tex, texvec[0], texvec[1], texvec[2] + offs);\r
+ this.texNormalDerivate(colorBand, texres, dataRepository);\r
+ }\r
+ this.brightnesAndContrast(texres, contrast, brightness);\r
+ }\r
+\r
+ public void mgRidgedOrHybridMFTex(Structure tex, float[] texvec, ColorBand colorBand, TexResult texres, DataRepository dataRepository) {\r
+ int stype = ((Number) tex.getFieldValue("stype")).intValue();\r
+ float nsOutscale = ((Number) tex.getFieldValue("ns_outscale")).floatValue();\r
+ float nabla = ((Number) tex.getFieldValue("nabla")).floatValue();\r
+ float noisesize = ((Number) tex.getFieldValue("noisesize")).floatValue();\r
+ float contrast = ((Number) tex.getFieldValue("contrast")).floatValue();\r
+ float brightness = ((Number) tex.getFieldValue("bright")).floatValue();\r
+\r
+ MusgraveFunction mgravefunc = stype == TEX_RIDGEDMF ? musgraveFunctions.get(Integer.valueOf(stype)) : musgraveFunctions.get(Integer.valueOf(TEX_HYBRIDMF));\r
+\r
+ texres.tin = nsOutscale * mgravefunc.execute(tex, texvec[0], texvec[1], texvec[2]);\r
+ if (texres.nor != null) {\r
+ float offs = nabla / noisesize; // also scaling of texvec\r
+ // calculate bumpnormal\r
+ texres.nor[0] = nsOutscale * mgravefunc.execute(tex, texvec[0] + offs, texvec[1], texvec[2]);\r
+ texres.nor[1] = nsOutscale * mgravefunc.execute(tex, texvec[0], texvec[1] + offs, texvec[2]);\r
+ texres.nor[2] = nsOutscale * mgravefunc.execute(tex, texvec[0], texvec[1], texvec[2] + offs);\r
+ this.texNormalDerivate(colorBand, texres, dataRepository);\r
+ }\r
+ this.brightnesAndContrast(texres, contrast, brightness);\r
+ }\r
+\r
+ public void mgHTerrainTex(Structure tex, float[] texvec, ColorBand colorBand, TexResult texres, DataRepository dataRepository) {\r
+ float nsOutscale = ((Number) tex.getFieldValue("ns_outscale")).floatValue();\r
+ float nabla = ((Number) tex.getFieldValue("nabla")).floatValue();\r
+ float noisesize = ((Number) tex.getFieldValue("noisesize")).floatValue();\r
+ float contrast = ((Number) tex.getFieldValue("contrast")).floatValue();\r
+ float brightness = ((Number) tex.getFieldValue("bright")).floatValue();\r
+\r
+ MusgraveFunction musgraveFunction = musgraveFunctions.get(Integer.valueOf(TEX_HTERRAIN));\r
+ texres.tin = nsOutscale * musgraveFunction.execute(tex, texvec[0], texvec[1], texvec[2]);\r
+ if (texres.nor != null) {\r
+ float offs = nabla / noisesize; // also scaling of texvec\r
+ // calculate bumpnormal\r
+ texres.nor[0] = nsOutscale * musgraveFunction.execute(tex, texvec[0] + offs, texvec[1], texvec[2]);\r
+ texres.nor[1] = nsOutscale * musgraveFunction.execute(tex, texvec[0], texvec[1] + offs, texvec[2]);\r
+ texres.nor[2] = nsOutscale * musgraveFunction.execute(tex, texvec[0], texvec[1], texvec[2] + offs);\r
+ this.texNormalDerivate(colorBand, texres, dataRepository);\r
+ }\r
+ this.brightnesAndContrast(texres, contrast, brightness);\r
+ }\r
+\r
+ /**\r
+ * This class is abstract to the noise functions computations. It has two methods. One calculates the Signed (with\r
+ * 'S' at the end) and the other Unsigned value.\r
+ * @author Marcin Roguski (Kaelthas)\r
+ */\r
+ protected static abstract class AbstractNoiseFunc {\r
+\r
+ /**\r
+ * This method calculates the unsigned value of the noise.\r
+ * @param x\r
+ * the x texture coordinate\r
+ * @param y\r
+ * the y texture coordinate\r
+ * @param z\r
+ * the z texture coordinate\r
+ * @return value of the noise\r
+ */\r
+ public abstract float execute(float x, float y, float z);\r
+\r
+ /**\r
+ * This method calculates the signed value of the noise.\r
+ * @param x\r
+ * the x texture coordinate\r
+ * @param y\r
+ * the y texture coordinate\r
+ * @param z\r
+ * the z texture coordinate\r
+ * @return value of the noise\r
+ */\r
+ public abstract float executeS(float x, float y, float z);\r
+\r
+ /*\r
+ * Not 'pure' Worley, but the results are virtually the same. Returns distances in da and point coords in pa\r
+ */\r
+ protected static void voronoi(float x, float y, float z, float[] da, float[] pa, float me, int dtype) {\r
+ float xd, yd, zd, d, p[];\r
+\r
+ DistanceFunc distanceFunc = distanceFunctions.get(Integer.valueOf(dtype));\r
+ if (distanceFunc == null) {\r
+ distanceFunc = distanceFunctions.get(Integer.valueOf(0));\r
+ }\r
+\r
+ int xi = (int) FastMath.floor(x);\r
+ int yi = (int) FastMath.floor(y);\r
+ int zi = (int) FastMath.floor(z);\r
+ da[0] = da[1] = da[2] = da[3] = 1e10f;\r
+ for (int xx = xi - 1; xx <= xi + 1; ++xx) {\r
+ for (int yy = yi - 1; yy <= yi + 1; ++yy) {\r
+ for (int zz = zi - 1; zz <= zi + 1; ++zz) {\r
+ p = AbstractNoiseFunc.hashPoint(xx, yy, zz);\r
+ xd = x - (p[0] + xx);\r
+ yd = y - (p[1] + yy);\r
+ zd = z - (p[2] + zz);\r
+ d = distanceFunc.execute(xd, yd, zd, me);\r
+ if (d < da[0]) {\r
+ da[3] = da[2];\r
+ da[2] = da[1];\r
+ da[1] = da[0];\r
+ da[0] = d;\r
+ pa[9] = pa[6];\r
+ pa[10] = pa[7];\r
+ pa[11] = pa[8];\r
+ pa[6] = pa[3];\r
+ pa[7] = pa[4];\r
+ pa[8] = pa[5];\r
+ pa[3] = pa[0];\r
+ pa[4] = pa[1];\r
+ pa[5] = pa[2];\r
+ pa[0] = p[0] + xx;\r
+ pa[1] = p[1] + yy;\r
+ pa[2] = p[2] + zz;\r
+ } else if (d < da[1]) {\r
+ da[3] = da[2];\r
+ da[2] = da[1];\r
+ da[1] = d;\r
+ pa[9] = pa[6];\r
+ pa[10] = pa[7];\r
+ pa[11] = pa[8];\r
+ pa[6] = pa[3];\r
+ pa[7] = pa[4];\r
+ pa[8] = pa[5];\r
+ pa[3] = p[0] + xx;\r
+ pa[4] = p[1] + yy;\r
+ pa[5] = p[2] + zz;\r
+ } else if (d < da[2]) {\r
+ da[3] = da[2];\r
+ da[2] = d;\r
+ pa[9] = pa[6];\r
+ pa[10] = pa[7];\r
+ pa[11] = pa[8];\r
+ pa[6] = p[0] + xx;\r
+ pa[7] = p[1] + yy;\r
+ pa[8] = p[2] + zz;\r
+ } else if (d < da[3]) {\r
+ da[3] = d;\r
+ pa[9] = p[0] + xx;\r
+ pa[10] = p[1] + yy;\r
+ pa[11] = p[2] + zz;\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ // #define HASHVEC(x,y,z) hashvectf+3*hash[ (hash[ (hash[(z) & 255]+(y)) & 255]+(x)) & 255]\r
+\r
+ /* needed for voronoi */\r
+ // #define HASHPNT(x,y,z) hashpntf+3*hash[ (hash[ (hash[(z) & 255]+(y)) & 255]+(x)) & 255]\r
+ protected static float[] hashPoint(int x, int y, int z) {\r
+ float[] result = new float[3];\r
+ result[0] = hashpntf[3 * hash[hash[hash[z & 255] + y & 255] + x & 255]];\r
+ result[1] = hashpntf[3 * hash[hash[hash[z & 255] + y & 255] + x & 255] + 1];\r
+ result[2] = hashpntf[3 * hash[hash[hash[z & 255] + y & 255] + x & 255] + 2];\r
+ return result;\r
+ }\r
+\r
+ // #define setup(i,b0,b1,r0,r1) \\r
+ // t = vec[i] + 10000.; \\r
+ // b0 = ((int)t) & 255; \\r
+ // b1 = (b0+1) & 255; \\r
+ // r0 = t - (int)t; \\r
+ // r1 = r0 - 1.;\r
+ // vec[3]\r
+ public float noise3Perlin(float[] vec) {\r
+ int bx0, bx1, by0, by1, bz0, bz1, b00, b10, b01, b11;\r
+ float rx0, rx1, ry0, ry1, rz0, rz1, sx, sy, sz, a, b, c, d, t, u, v;\r
+ int i, j;\r
+\r
+ // setup(0, bx0,bx1, rx0,rx1);\r
+ t = vec[0] + 10000.0f;\r
+ bx0 = (int) t & 255;\r
+ bx1 = bx0 + 1 & 255;\r
+ rx0 = t - (int) t;\r
+ rx1 = rx0 - 1.0f;\r
+ // setup(1, by0,by1, ry0,ry1);\r
+ t = vec[0] + 10000.0f;\r
+ by0 = (int) t & 255;\r
+ by1 = by0 + 1 & 255;\r
+ ry0 = t - (int) t;\r
+ ry1 = ry0 - 1.0f;\r
+ // setup(2, bz0,bz1, rz0,rz1);\r
+ t = vec[0] + 10000.0f;\r
+ bz0 = (int) t & 255;\r
+ bz1 = bz0 + 1 & 255;\r
+ rz0 = t - (int) t;\r
+ rz1 = rz0 - 1.0f;\r
+\r
+ i = p[bx0];\r
+ j = p[bx1];\r
+\r
+ b00 = p[i + by0];\r
+ b10 = p[j + by0];\r
+ b01 = p[i + by1];\r
+ b11 = p[j + by1];\r
+\r
+ /* lerp moved to improved perlin above */\r
+\r
+ sx = this.surve(rx0);\r
+ sy = this.surve(ry0);\r
+ sz = this.surve(rz0);\r
+\r
+ float[] q = new float[3];\r
+ q = g[b00 + bz0];\r
+ u = this.at(rx0, ry0, rz0, q);\r
+ q = g[b10 + bz0];\r
+ v = this.at(rx1, ry0, rz0, q);\r
+ a = this.lerp(sx, u, v);\r
+\r
+ q = g[b01 + bz0];\r
+ u = this.at(rx0, ry1, rz0, q);\r
+ q = g[b11 + bz0];\r
+ v = this.at(rx1, ry1, rz0, q);\r
+ b = this.lerp(sx, u, v);\r
+\r
+ c = this.lerp(sy, a, b); /* interpolate in y at lo x */\r
+\r
+ q = g[b00 + bz1];\r
+ u = this.at(rx0, ry0, rz1, q);\r
+ q = g[b10 + bz1];\r
+ v = this.at(rx1, ry0, rz1, q);\r
+ a = this.lerp(sx, u, v);\r
+\r
+ q = g[b01 + bz1];\r
+ u = this.at(rx0, ry1, rz1, q);\r
+ q = g[b11 + bz1];\r
+ v = this.at(rx1, ry1, rz1, q);\r
+ b = this.lerp(sx, u, v);\r
+\r
+ d = this.lerp(sy, a, b); /* interpolate in y at hi x */\r
+\r
+ return 1.5f * this.lerp(sz, c, d); /* interpolate in z */\r
+ }\r
+\r
+ public float orgBlenderNoise(float x, float y, float z) {\r
+ float cn1, cn2, cn3, cn4, cn5, cn6, i;\r
+ float ox, oy, oz, jx, jy, jz;\r
+ float n = 0.5f;\r
+ int ix, iy, iz, b00, b01, b10, b11, b20, b21;\r
+\r
+ ox = x - (ix = (int) Math.floor(x));\r
+ oy = y - (iy = (int) Math.floor(y));\r
+ oz = z - (iz = (int) Math.floor(z));\r
+\r
+ jx = ox - 1;\r
+ jy = oy - 1;\r
+ jz = oz - 1;\r
+\r
+ cn1 = ox * ox;\r
+ cn2 = oy * oy;\r
+ cn3 = oz * oz;\r
+ cn4 = jx * jx;\r
+ cn5 = jy * jy;\r
+ cn6 = jz * jz;\r
+\r
+ cn1 = 1.0f - 3.0f * cn1 + 2.0f * cn1 * ox;\r
+ cn2 = 1.0f - 3.0f * cn2 + 2.0f * cn2 * oy;\r
+ cn3 = 1.0f - 3.0f * cn3 + 2.0f * cn3 * oz;\r
+ cn4 = 1.0f - 3.0f * cn4 - 2.0f * cn4 * jx;\r
+ cn5 = 1.0f - 3.0f * cn5 - 2.0f * cn5 * jy;\r
+ cn6 = 1.0f - 3.0f * cn6 - 2.0f * cn6 * jz;\r
+\r
+ b00 = hash[hash[ix & 255] + (iy & 255)];\r
+ b10 = hash[hash[ix + 1 & 255] + (iy & 255)];\r
+ b01 = hash[hash[ix & 255] + (iy + 1 & 255)];\r
+ b11 = hash[hash[ix + 1 & 255] + (iy + 1 & 255)];\r
+\r
+ b20 = iz & 255;\r
+ b21 = iz + 1 & 255;\r
+\r
+ /* 0 */\r
+ i = cn1 * cn2 * cn3;\r
+ int hIndex = 3 * hash[b20 + b00];\r
+ n += i * (hashvectf[hIndex] * ox + hashvectf[hIndex + 1] * oy + hashvectf[hIndex + 2] * oz);\r
+ /* 1 */\r
+ i = cn1 * cn2 * cn6;\r
+ hIndex = 3 * hash[b21 + b00];\r
+ n += i * (hashvectf[hIndex] * ox + hashvectf[hIndex + 1] * oy + hashvectf[hIndex + 2] * jz);\r
+ /* 2 */\r
+ i = cn1 * cn5 * cn3;\r
+ hIndex = 3 * hash[b20 + b01];\r
+ n += i * (hashvectf[hIndex] * ox + hashvectf[hIndex + 1] * jy + hashvectf[hIndex + 2] * oz);\r
+ /* 3 */\r
+ i = cn1 * cn5 * cn6;\r
+ hIndex = 3 * hash[b21 + b01];\r
+ n += i * (hashvectf[hIndex] * ox + hashvectf[hIndex + 1] * jy + hashvectf[hIndex + 2] * jz);\r
+ /* 4 */\r
+ i = cn4 * cn2 * cn3;\r
+ hIndex = 3 * hash[b20 + b10];\r
+ n += i * (hashvectf[hIndex] * jx + hashvectf[hIndex + 1] * oy + hashvectf[hIndex + 2] * oz);\r
+ /* 5 */\r
+ i = cn4 * cn2 * cn6;\r
+ hIndex = 3 * hash[b21 + b10];\r
+ n += i * (hashvectf[hIndex] * jx + hashvectf[hIndex + 1] * oy + hashvectf[hIndex + 2] * jz);\r
+ /* 6 */\r
+ i = cn4 * cn5 * cn3;\r
+ hIndex = 3 * hash[b20 + b11];\r
+ n += i * (hashvectf[hIndex] * jx + hashvectf[hIndex + 1] * jy + hashvectf[hIndex + 2] * oz);\r
+ /* 7 */\r
+ i = cn4 * cn5 * cn6;\r
+ hIndex = 3 * hash[b21 + b11];\r
+ n += i * (hashvectf[hIndex] * jx + hashvectf[hIndex + 1] * jy + hashvectf[hIndex + 2] * jz);\r
+\r
+ if (n < 0.0f) {\r
+ n = 0.0f;\r
+ } else if (n > 1.0f) {\r
+ n = 1.0f;\r
+ }\r
+ return n;\r
+ }\r
+\r
+ /* instead of adding another permutation array, just use hash table defined above */\r
+ public float newPerlin(float x, float y, float z) {\r
+ int A, AA, AB, B, BA, BB;\r
+ float u = (float) Math.floor(x), v = (float) Math.floor(y), w = (float) Math.floor(z);\r
+ int X = (int) u & 255, Y = (int) v & 255, Z = (int) w & 255; // FIND UNIT CUBE THAT CONTAINS POINT\r
+ x -= u; // FIND RELATIVE X,Y,Z\r
+ y -= v; // OF POINT IN CUBE.\r
+ z -= w;\r
+ u = this.npfade(x); // COMPUTE FADE CURVES\r
+ v = this.npfade(y); // FOR EACH OF X,Y,Z.\r
+ w = this.npfade(z);\r
+ A = hash[X] + Y;\r
+ AA = hash[A] + Z;\r
+ AB = hash[A + 1] + Z; // HASH COORDINATES OF\r
+ B = hash[X + 1] + Y;\r
+ BA = hash[B] + Z;\r
+ BB = hash[B + 1] + Z; // THE 8 CUBE CORNERS,\r
+ return this.lerp(w, this.lerp(v, this.lerp(u, this.grad(hash[AA], x, y, z), // AND ADD\r
+ this.grad(hash[BA], x - 1, y, z)), // BLENDED\r
+ this.lerp(u, this.grad(hash[AB], x, y - 1, z), // RESULTS\r
+ this.grad(hash[BB], x - 1, y - 1, z))),// FROM 8\r
+ this.lerp(v, this.lerp(u, this.grad(hash[AA + 1], x, y, z - 1), // CORNERS\r
+ this.grad(hash[BA + 1], x - 1, y, z - 1)), // OF CUBE\r
+ this.lerp(u, this.grad(hash[AB + 1], x, y - 1, z - 1), this.grad(hash[BB + 1], x - 1, y - 1, z - 1))));\r
+ }\r
+\r
+ /**\r
+ * Returns a vector/point/color in ca, using point hasharray directly\r
+ */\r
+ protected static void cellNoiseV(float x, float y, float z, float[] ca) {\r
+ int xi = (int) Math.floor(x);\r
+ int yi = (int) Math.floor(y);\r
+ int zi = (int) Math.floor(z);\r
+ float[] p = AbstractNoiseFunc.hashPoint(xi, yi, zi);\r
+ ca[0] = p[0];\r
+ ca[1] = p[1];\r
+ ca[2] = p[2];\r
+ }\r
+\r
+ protected float lerp(float t, float a, float b) {\r
+ return a + t * (b - a);\r
+ }\r
+\r
+ protected float npfade(float t) {\r
+ return t * t * t * (t * (t * 6.0f - 15.0f) + 10.0f);\r
+ }\r
+\r
+ protected float grad(int hash, float x, float y, float z) {\r
+ int h = hash & 0x0F; // CONVERT LO 4 BITS OF HASH CODE\r
+ float u = h < 8 ? x : y, // INTO 12 GRADIENT DIRECTIONS.\r
+ v = h < 4 ? y : h == 12 || h == 14 ? x : z;\r
+ return ((h & 1) == 0 ? u : -u) + ((h & 2) == 0 ? v : -v);\r
+ }\r
+\r
+ /**\r
+ * Dot product of two vectors.\r
+ * @param a\r
+ * the first vector\r
+ * @param b\r
+ * the second vector\r
+ * @return the dot product of two vectors\r
+ */\r
+ protected float dot(float[] a, float[] b) {\r
+ return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];\r
+ }\r
+\r
+ protected float surve(float t) {\r
+ return t * t * (3.0f - 2.0f * t);\r
+ }\r
+\r
+ protected float at(float rx, float ry, float rz, float[] q) {\r
+ return rx * q[0] + ry * q[1] + rz * q[2];\r
+ }\r
+ }\r
+\r
+ /**\r
+ * This interface is used for distance calculation classes. Distance metrics for voronoi. e parameter only used in\r
+ * Minkovsky.\r
+ */\r
+ interface DistanceFunc {\r
+\r
+ /**\r
+ * This method calculates the distance for voronoi algorithms.\r
+ * @param x\r
+ * the x coordinate\r
+ * @param y\r
+ * the y coordinate\r
+ * @param z\r
+ * the z coordinate\r
+ * @param e\r
+ * this parameter used in Monkovsky (no idea what it really is ;)\r
+ * @return\r
+ */\r
+ float execute(float x, float y, float z, float e);\r
+ }\r
+\r
+ interface MusgraveFunction {\r
+\r
+ float execute(Structure tex, float x, float y, float z);\r
+ }\r
}\r
* @author Marcin Roguski\r
*/\r
public abstract class AbstractInfluenceFunction {\r
- protected static final Logger LOGGER = Logger.getLogger(AbstractInfluenceFunction.class.getName());\r
\r
- protected static final float IK_SOLVER_ERROR = 0.5f;\r
-\r
- //DISTLIMIT\r
- protected static final int LIMITDIST_INSIDE = 0;\r
- protected static final int LIMITDIST_OUTSIDE = 1;\r
- protected static final int LIMITDIST_ONSURFACE = 2;\r
-\r
- //CONSTRAINT_TYPE_LOCLIKE\r
- protected static final int LOCLIKE_X = 0x01;\r
- protected static final int LOCLIKE_Y = 0x02;\r
- protected static final int LOCLIKE_Z = 0x04;\r
-\r
- //ROTLIKE\r
- protected static final int ROTLIKE_X = 0x01;\r
- protected static final int ROTLIKE_Y = 0x02;\r
- protected static final int ROTLIKE_Z = 0x04;\r
- protected static final int ROTLIKE_X_INVERT = 0x10;\r
- protected static final int ROTLIKE_Y_INVERT = 0x20;\r
- protected static final int ROTLIKE_Z_INVERT = 0x40;\r
- protected static final int ROTLIKE_OFFSET = 0x80;\r
-\r
- //SIZELIKE\r
- protected static final int SIZELIKE_X = 0x01;\r
- protected static final int SIZELIKE_Y = 0x02;\r
- protected static final int SIZELIKE_Z = 0x04;\r
- protected static final int SIZELIKE_OFFSET = 0x80;\r
-\r
- /* LOCLIKE_TIP is a depreceated option... use headtail=1.0f instead */\r
- //protected static final int LOCLIKE_TIP = 0x08;\r
- protected static final int LOCLIKE_X_INVERT = 0x10;\r
- protected static final int LOCLIKE_Y_INVERT = 0x20;\r
- protected static final int LOCLIKE_Z_INVERT = 0x40;\r
- protected static final int LOCLIKE_OFFSET = 0x80;\r
-\r
- //LOCLIMIT, SIZELIMIT\r
- protected static final int LIMIT_XMIN = 0x01;\r
- protected static final int LIMIT_XMAX = 0x02;\r
- protected static final int LIMIT_YMIN = 0x04;\r
- protected static final int LIMIT_YMAX = 0x08;\r
- protected static final int LIMIT_ZMIN = 0x10;\r
- protected static final int LIMIT_ZMAX = 0x20;\r
-\r
- //ROTLIMIT\r
- protected static final int LIMIT_XROT = 0x01;\r
- protected static final int LIMIT_YROT = 0x02;\r
- protected static final int LIMIT_ZROT = 0x04;\r
-\r
- /** The type of the constraint. */\r
- protected ConstraintType constraintType;\r
- /** The data repository. */\r
- protected DataRepository dataRepository;\r
-\r
- /**\r
- * Constructor.\r
- * @param constraintType\r
- * the type of the current constraint\r
- * @param dataRepository\r
- * the data repository\r
- */\r
- public AbstractInfluenceFunction(ConstraintType constraintType, DataRepository dataRepository) {\r
- this.constraintType = constraintType;\r
- this.dataRepository = dataRepository;\r
- }\r
-\r
- /**\r
- * This method validates the constraint type. It throws an IllegalArgumentException if the constraint type of the\r
- * given structure is invalid.\r
- * @param constraintStructure\r
- * the structure with constraint data\r
- */\r
- protected void validateConstraintType(Structure constraintStructure) {\r
- if(!constraintType.getClassName().equalsIgnoreCase(constraintStructure.getType())) {\r
- throw new IllegalArgumentException("Invalud structure type (" + constraintStructure.getType() + ") for the constraint: " + constraintType.getClassName() + '!');\r
- }\r
- }\r
-\r
- /**\r
- * This method affects the bone animation tracks for the given skeleton.\r
- * @param skeleton\r
- * the skeleton containing the affected bones by constraint\r
- * @param boneAnimation\r
- * the bone animation baked traces\r
- * @param constraint\r
- * the constraint\r
- */\r
- public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation, Constraint constraint) {}\r
-\r
- /**\r
- * This method returns the bone traces for the bone that is affected by the given constraint.\r
- * @param skeleton\r
- * the skeleton containing bones\r
- * @param boneAnimation\r
- * the bone animation that affects the skeleton\r
- * @param constraint\r
- * the affecting constraint\r
- * @return the bone track for the bone that is being affected by the constraint\r
- */\r
- protected BoneTrack getBoneTrack(Skeleton skeleton, BoneAnimation boneAnimation, Constraint constraint) {\r
- Long boneOMA = constraint.getBoneOMA();\r
- Bone bone = (Bone)dataRepository.getLoadedFeature(boneOMA, LoadedFeatureDataType.LOADED_FEATURE);\r
- int boneIndex = skeleton.getBoneIndex(bone);\r
- if(boneIndex != -1) {\r
- //searching for track for this bone\r
- for(BoneTrack boneTrack : boneAnimation.getTracks()) {\r
- if(boneTrack.getTargetBoneIndex() == boneIndex) {\r
- return boneTrack;\r
- }\r
- }\r
- }\r
- return null;\r
- }\r
-\r
- /**\r
- * This method returns the target or subtarget object (if specified).\r
- * @param constraint\r
- * the constraint instance\r
- * @return target or subtarget feature\r
- */\r
- protected Object getTarget(Constraint constraint, LoadedFeatureDataType loadedFeatureDataType) {\r
- Long targetOMA = ((Pointer)constraint.getData().getFieldValue("tar")).getOldMemoryAddress();\r
- Object targetObject = dataRepository.getLoadedFeature(targetOMA, loadedFeatureDataType);\r
- String subtargetName = constraint.getData().getFieldValue("subtarget").toString();\r
- if(subtargetName.length() > 0) {\r
- return dataRepository.getLoadedFeature(subtargetName, loadedFeatureDataType);\r
- }\r
- return targetObject;\r
- }\r
-\r
- /**\r
- * This method returns target's object location.\r
- * @param constraint\r
- * the constraint instance\r
- * @return target's object location\r
- */\r
- protected Vector3f getTargetLocation(Constraint constraint) {\r
- Long targetOMA = ((Pointer)constraint.getData().getFieldValue("tar")).getOldMemoryAddress();\r
- Space targetSpace = constraint.getTargetSpace();\r
- Node targetObject = (Node)dataRepository.getLoadedFeature(targetOMA, LoadedFeatureDataType.LOADED_FEATURE);\r
- switch(targetSpace) {\r
- case CONSTRAINT_SPACE_LOCAL:\r
- return targetObject.getLocalTranslation();\r
- case CONSTRAINT_SPACE_WORLD:\r
- return targetObject.getWorldTranslation();\r
- default:\r
- throw new IllegalStateException("Invalid space type for target object: " + targetSpace.toString());\r
- }\r
- }\r
- \r
- /**\r
- * This method returns target's object location in the specified frame.\r
- * @param constraint\r
- * the constraint instance\r
- * @param frame\r
- * the frame number\r
- * @return target's object location\r
- */\r
- protected Vector3f getTargetLocation(Constraint constraint, int frame) {\r
- return this.getTargetLocation(constraint);//TODO: implement getting location in a specified frame\r
- }\r
-\r
- /**\r
- * This method returns target's object rotation.\r
- * @param constraint\r
- * the constraint instance\r
- * @return target's object rotation\r
- */\r
- protected Quaternion getTargetRotation(Constraint constraint) {\r
- Long targetOMA = ((Pointer)constraint.getData().getFieldValue("tar")).getOldMemoryAddress();\r
- Space targetSpace = constraint.getTargetSpace();\r
- Node targetObject = (Node)dataRepository.getLoadedFeature(targetOMA, LoadedFeatureDataType.LOADED_FEATURE);\r
- switch(targetSpace) {\r
- case CONSTRAINT_SPACE_LOCAL:\r
- return targetObject.getLocalRotation();\r
- case CONSTRAINT_SPACE_WORLD:\r
- return targetObject.getWorldRotation();\r
- default:\r
- throw new IllegalStateException("Invalid space type for target object: " + targetSpace.toString());\r
- }\r
- }\r
-\r
- /**\r
- * This method returns target's object scale.\r
- * @param constraint\r
- * the constraint instance\r
- * @return target's object scale\r
- */\r
- protected Vector3f getTargetScale(Constraint constraint) {\r
- Long targetOMA = ((Pointer)constraint.getData().getFieldValue("tar")).getOldMemoryAddress();\r
- Space targetSpace = constraint.getTargetSpace();\r
- Node targetObject = (Node)dataRepository.getLoadedFeature(targetOMA, LoadedFeatureDataType.LOADED_FEATURE);\r
- switch(targetSpace) {\r
- case CONSTRAINT_SPACE_LOCAL:\r
- return targetObject.getLocalScale();\r
- case CONSTRAINT_SPACE_WORLD:\r
- return targetObject.getWorldScale();\r
- default:\r
- throw new IllegalStateException("Invalid space type for target object: " + targetSpace.toString());\r
- }\r
- }\r
+ protected static final Logger LOGGER = Logger.getLogger(AbstractInfluenceFunction.class.getName());\r
+ protected static final float IK_SOLVER_ERROR = 0.5f;\r
+ //DISTLIMIT\r
+ protected static final int LIMITDIST_INSIDE = 0;\r
+ protected static final int LIMITDIST_OUTSIDE = 1;\r
+ protected static final int LIMITDIST_ONSURFACE = 2;\r
+ //CONSTRAINT_TYPE_LOCLIKE\r
+ protected static final int LOCLIKE_X = 0x01;\r
+ protected static final int LOCLIKE_Y = 0x02;\r
+ protected static final int LOCLIKE_Z = 0x04;\r
+ //ROTLIKE\r
+ protected static final int ROTLIKE_X = 0x01;\r
+ protected static final int ROTLIKE_Y = 0x02;\r
+ protected static final int ROTLIKE_Z = 0x04;\r
+ protected static final int ROTLIKE_X_INVERT = 0x10;\r
+ protected static final int ROTLIKE_Y_INVERT = 0x20;\r
+ protected static final int ROTLIKE_Z_INVERT = 0x40;\r
+ protected static final int ROTLIKE_OFFSET = 0x80;\r
+ //SIZELIKE\r
+ protected static final int SIZELIKE_X = 0x01;\r
+ protected static final int SIZELIKE_Y = 0x02;\r
+ protected static final int SIZELIKE_Z = 0x04;\r
+ protected static final int SIZELIKE_OFFSET = 0x80;\r
+\r
+ /* LOCLIKE_TIP is a depreceated option... use headtail=1.0f instead */\r
+ //protected static final int LOCLIKE_TIP = 0x08;\r
+ protected static final int LOCLIKE_X_INVERT = 0x10;\r
+ protected static final int LOCLIKE_Y_INVERT = 0x20;\r
+ protected static final int LOCLIKE_Z_INVERT = 0x40;\r
+ protected static final int LOCLIKE_OFFSET = 0x80;\r
+ //LOCLIMIT, SIZELIMIT\r
+ protected static final int LIMIT_XMIN = 0x01;\r
+ protected static final int LIMIT_XMAX = 0x02;\r
+ protected static final int LIMIT_YMIN = 0x04;\r
+ protected static final int LIMIT_YMAX = 0x08;\r
+ protected static final int LIMIT_ZMIN = 0x10;\r
+ protected static final int LIMIT_ZMAX = 0x20;\r
+ //ROTLIMIT\r
+ protected static final int LIMIT_XROT = 0x01;\r
+ protected static final int LIMIT_YROT = 0x02;\r
+ protected static final int LIMIT_ZROT = 0x04;\r
+ /** The type of the constraint. */\r
+ protected ConstraintType constraintType;\r
+ /** The data repository. */\r
+ protected DataRepository dataRepository;\r
+\r
+ /**\r
+ * Constructor.\r
+ * @param constraintType\r
+ * the type of the current constraint\r
+ * @param dataRepository\r
+ * the data repository\r
+ */\r
+ public AbstractInfluenceFunction(ConstraintType constraintType, DataRepository dataRepository) {\r
+ this.constraintType = constraintType;\r
+ this.dataRepository = dataRepository;\r
+ }\r
+\r
+ /**\r
+ * This method validates the constraint type. It throws an IllegalArgumentException if the constraint type of the\r
+ * given structure is invalid.\r
+ * @param constraintStructure\r
+ * the structure with constraint data\r
+ */\r
+ protected void validateConstraintType(Structure constraintStructure) {\r
+ if (!constraintType.getClassName().equalsIgnoreCase(constraintStructure.getType())) {\r
+ throw new IllegalArgumentException("Invalud structure type (" + constraintStructure.getType() + ") for the constraint: " + constraintType.getClassName() + '!');\r
+ }\r
+ }\r
+\r
+ /**\r
+ * This method affects the bone animation tracks for the given skeleton.\r
+ * @param skeleton\r
+ * the skeleton containing the affected bones by constraint\r
+ * @param boneAnimation\r
+ * the bone animation baked traces\r
+ * @param constraint\r
+ * the constraint\r
+ */\r
+ public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation, Constraint constraint) {\r
+ }\r
+\r
+ /**\r
+ * This method returns the bone traces for the bone that is affected by the given constraint.\r
+ * @param skeleton\r
+ * the skeleton containing bones\r
+ * @param boneAnimation\r
+ * the bone animation that affects the skeleton\r
+ * @param constraint\r
+ * the affecting constraint\r
+ * @return the bone track for the bone that is being affected by the constraint\r
+ */\r
+ protected BoneTrack getBoneTrack(Skeleton skeleton, BoneAnimation boneAnimation, Constraint constraint) {\r
+ Long boneOMA = constraint.getBoneOMA();\r
+ Bone bone = (Bone) dataRepository.getLoadedFeature(boneOMA, LoadedFeatureDataType.LOADED_FEATURE);\r
+ int boneIndex = skeleton.getBoneIndex(bone);\r
+ if (boneIndex != -1) {\r
+ //searching for track for this bone\r
+ for (BoneTrack boneTrack : boneAnimation.getTracks()) {\r
+ if (boneTrack.getTargetBoneIndex() == boneIndex) {\r
+ return boneTrack;\r
+ }\r
+ }\r
+ }\r
+ return null;\r
+ }\r
+\r
+ /**\r
+ * This method returns the target or subtarget object (if specified).\r
+ * @param constraint\r
+ * the constraint instance\r
+ * @return target or subtarget feature\r
+ */\r
+ protected Object getTarget(Constraint constraint, LoadedFeatureDataType loadedFeatureDataType) {\r
+ Long targetOMA = ((Pointer) constraint.getData().getFieldValue("tar")).getOldMemoryAddress();\r
+ Object targetObject = dataRepository.getLoadedFeature(targetOMA, loadedFeatureDataType);\r
+ String subtargetName = constraint.getData().getFieldValue("subtarget").toString();\r
+ if (subtargetName.length() > 0) {\r
+ return dataRepository.getLoadedFeature(subtargetName, loadedFeatureDataType);\r
+ }\r
+ return targetObject;\r
+ }\r
+\r
+ /**\r
+ * This method returns target's object location.\r
+ * @param constraint\r
+ * the constraint instance\r
+ * @return target's object location\r
+ */\r
+ protected Vector3f getTargetLocation(Constraint constraint) {\r
+ Long targetOMA = ((Pointer) constraint.getData().getFieldValue("tar")).getOldMemoryAddress();\r
+ Space targetSpace = constraint.getTargetSpace();\r
+ Node targetObject = (Node) dataRepository.getLoadedFeature(targetOMA, LoadedFeatureDataType.LOADED_FEATURE);\r
+ switch (targetSpace) {\r
+ case CONSTRAINT_SPACE_LOCAL:\r
+ return targetObject.getLocalTranslation();\r
+ case CONSTRAINT_SPACE_WORLD:\r
+ return targetObject.getWorldTranslation();\r
+ default:\r
+ throw new IllegalStateException("Invalid space type for target object: " + targetSpace.toString());\r
+ }\r
+ }\r
+\r
+ /**\r
+ * This method returns target's object location in the specified frame.\r
+ * @param constraint\r
+ * the constraint instance\r
+ * @param frame\r
+ * the frame number\r
+ * @return target's object location\r
+ */\r
+ protected Vector3f getTargetLocation(Constraint constraint, int frame) {\r
+ return this.getTargetLocation(constraint);//TODO: implement getting location in a specified frame\r
+ }\r
+\r
+ /**\r
+ * This method returns target's object rotation.\r
+ * @param constraint\r
+ * the constraint instance\r
+ * @return target's object rotation\r
+ */\r
+ protected Quaternion getTargetRotation(Constraint constraint) {\r
+ Long targetOMA = ((Pointer) constraint.getData().getFieldValue("tar")).getOldMemoryAddress();\r
+ Space targetSpace = constraint.getTargetSpace();\r
+ Node targetObject = (Node) dataRepository.getLoadedFeature(targetOMA, LoadedFeatureDataType.LOADED_FEATURE);\r
+ switch (targetSpace) {\r
+ case CONSTRAINT_SPACE_LOCAL:\r
+ return targetObject.getLocalRotation();\r
+ case CONSTRAINT_SPACE_WORLD:\r
+ return targetObject.getWorldRotation();\r
+ default:\r
+ throw new IllegalStateException("Invalid space type for target object: " + targetSpace.toString());\r
+ }\r
+ }\r
+\r
+ /**\r
+ * This method returns target's object scale.\r
+ * @param constraint\r
+ * the constraint instance\r
+ * @return target's object scale\r
+ */\r
+ protected Vector3f getTargetScale(Constraint constraint) {\r
+ Long targetOMA = ((Pointer) constraint.getData().getFieldValue("tar")).getOldMemoryAddress();\r
+ Space targetSpace = constraint.getTargetSpace();\r
+ Node targetObject = (Node) dataRepository.getLoadedFeature(targetOMA, LoadedFeatureDataType.LOADED_FEATURE);\r
+ switch (targetSpace) {\r
+ case CONSTRAINT_SPACE_LOCAL:\r
+ return targetObject.getLocalScale();\r
+ case CONSTRAINT_SPACE_WORLD:\r
+ return targetObject.getWorldScale();\r
+ default:\r
+ throw new IllegalStateException("Invalid space type for target object: " + targetSpace.toString());\r
+ }\r
+ }\r
}\r
* @author Marcin Roguski\r
*/\r
public class BezierCurve {\r
- public static final int X_VALUE = 0;\r
- public static final int Y_VALUE = 1;\r
- public static final int Z_VALUE = 2;\r
\r
- /** \r
- * The type of the curve. Describes the data it modifies. \r
- * Used in ipos calculations.\r
- */\r
- private int type;\r
- /** The dimension of the curve. */\r
- private int dimension;\r
- /** A table of the bezier points. */\r
- private float[][][] bezierPoints;\r
+ public static final int X_VALUE = 0;\r
+ public static final int Y_VALUE = 1;\r
+ public static final int Z_VALUE = 2;\r
+ /** \r
+ * The type of the curve. Describes the data it modifies. \r
+ * Used in ipos calculations.\r
+ */\r
+ private int type;\r
+ /** The dimension of the curve. */\r
+ private int dimension;\r
+ /** A table of the bezier points. */\r
+ private float[][][] bezierPoints;\r
\r
- @SuppressWarnings("unchecked")\r
- public BezierCurve(final int type, final List<Structure> bezTriples, final int dimension) {\r
- if(dimension != 2 && dimension != 3) {\r
- throw new IllegalArgumentException("The dimension of the curve should be 2 or 3!");\r
- }\r
- this.type = type;\r
- this.dimension = dimension;\r
- //first index of the bezierPoints table has the length of triples amount\r
- //the second index points to a table od three points of a bezier triple (handle, point, handle)\r
- //the third index specifies the coordinates of the specific point in a bezier triple\r
- bezierPoints = new float[bezTriples.size()][3][dimension];\r
- int i = 0, j, k;\r
- for(Structure bezTriple : bezTriples) {\r
- DynamicArray<Number> vec = (DynamicArray<Number>)bezTriple.getFieldValue("vec");\r
- for(j = 0; j < 3; ++j) {\r
- for(k = 0; k < dimension; ++k) {\r
- bezierPoints[i][j][k] = vec.get(j, k).floatValue();\r
- }\r
- }\r
- ++i;\r
- }\r
- }\r
+ @SuppressWarnings("unchecked")\r
+ public BezierCurve(final int type, final List<Structure> bezTriples, final int dimension) {\r
+ if (dimension != 2 && dimension != 3) {\r
+ throw new IllegalArgumentException("The dimension of the curve should be 2 or 3!");\r
+ }\r
+ this.type = type;\r
+ this.dimension = dimension;\r
+ //first index of the bezierPoints table has the length of triples amount\r
+ //the second index points to a table od three points of a bezier triple (handle, point, handle)\r
+ //the third index specifies the coordinates of the specific point in a bezier triple\r
+ bezierPoints = new float[bezTriples.size()][3][dimension];\r
+ int i = 0, j, k;\r
+ for (Structure bezTriple : bezTriples) {\r
+ DynamicArray<Number> vec = (DynamicArray<Number>) bezTriple.getFieldValue("vec");\r
+ for (j = 0; j < 3; ++j) {\r
+ for (k = 0; k < dimension; ++k) {\r
+ bezierPoints[i][j][k] = vec.get(j, k).floatValue();\r
+ }\r
+ }\r
+ ++i;\r
+ }\r
+ }\r
\r
- /**\r
- * This method evaluates the data for the specified frame. The Y value is returned.\r
- * @param frame\r
- * the frame for which the value is being calculated\r
- * @param valuePart\r
- * this param specifies wheather we should return the X, Y or Z part of the result value; it should have\r
- * one of the following values: X_VALUE - the X factor of the result Y_VALUE - the Y factor of the result\r
- * Z_VALUE - the Z factor of the result\r
- * @return the value of the curve\r
- */\r
- public float evaluate(int frame, int valuePart) {\r
- for(int i = 0; i < bezierPoints.length - 1; ++i) {\r
- if(frame >= bezierPoints[i][1][0] && frame <= bezierPoints[i + 1][1][0]) {\r
- float t = (frame - bezierPoints[i][1][0]) / (bezierPoints[i + 1][1][0] - bezierPoints[i][1][0]);\r
- float oneMinusT = 1.0f - t;\r
- float oneMinusT2 = oneMinusT * oneMinusT;\r
- float t2 = t * t;\r
- return bezierPoints[i][1][valuePart] * oneMinusT2 * oneMinusT + 3.0f * bezierPoints[i][2][valuePart] * t * oneMinusT2 + 3.0f * bezierPoints[i + 1][0][valuePart] * t2 * oneMinusT + bezierPoints[i + 1][1][valuePart] * t2 * t;\r
- }\r
- }\r
- if(frame < bezierPoints[0][1][0]) {\r
- return bezierPoints[0][1][1];\r
- } else { //frame>bezierPoints[bezierPoints.length-1][1][0]\r
- return bezierPoints[bezierPoints.length - 1][1][1];\r
- }\r
- }\r
+ /**\r
+ * This method evaluates the data for the specified frame. The Y value is returned.\r
+ * @param frame\r
+ * the frame for which the value is being calculated\r
+ * @param valuePart\r
+ * this param specifies wheather we should return the X, Y or Z part of the result value; it should have\r
+ * one of the following values: X_VALUE - the X factor of the result Y_VALUE - the Y factor of the result\r
+ * Z_VALUE - the Z factor of the result\r
+ * @return the value of the curve\r
+ */\r
+ public float evaluate(int frame, int valuePart) {\r
+ for (int i = 0; i < bezierPoints.length - 1; ++i) {\r
+ if (frame >= bezierPoints[i][1][0] && frame <= bezierPoints[i + 1][1][0]) {\r
+ float t = (frame - bezierPoints[i][1][0]) / (bezierPoints[i + 1][1][0] - bezierPoints[i][1][0]);\r
+ float oneMinusT = 1.0f - t;\r
+ float oneMinusT2 = oneMinusT * oneMinusT;\r
+ float t2 = t * t;\r
+ return bezierPoints[i][1][valuePart] * oneMinusT2 * oneMinusT + 3.0f * bezierPoints[i][2][valuePart] * t * oneMinusT2 + 3.0f * bezierPoints[i + 1][0][valuePart] * t2 * oneMinusT + bezierPoints[i + 1][1][valuePart] * t2 * t;\r
+ }\r
+ }\r
+ if (frame < bezierPoints[0][1][0]) {\r
+ return bezierPoints[0][1][1];\r
+ } else { //frame>bezierPoints[bezierPoints.length-1][1][0]\r
+ return bezierPoints[bezierPoints.length - 1][1][1];\r
+ }\r
+ }\r
\r
- /**\r
- * This method returns the frame where last bezier triple center point of the bezier curve is located.\r
- * @return the frame number of the last defined bezier triple point for the curve\r
- */\r
- public int getLastFrame() {\r
- return (int)bezierPoints[bezierPoints.length - 1][1][0];\r
- }\r
- \r
- /**\r
- * This method returns the type of the bezier curve. The type describes the parameter that this curve modifies\r
- * (ie. LocationX or rotationW of the feature).\r
- * @return the type of the bezier curve\r
- */\r
- public int getType() {\r
- return type;\r
- }\r
- \r
- /**\r
- * This method returns a list of control points for this curve.\r
- * @return a list of control points for this curve.\r
- */\r
- public List<Vector3f> getControlPoints() {\r
- List<Vector3f> controlPoints = new ArrayList<Vector3f>(bezierPoints.length * 3);\r
- for(int i = 0;i<bezierPoints.length;++i) {\r
- controlPoints.add(new Vector3f(bezierPoints[i][0][0], bezierPoints[i][0][1], bezierPoints[i][0][2]));\r
- controlPoints.add(new Vector3f(bezierPoints[i][1][0], bezierPoints[i][1][1], bezierPoints[i][1][2]));\r
- controlPoints.add(new Vector3f(bezierPoints[i][2][0], bezierPoints[i][2][1], bezierPoints[i][2][2]));\r
- }\r
- return controlPoints;\r
- }\r
+ /**\r
+ * This method returns the frame where last bezier triple center point of the bezier curve is located.\r
+ * @return the frame number of the last defined bezier triple point for the curve\r
+ */\r
+ public int getLastFrame() {\r
+ return (int) bezierPoints[bezierPoints.length - 1][1][0];\r
+ }\r
\r
- @Override\r
- public String toString() {\r
- StringBuilder sb = new StringBuilder("Bezier curve: ").append(type).append('\n');\r
- for(int i = 0; i < bezierPoints.length; ++i) {\r
- sb.append(this.toStringBezTriple(i)).append('\n');\r
- }\r
- return sb.toString();\r
- }\r
+ /**\r
+ * This method returns the type of the bezier curve. The type describes the parameter that this curve modifies\r
+ * (ie. LocationX or rotationW of the feature).\r
+ * @return the type of the bezier curve\r
+ */\r
+ public int getType() {\r
+ return type;\r
+ }\r
\r
- /**\r
- * This method converts the bezier triple of a specified index into text.\r
- * @param tripleIndex\r
- * index of the triple\r
- * @return text representation of the triple\r
- */\r
- private String toStringBezTriple(int tripleIndex) {\r
- if(this.dimension==2) {\r
- return "[(" + bezierPoints[tripleIndex][0][0] + ", " + bezierPoints[tripleIndex][0][1] + ") (" \r
- + bezierPoints[tripleIndex][1][0] + ", " + bezierPoints[tripleIndex][1][1] + ") (" \r
- + bezierPoints[tripleIndex][2][0] + ", " + bezierPoints[tripleIndex][2][1] + ")]";\r
- } else {\r
- return "[(" + bezierPoints[tripleIndex][0][0] + ", " + bezierPoints[tripleIndex][0][1] + ", " + bezierPoints[tripleIndex][0][2] + ") (" \r
- + bezierPoints[tripleIndex][1][0] + ", " + bezierPoints[tripleIndex][1][1] + ", " + bezierPoints[tripleIndex][1][2] + ") (" \r
- + bezierPoints[tripleIndex][2][0] + ", " + bezierPoints[tripleIndex][2][1] + ", " + bezierPoints[tripleIndex][2][2] + ")]";\r
- }\r
- }\r
+ /**\r
+ * This method returns a list of control points for this curve.\r
+ * @return a list of control points for this curve.\r
+ */\r
+ public List<Vector3f> getControlPoints() {\r
+ List<Vector3f> controlPoints = new ArrayList<Vector3f>(bezierPoints.length * 3);\r
+ for (int i = 0; i < bezierPoints.length; ++i) {\r
+ controlPoints.add(new Vector3f(bezierPoints[i][0][0], bezierPoints[i][0][1], bezierPoints[i][0][2]));\r
+ controlPoints.add(new Vector3f(bezierPoints[i][1][0], bezierPoints[i][1][1], bezierPoints[i][1][2]));\r
+ controlPoints.add(new Vector3f(bezierPoints[i][2][0], bezierPoints[i][2][1], bezierPoints[i][2][2]));\r
+ }\r
+ return controlPoints;\r
+ }\r
+\r
+ @Override\r
+ public String toString() {\r
+ StringBuilder sb = new StringBuilder("Bezier curve: ").append(type).append('\n');\r
+ for (int i = 0; i < bezierPoints.length; ++i) {\r
+ sb.append(this.toStringBezTriple(i)).append('\n');\r
+ }\r
+ return sb.toString();\r
+ }\r
+\r
+ /**\r
+ * This method converts the bezier triple of a specified index into text.\r
+ * @param tripleIndex\r
+ * index of the triple\r
+ * @return text representation of the triple\r
+ */\r
+ private String toStringBezTriple(int tripleIndex) {\r
+ if (this.dimension == 2) {\r
+ return "[(" + bezierPoints[tripleIndex][0][0] + ", " + bezierPoints[tripleIndex][0][1] + ") ("\r
+ + bezierPoints[tripleIndex][1][0] + ", " + bezierPoints[tripleIndex][1][1] + ") ("\r
+ + bezierPoints[tripleIndex][2][0] + ", " + bezierPoints[tripleIndex][2][1] + ")]";\r
+ } else {\r
+ return "[(" + bezierPoints[tripleIndex][0][0] + ", " + bezierPoints[tripleIndex][0][1] + ", " + bezierPoints[tripleIndex][0][2] + ") ("\r
+ + bezierPoints[tripleIndex][1][0] + ", " + bezierPoints[tripleIndex][1][1] + ", " + bezierPoints[tripleIndex][1][2] + ") ("\r
+ + bezierPoints[tripleIndex][2][0] + ", " + bezierPoints[tripleIndex][2][1] + ", " + bezierPoints[tripleIndex][2][2] + ")]";\r
+ }\r
+ }\r
}
\ No newline at end of file
* @author Marcin Roguski\r
*/\r
public class Constraint {\r
- /** The type of this constraint. */\r
- private final ConstraintType type;\r
- /** The name of this constraint. */\r
- private final String name;\r
- /** The old memory address of the constraint's owner. */\r
- private final Long boneOMA;\r
\r
- private final Space ownerSpace;\r
+ /** The type of this constraint. */\r
+ private final ConstraintType type;\r
+ /** The name of this constraint. */\r
+ private final String name;\r
+ /** The old memory address of the constraint's owner. */\r
+ private final Long boneOMA;\r
+ private final Space ownerSpace;\r
+ private final Space targetSpace;\r
+ /** The structure with constraint's data. */\r
+ private final Structure data;\r
+ /** The ipo object defining influence. */\r
+ private final Ipo ipo;\r
+ /** The influence function of this constraint. */\r
+ private final AbstractInfluenceFunction influenceFunction;\r
\r
- private final Space targetSpace;\r
- /** The structure with constraint's data. */\r
- private final Structure data;\r
- /** The ipo object defining influence. */\r
- private final Ipo ipo;\r
- /** The influence function of this constraint. */\r
- private final AbstractInfluenceFunction influenceFunction;\r
+ /**\r
+ * This constructor creates the constraint instance.\r
+ * @param constraintStructure\r
+ * the constraint's structure (bConstraint clss in blender 2.49).\r
+ * @param influenceFunction\r
+ * the constraint's influence function (taken from ConstraintHelper)\r
+ * @param boneOMA\r
+ * the old memory address of the constraint owner\r
+ * @param influenceIpo\r
+ * the ipo curve of the influence factor\r
+ * @param dataRepository\r
+ * the data repository\r
+ * @throws BlenderFileException\r
+ */\r
+ public Constraint(Structure constraintStructure, AbstractInfluenceFunction influenceFunction, Long boneOMA, Space ownerSpace, Space targetSpace, Ipo influenceIpo, DataRepository dataRepository) throws BlenderFileException {\r
+ if (influenceFunction == null) {\r
+ throw new IllegalArgumentException("Influence function is not defined!");\r
+ }\r
+ Pointer pData = (Pointer) constraintStructure.getFieldValue("data");\r
+ if (!pData.isNull()) {\r
+ data = pData.fetchData(dataRepository.getInputStream()).get(0);\r
+ } else {\r
+ throw new BlenderFileException("The constraint has no data specified!");\r
+ }\r
+ this.boneOMA = boneOMA;\r
+ this.type = ConstraintType.valueOf(((Number) constraintStructure.getFieldValue("type")).intValue());\r
+ this.name = constraintStructure.getFieldValue("name").toString();\r
+ this.ownerSpace = ownerSpace;\r
+ this.targetSpace = targetSpace;\r
+ this.ipo = influenceIpo;\r
+ this.influenceFunction = influenceFunction;\r
+ }\r
\r
- /**\r
- * This constructor creates the constraint instance.\r
- * @param constraintStructure\r
- * the constraint's structure (bConstraint clss in blender 2.49).\r
- * @param influenceFunction\r
- * the constraint's influence function (taken from ConstraintHelper)\r
- * @param boneOMA\r
- * the old memory address of the constraint owner\r
- * @param influenceIpo\r
- * the ipo curve of the influence factor\r
- * @param dataRepository\r
- * the data repository\r
- * @throws BlenderFileException\r
- */\r
- public Constraint(Structure constraintStructure, AbstractInfluenceFunction influenceFunction, Long boneOMA, Space ownerSpace, Space targetSpace, Ipo influenceIpo, DataRepository dataRepository) throws BlenderFileException {\r
- if(influenceFunction == null) {\r
- throw new IllegalArgumentException("Influence function is not defined!");\r
- }\r
- Pointer pData = (Pointer)constraintStructure.getFieldValue("data");\r
- if(!pData.isNull()) {\r
- data = pData.fetchData(dataRepository.getInputStream()).get(0);\r
- } else {\r
- throw new BlenderFileException("The constraint has no data specified!");\r
- }\r
- this.boneOMA = boneOMA;\r
- this.type = ConstraintType.valueOf(((Number)constraintStructure.getFieldValue("type")).intValue());\r
- this.name = constraintStructure.getFieldValue("name").toString();\r
- this.ownerSpace = ownerSpace;\r
- this.targetSpace = targetSpace;\r
- this.ipo = influenceIpo;\r
- this.influenceFunction = influenceFunction;\r
- }\r
+ /**\r
+ * This method returns the name of the constraint.\r
+ * @return the name of the constraint\r
+ */\r
+ public String getName() {\r
+ return name;\r
+ }\r
\r
- /**\r
- * This method returns the name of the constraint.\r
- * @return the name of the constraint\r
- */\r
- public String getName() {\r
- return name;\r
- }\r
+ /**\r
+ * This method returns the old memoty address of the bone this constraint affects.\r
+ * @return the old memory address of the bone this constraint affects\r
+ */\r
+ public Long getBoneOMA() {\r
+ return boneOMA;\r
+ }\r
\r
- /**\r
- * This method returns the old memoty address of the bone this constraint affects.\r
- * @return the old memory address of the bone this constraint affects\r
- */\r
- public Long getBoneOMA() {\r
- return boneOMA;\r
- }\r
+ /**\r
+ * This method returns owner's transform space.\r
+ * @return owner's transform space\r
+ */\r
+ public Space getOwnerSpace() {\r
+ return ownerSpace;\r
+ }\r
\r
- /**\r
- * This method returns owner's transform space.\r
- * @return owner's transform space\r
- */\r
- public Space getOwnerSpace() {\r
- return ownerSpace;\r
- }\r
+ /**\r
+ * This method returns target's transform space.\r
+ * @return target's transform space\r
+ */\r
+ public Space getTargetSpace() {\r
+ return targetSpace;\r
+ }\r
\r
- /**\r
- * This method returns target's transform space.\r
- * @return target's transform space\r
- */\r
- public Space getTargetSpace() {\r
- return targetSpace;\r
- }\r
+ /**\r
+ * This method returns the type of the constraint.\r
+ * @return the type of the constraint\r
+ */\r
+ public ConstraintType getType() {\r
+ return type;\r
+ }\r
\r
- /**\r
- * This method returns the type of the constraint.\r
- * @return the type of the constraint\r
- */\r
- public ConstraintType getType() {\r
- return type;\r
- }\r
+ /**\r
+ * This method returns the constraint's data structure.\r
+ * @return the constraint's data structure\r
+ */\r
+ public Structure getData() {\r
+ return data;\r
+ }\r
\r
- /**\r
- * This method returns the constraint's data structure.\r
- * @return the constraint's data structure\r
- */\r
- public Structure getData() {\r
- return data;\r
- }\r
+ /**\r
+ * This method returns the constraint's influcence curve.\r
+ * @return the constraint's influcence curve\r
+ */\r
+ public Ipo getIpo() {\r
+ return ipo;\r
+ }\r
\r
- /**\r
- * This method returns the constraint's influcence curve.\r
- * @return the constraint's influcence curve\r
- */\r
- public Ipo getIpo() {\r
- return ipo;\r
- }\r
+ /**\r
+ * This method affects the bone animation tracks for the given skeleton.\r
+ * @param skeleton\r
+ * the skeleton containing the affected bones by constraint\r
+ * @param boneAnimation\r
+ * the bone animation baked traces\r
+ * @param constraint\r
+ * the constraint\r
+ */\r
+ public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation) {\r
+ influenceFunction.affectAnimation(skeleton, boneAnimation, this);\r
+ }\r
\r
- /**\r
- * This method affects the bone animation tracks for the given skeleton.\r
- * @param skeleton\r
- * the skeleton containing the affected bones by constraint\r
- * @param boneAnimation\r
- * the bone animation baked traces\r
- * @param constraint\r
- * the constraint\r
- */\r
- public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation) {\r
- influenceFunction.affectAnimation(skeleton, boneAnimation, this);\r
- }\r
+ /**\r
+ * The space of target or owner transformation.\r
+ * @author Marcin Roguski\r
+ */\r
+ public static enum Space {\r
\r
- /**\r
- * The space of target or owner transformation.\r
- * @author Marcin Roguski\r
- */\r
- public static enum Space {\r
- CONSTRAINT_SPACE_WORLD, CONSTRAINT_SPACE_LOCAL, CONSTRAINT_SPACE_POSE, CONSTRAINT_SPACE_PARLOCAL, CONSTRAINT_SPACE_INVALID;\r
+ CONSTRAINT_SPACE_WORLD, CONSTRAINT_SPACE_LOCAL, CONSTRAINT_SPACE_POSE, CONSTRAINT_SPACE_PARLOCAL, CONSTRAINT_SPACE_INVALID;\r
\r
- /**\r
- * This method returns the enum instance when given the appropriate value from the blend file.\r
- * @param c\r
- * the blender's value of the space modifier\r
- * @return the scape enum instance\r
- */\r
- public static Space valueOf(byte c) {\r
- switch(c) {\r
- case 0:\r
- return CONSTRAINT_SPACE_WORLD;\r
- case 1:\r
- return CONSTRAINT_SPACE_LOCAL;\r
- case 2:\r
- return CONSTRAINT_SPACE_POSE;\r
- case 3:\r
- return CONSTRAINT_SPACE_PARLOCAL;\r
- default:\r
- return CONSTRAINT_SPACE_INVALID;\r
- }\r
- }\r
- }\r
+ /**\r
+ * This method returns the enum instance when given the appropriate value from the blend file.\r
+ * @param c\r
+ * the blender's value of the space modifier\r
+ * @return the scape enum instance\r
+ */\r
+ public static Space valueOf(byte c) {\r
+ switch (c) {\r
+ case 0:\r
+ return CONSTRAINT_SPACE_WORLD;\r
+ case 1:\r
+ return CONSTRAINT_SPACE_LOCAL;\r
+ case 2:\r
+ return CONSTRAINT_SPACE_POSE;\r
+ case 3:\r
+ return CONSTRAINT_SPACE_PARLOCAL;\r
+ default:\r
+ return CONSTRAINT_SPACE_INVALID;\r
+ }\r
+ }\r
+ }\r
}
\ No newline at end of file
* @author Marcin Roguski\r
*/\r
public enum ConstraintType {\r
- /* Invalid/legacy constraint */\r
- CONSTRAINT_TYPE_NULL(0, "bNullConstraint"),\r
- /* Unimplemented non longer :) - during constraints recode, Aligorith */\r
- CONSTRAINT_TYPE_CHILDOF(1, "bChildOfConstraint"), \r
- CONSTRAINT_TYPE_KINEMATIC(3, "bKinematicConstraint"), \r
- CONSTRAINT_TYPE_FOLLOWPATH(4, "bFollowPathConstraint"),\r
- /* Unimplemented no longer :) - Aligorith */\r
- CONSTRAINT_TYPE_ROTLIMIT(5, "bRotLimitConstraint"),\r
- /* Unimplemented no longer :) - Aligorith */\r
- CONSTRAINT_TYPE_LOCLIMIT(6, "bLocLimitConstraint"),\r
- /* Unimplemented no longer :) - Aligorith */\r
- CONSTRAINT_TYPE_SIZELIMIT(7, "bSizeLimitConstraint"), \r
- CONSTRAINT_TYPE_ROTLIKE(8, "bRotateLikeConstraint"), \r
- CONSTRAINT_TYPE_LOCLIKE(9, "bLocateLikeConstraint"), \r
- CONSTRAINT_TYPE_SIZELIKE(10, "bSizeLikeConstraint"),\r
- /* Unimplemented no longer :) - Aligorith. Scripts */\r
- CONSTRAINT_TYPE_PYTHON(11, "bPythonConstraint"), \r
- CONSTRAINT_TYPE_ACTION(12, "bActionConstraint"),\r
- /* New Tracking constraint that locks an axis in place - theeth */\r
- CONSTRAINT_TYPE_LOCKTRACK(13, "bLockTrackConstraint"),\r
- /* limit distance */\r
- CONSTRAINT_TYPE_DISTLIMIT(14, "bDistLimitConstraint"),\r
- /* claiming this to be mine :) is in tuhopuu bjornmose */\r
- CONSTRAINT_TYPE_STRETCHTO(15, "bStretchToConstraint"),\r
- /* floor constraint */\r
- CONSTRAINT_TYPE_MINMAX(16, "bMinMaxConstraint"),\r
- /* rigidbody constraint */\r
- CONSTRAINT_TYPE_RIGIDBODYJOINT(17, "bRigidBodyConstraint"),\r
- /* clampto constraint */\r
- CONSTRAINT_TYPE_CLAMPTO(18, "bClampToConstraint"),\r
- /* transformation (loc/rot/size -> loc/rot/size) constraint */\r
- CONSTRAINT_TYPE_TRANSFORM(19, "bTransformConstraint"),\r
- /* shrinkwrap (loc/rot) constraint */\r
- CONSTRAINT_TYPE_SHRINKWRAP(20, "bShrinkwrapConstraint");\r
+ /* Invalid/legacy constraint */\r
\r
- /** The constraint's id (in blender known as 'type'). */\r
- private int constraintId;\r
- /** The name of constraint class used by blender. */\r
- private String className;\r
- /** The map containing class names and types of constraints. */\r
- private static Map<String, ConstraintType> typesMap = new HashMap<String, ConstraintType>(ConstraintType.values().length);\r
- /** The map containing class names and types of constraints. */\r
- private static Map<Integer, ConstraintType> idsMap = new HashMap<Integer, ConstraintType>(ConstraintType.values().length);\r
- static {\r
- idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_NULL.constraintId), CONSTRAINT_TYPE_NULL);\r
- idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_CHILDOF.constraintId), CONSTRAINT_TYPE_CHILDOF);\r
- idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_KINEMATIC.constraintId), CONSTRAINT_TYPE_KINEMATIC);\r
- idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_FOLLOWPATH.constraintId), CONSTRAINT_TYPE_FOLLOWPATH);\r
- idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_ROTLIMIT.constraintId), CONSTRAINT_TYPE_ROTLIMIT);\r
- idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_LOCLIMIT.constraintId), CONSTRAINT_TYPE_LOCLIMIT);\r
- idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_SIZELIMIT.constraintId), CONSTRAINT_TYPE_SIZELIMIT);\r
- idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_ROTLIKE.constraintId), CONSTRAINT_TYPE_ROTLIKE);\r
- idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_LOCLIKE.constraintId), CONSTRAINT_TYPE_LOCLIKE);\r
- idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_SIZELIKE.constraintId), CONSTRAINT_TYPE_SIZELIKE);\r
- idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_PYTHON.constraintId), CONSTRAINT_TYPE_PYTHON);\r
- idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_ACTION.constraintId), CONSTRAINT_TYPE_ACTION);\r
- idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_LOCKTRACK.constraintId), CONSTRAINT_TYPE_LOCKTRACK);\r
- idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_DISTLIMIT.constraintId), CONSTRAINT_TYPE_DISTLIMIT);\r
- idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_STRETCHTO.constraintId), CONSTRAINT_TYPE_STRETCHTO);\r
- idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_MINMAX.constraintId), CONSTRAINT_TYPE_MINMAX);\r
- idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_RIGIDBODYJOINT.constraintId), CONSTRAINT_TYPE_RIGIDBODYJOINT);\r
- idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_CLAMPTO.constraintId), CONSTRAINT_TYPE_CLAMPTO);\r
- idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_TRANSFORM.constraintId), CONSTRAINT_TYPE_TRANSFORM);\r
- idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_SHRINKWRAP.constraintId), CONSTRAINT_TYPE_SHRINKWRAP);\r
- }\r
- /**\r
- * Constructor. Stores constraint type and class name.\r
- * @param constraintId\r
- * the constraint's type\r
- * @param className\r
- * the constraint's type name\r
- */\r
- private ConstraintType(int constraintId, String className) {\r
- this.constraintId = constraintId;\r
- this.className = className;\r
- }\r
+ CONSTRAINT_TYPE_NULL(0, "bNullConstraint"),\r
+ /* Unimplemented non longer :) - during constraints recode, Aligorith */\r
+ CONSTRAINT_TYPE_CHILDOF(1, "bChildOfConstraint"),\r
+ CONSTRAINT_TYPE_KINEMATIC(3, "bKinematicConstraint"),\r
+ CONSTRAINT_TYPE_FOLLOWPATH(4, "bFollowPathConstraint"),\r
+ /* Unimplemented no longer :) - Aligorith */\r
+ CONSTRAINT_TYPE_ROTLIMIT(5, "bRotLimitConstraint"),\r
+ /* Unimplemented no longer :) - Aligorith */\r
+ CONSTRAINT_TYPE_LOCLIMIT(6, "bLocLimitConstraint"),\r
+ /* Unimplemented no longer :) - Aligorith */\r
+ CONSTRAINT_TYPE_SIZELIMIT(7, "bSizeLimitConstraint"),\r
+ CONSTRAINT_TYPE_ROTLIKE(8, "bRotateLikeConstraint"),\r
+ CONSTRAINT_TYPE_LOCLIKE(9, "bLocateLikeConstraint"),\r
+ CONSTRAINT_TYPE_SIZELIKE(10, "bSizeLikeConstraint"),\r
+ /* Unimplemented no longer :) - Aligorith. Scripts */\r
+ CONSTRAINT_TYPE_PYTHON(11, "bPythonConstraint"),\r
+ CONSTRAINT_TYPE_ACTION(12, "bActionConstraint"),\r
+ /* New Tracking constraint that locks an axis in place - theeth */\r
+ CONSTRAINT_TYPE_LOCKTRACK(13, "bLockTrackConstraint"),\r
+ /* limit distance */\r
+ CONSTRAINT_TYPE_DISTLIMIT(14, "bDistLimitConstraint"),\r
+ /* claiming this to be mine :) is in tuhopuu bjornmose */\r
+ CONSTRAINT_TYPE_STRETCHTO(15, "bStretchToConstraint"),\r
+ /* floor constraint */\r
+ CONSTRAINT_TYPE_MINMAX(16, "bMinMaxConstraint"),\r
+ /* rigidbody constraint */\r
+ CONSTRAINT_TYPE_RIGIDBODYJOINT(17, "bRigidBodyConstraint"),\r
+ /* clampto constraint */\r
+ CONSTRAINT_TYPE_CLAMPTO(18, "bClampToConstraint"),\r
+ /* transformation (loc/rot/size -> loc/rot/size) constraint */\r
+ CONSTRAINT_TYPE_TRANSFORM(19, "bTransformConstraint"),\r
+ /* shrinkwrap (loc/rot) constraint */\r
+ CONSTRAINT_TYPE_SHRINKWRAP(20, "bShrinkwrapConstraint");\r
+ /** The constraint's id (in blender known as 'type'). */\r
+ private int constraintId;\r
+ /** The name of constraint class used by blender. */\r
+ private String className;\r
+ /** The map containing class names and types of constraints. */\r
+ private static final Map<String, ConstraintType> typesMap = new HashMap<String, ConstraintType>(ConstraintType.values().length);\r
+ /** The map containing class names and types of constraints. */\r
+ private static final Map<Integer, ConstraintType> idsMap = new HashMap<Integer, ConstraintType>(ConstraintType.values().length);\r
\r
- /**\r
- * This method returns the type by given constraint id.\r
- * @param constraintId\r
- * the id of the constraint\r
- * @return the constraint type enum value\r
- */\r
- public static ConstraintType valueOf(int constraintId) {\r
- return idsMap.get(Integer.valueOf(constraintId));\r
- }\r
+ static {\r
+ idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_NULL.constraintId), CONSTRAINT_TYPE_NULL);\r
+ idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_CHILDOF.constraintId), CONSTRAINT_TYPE_CHILDOF);\r
+ idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_KINEMATIC.constraintId), CONSTRAINT_TYPE_KINEMATIC);\r
+ idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_FOLLOWPATH.constraintId), CONSTRAINT_TYPE_FOLLOWPATH);\r
+ idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_ROTLIMIT.constraintId), CONSTRAINT_TYPE_ROTLIMIT);\r
+ idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_LOCLIMIT.constraintId), CONSTRAINT_TYPE_LOCLIMIT);\r
+ idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_SIZELIMIT.constraintId), CONSTRAINT_TYPE_SIZELIMIT);\r
+ idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_ROTLIKE.constraintId), CONSTRAINT_TYPE_ROTLIKE);\r
+ idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_LOCLIKE.constraintId), CONSTRAINT_TYPE_LOCLIKE);\r
+ idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_SIZELIKE.constraintId), CONSTRAINT_TYPE_SIZELIKE);\r
+ idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_PYTHON.constraintId), CONSTRAINT_TYPE_PYTHON);\r
+ idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_ACTION.constraintId), CONSTRAINT_TYPE_ACTION);\r
+ idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_LOCKTRACK.constraintId), CONSTRAINT_TYPE_LOCKTRACK);\r
+ idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_DISTLIMIT.constraintId), CONSTRAINT_TYPE_DISTLIMIT);\r
+ idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_STRETCHTO.constraintId), CONSTRAINT_TYPE_STRETCHTO);\r
+ idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_MINMAX.constraintId), CONSTRAINT_TYPE_MINMAX);\r
+ idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_RIGIDBODYJOINT.constraintId), CONSTRAINT_TYPE_RIGIDBODYJOINT);\r
+ idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_CLAMPTO.constraintId), CONSTRAINT_TYPE_CLAMPTO);\r
+ idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_TRANSFORM.constraintId), CONSTRAINT_TYPE_TRANSFORM);\r
+ idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_SHRINKWRAP.constraintId), CONSTRAINT_TYPE_SHRINKWRAP);\r
+ }\r
\r
- /**\r
- * This method returns the constraint's id (type).\r
- * @return the constraint's id (type)\r
- */\r
- public int getConstraintId() {\r
- return constraintId;\r
- }\r
+ /**\r
+ * Constructor. Stores constraint type and class name.\r
+ * @param constraintId\r
+ * the constraint's type\r
+ * @param className\r
+ * the constraint's type name\r
+ */\r
+ private ConstraintType(int constraintId, String className) {\r
+ this.constraintId = constraintId;\r
+ this.className = className;\r
+ }\r
\r
- /**\r
- * This method returns the constraint's class name.\r
- * @return the constraint's class name\r
- */\r
- public String getClassName() {\r
- return className;\r
- }\r
+ /**\r
+ * This method returns the type by given constraint id.\r
+ * @param constraintId\r
+ * the id of the constraint\r
+ * @return the constraint type enum value\r
+ */\r
+ public static ConstraintType valueOf(int constraintId) {\r
+ return idsMap.get(Integer.valueOf(constraintId));\r
+ }\r
\r
- /**\r
- * This method returns constraint enum type by the given class name.\r
- * @param className\r
- * the blender's constraint class name\r
- * @return the constraint enum type of the specified class name\r
- */\r
- public static ConstraintType getByBlenderClassName(String className) {\r
- ConstraintType result = typesMap.get(className);\r
- if(result == null) {\r
- ConstraintType[] constraints = ConstraintType.values();\r
- for(ConstraintType constraint : constraints) {\r
- if(constraint.className.equals(className)) {\r
- return constraint;\r
- }\r
- }\r
- }\r
- return result;\r
- }\r
+ /**\r
+ * This method returns the constraint's id (type).\r
+ * @return the constraint's id (type)\r
+ */\r
+ public int getConstraintId() {\r
+ return constraintId;\r
+ }\r
\r
- /**\r
- * This method returns the type value of the last defined constraint. It can be used for allocating tables for\r
- * storing constraint procedures since not all type values from 0 to the last value are used.\r
- * @return the type value of the last defined constraint\r
- */\r
- public static int getLastDefinedTypeValue() {\r
- return CONSTRAINT_TYPE_SHRINKWRAP.getConstraintId();\r
- }\r
+ /**\r
+ * This method returns the constraint's class name.\r
+ * @return the constraint's class name\r
+ */\r
+ public String getClassName() {\r
+ return className;\r
+ }\r
+\r
+ /**\r
+ * This method returns constraint enum type by the given class name.\r
+ * @param className\r
+ * the blender's constraint class name\r
+ * @return the constraint enum type of the specified class name\r
+ */\r
+ public static ConstraintType getByBlenderClassName(String className) {\r
+ ConstraintType result = typesMap.get(className);\r
+ if (result == null) {\r
+ ConstraintType[] constraints = ConstraintType.values();\r
+ for (ConstraintType constraint : constraints) {\r
+ if (constraint.className.equals(className)) {\r
+ return constraint;\r
+ }\r
+ }\r
+ }\r
+ return result;\r
+ }\r
+\r
+ /**\r
+ * This method returns the type value of the last defined constraint. It can be used for allocating tables for\r
+ * storing constraint procedures since not all type values from 0 to the last value are used.\r
+ * @return the type value of the last defined constraint\r
+ */\r
+ public static int getLastDefinedTypeValue() {\r
+ return CONSTRAINT_TYPE_SHRINKWRAP.getConstraintId();\r
+ }\r
}\r
* @author Marcin Roguski\r
*/\r
public class Ipo {\r
- public static final int AC_LOC_X = 1;\r
- public static final int AC_LOC_Y = 2;\r
- public static final int AC_LOC_Z = 3;\r
- public static final int OB_ROT_X = 7;\r
- public static final int OB_ROT_Y = 8;\r
- public static final int OB_ROT_Z = 9;\r
- public static final int AC_SIZE_X = 13;\r
- public static final int AC_SIZE_Y = 14;\r
- public static final int AC_SIZE_Z = 15;\r
- public static final int AC_QUAT_W = 25;\r
- public static final int AC_QUAT_X = 26;\r
- public static final int AC_QUAT_Y = 27;\r
- public static final int AC_QUAT_Z = 28;\r
- \r
- /** A list of bezier curves for this interpolation object. */\r
- private BezierCurve[] bezierCurves;\r
- /** Each ipo contains one bone track. */\r
- private BoneTrack calculatedTrack;\r
\r
- /**\r
- * Constructor. Stores the bezier curves.\r
- * @param bezierCurves\r
- * a table of bezier curves\r
- */\r
- public Ipo(BezierCurve[] bezierCurves) {\r
- this.bezierCurves = bezierCurves;\r
- }\r
+ public static final int AC_LOC_X = 1;\r
+ public static final int AC_LOC_Y = 2;\r
+ public static final int AC_LOC_Z = 3;\r
+ public static final int OB_ROT_X = 7;\r
+ public static final int OB_ROT_Y = 8;\r
+ public static final int OB_ROT_Z = 9;\r
+ public static final int AC_SIZE_X = 13;\r
+ public static final int AC_SIZE_Y = 14;\r
+ public static final int AC_SIZE_Z = 15;\r
+ public static final int AC_QUAT_W = 25;\r
+ public static final int AC_QUAT_X = 26;\r
+ public static final int AC_QUAT_Y = 27;\r
+ public static final int AC_QUAT_Z = 28;\r
+ /** A list of bezier curves for this interpolation object. */\r
+ private BezierCurve[] bezierCurves;\r
+ /** Each ipo contains one bone track. */\r
+ private BoneTrack calculatedTrack;\r
\r
- /**\r
- * This method calculates the ipo value for the first curve.\r
- * @param frame\r
- * the frame for which the value is calculated\r
- * @return calculated ipo value\r
- */\r
- public float calculateValue(int frame) {\r
- return this.calculateValue(frame, 0);\r
- }\r
+ /**\r
+ * Constructor. Stores the bezier curves.\r
+ * @param bezierCurves\r
+ * a table of bezier curves\r
+ */\r
+ public Ipo(BezierCurve[] bezierCurves) {\r
+ this.bezierCurves = bezierCurves;\r
+ }\r
\r
- /**\r
- * This method calculates the ipo value for the curve of the specified index. Make sure you do not exceed the\r
- * curves amount. Alway chech the amount of curves before calling this method.\r
- * @param frame\r
- * the frame for which the value is calculated\r
- * @param curveIndex\r
- * the index of the curve\r
- * @return calculated ipo value\r
- */\r
- public float calculateValue(int frame, int curveIndex) {\r
- return bezierCurves[curveIndex].evaluate(frame, BezierCurve.Y_VALUE);\r
- }\r
+ /**\r
+ * This method calculates the ipo value for the first curve.\r
+ * @param frame\r
+ * the frame for which the value is calculated\r
+ * @return calculated ipo value\r
+ */\r
+ public float calculateValue(int frame) {\r
+ return this.calculateValue(frame, 0);\r
+ }\r
\r
- /**\r
- * This method returns the curves amount.\r
- * @return the curves amount\r
- */\r
- public int getCurvesAmount() {\r
- return bezierCurves.length;\r
- }\r
+ /**\r
+ * This method calculates the ipo value for the curve of the specified index. Make sure you do not exceed the\r
+ * curves amount. Alway chech the amount of curves before calling this method.\r
+ * @param frame\r
+ * the frame for which the value is calculated\r
+ * @param curveIndex\r
+ * the index of the curve\r
+ * @return calculated ipo value\r
+ */\r
+ public float calculateValue(int frame, int curveIndex) {\r
+ return bezierCurves[curveIndex].evaluate(frame, BezierCurve.Y_VALUE);\r
+ }\r
\r
- /**\r
- * This method returns the frame where last bezier triple center point of the specified bezier curve is located.\r
- * @return the frame number of the last defined bezier triple point for the specified ipo\r
- */\r
- public int getLastFrame() {\r
- int result = 1;\r
- for(int i = 0; i < bezierCurves.length; ++i) {\r
- int tempResult = bezierCurves[i].getLastFrame();\r
- if(tempResult > result) {\r
- result = tempResult;\r
- }\r
- }\r
- return result;\r
- }\r
- \r
- public void modifyTranslation(int frame, Vector3f translation) {\r
- if(calculatedTrack!=null) {\r
- calculatedTrack.getTranslations()[frame].set(translation);\r
- }\r
- }\r
- \r
- public void modifyRotation(int frame, Quaternion rotation) {\r
- if(calculatedTrack!=null) {\r
- calculatedTrack.getRotations()[frame].set(rotation);\r
- }\r
- }\r
- \r
- public void modifyScale(int frame, Vector3f scale) {\r
- if(calculatedTrack!=null) {\r
- calculatedTrack.getScales()[frame].set(scale);\r
- }\r
- }\r
- \r
- /**\r
- * This method calculates the value of the curves as a bone track between the specified frames.\r
- * @param boneIndex\r
- * the index of the bone for which the method calculates the tracks\r
- * @param startFrame\r
- * the firs frame of tracks (inclusive)\r
- * @param stopFrame\r
- * the last frame of the tracks (inclusive)\r
- * @param fps\r
- * frame rate (frames per second)\r
- * @return bone track for the specified bone\r
- */\r
- public BoneTrack calculateTrack(int boneIndex, int startFrame, int stopFrame, int fps) {\r
- //preparing data for track\r
- int framesAmount = stopFrame - startFrame;\r
- float start = (startFrame - 1.0f) / fps;\r
- float timeBetweenFrames = 1.0f / fps;\r
+ /**\r
+ * This method returns the curves amount.\r
+ * @return the curves amount\r
+ */\r
+ public int getCurvesAmount() {\r
+ return bezierCurves.length;\r
+ }\r
\r
- float[] times = new float[framesAmount + 1];\r
- Vector3f[] translations = new Vector3f[framesAmount + 1];\r
- float[] translation = new float[3];\r
- Quaternion[] rotations = new Quaternion[framesAmount + 1];\r
- float[] quaternionRotation = new float[4];\r
- float[] objectRotation = new float[3];\r
- boolean bObjectRotation = false;\r
- Vector3f[] scales = new Vector3f[framesAmount + 1];\r
- float[] scale = new float[3];\r
+ /**\r
+ * This method returns the frame where last bezier triple center point of the specified bezier curve is located.\r
+ * @return the frame number of the last defined bezier triple point for the specified ipo\r
+ */\r
+ public int getLastFrame() {\r
+ int result = 1;\r
+ for (int i = 0; i < bezierCurves.length; ++i) {\r
+ int tempResult = bezierCurves[i].getLastFrame();\r
+ if (tempResult > result) {\r
+ result = tempResult;\r
+ }\r
+ }\r
+ return result;\r
+ }\r
\r
- //calculating track data\r
- for(int frame = startFrame; frame <= stopFrame; ++frame) {\r
- int index = frame - startFrame;\r
- times[index] = start + (frame - 1) * timeBetweenFrames;\r
- for(int j = 0; j < bezierCurves.length; ++j) {\r
- double value = bezierCurves[j].evaluate(frame, BezierCurve.Y_VALUE);\r
- switch(bezierCurves[j].getType()) {\r
- case AC_LOC_X:\r
- case AC_LOC_Y:\r
- case AC_LOC_Z:\r
- translation[bezierCurves[j].getType() - 1] = (float)value;\r
- break;\r
- case OB_ROT_X:\r
- case OB_ROT_Y:\r
- case OB_ROT_Z:\r
- objectRotation[bezierCurves[j].getType() - 7] = (float)value;\r
- bObjectRotation = true;\r
- break;\r
- case AC_SIZE_X:\r
- case AC_SIZE_Y:\r
- case AC_SIZE_Z:\r
- scale[bezierCurves[j].getType() - 13] = (float)value;\r
- break;\r
- case AC_QUAT_W:\r
- quaternionRotation[3] = (float)value;\r
- break;\r
- case AC_QUAT_X:\r
- case AC_QUAT_Y:\r
- case AC_QUAT_Z:\r
- quaternionRotation[bezierCurves[j].getType() - 26] = (float)value;\r
- break;\r
- default:\r
- //TODO: error? info? warning?\r
- }\r
- }\r
- translations[index] = new Vector3f(translation[0], translation[1], translation[2]);\r
- rotations[index] = bObjectRotation ? new Quaternion().fromAngles(objectRotation) : \r
- new Quaternion(quaternionRotation[0], quaternionRotation[1], quaternionRotation[2], quaternionRotation[3]);\r
- scales[index] = new Vector3f(scale[0], scale[1], scale[2]);\r
- }\r
- calculatedTrack = new BoneTrack(boneIndex, times, translations, rotations, scales);\r
- return calculatedTrack;\r
- }\r
+ public void modifyTranslation(int frame, Vector3f translation) {\r
+ if (calculatedTrack != null) {\r
+ calculatedTrack.getTranslations()[frame].set(translation);\r
+ }\r
+ }\r
+\r
+ public void modifyRotation(int frame, Quaternion rotation) {\r
+ if (calculatedTrack != null) {\r
+ calculatedTrack.getRotations()[frame].set(rotation);\r
+ }\r
+ }\r
+\r
+ public void modifyScale(int frame, Vector3f scale) {\r
+ if (calculatedTrack != null) {\r
+ calculatedTrack.getScales()[frame].set(scale);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * This method calculates the value of the curves as a bone track between the specified frames.\r
+ * @param boneIndex\r
+ * the index of the bone for which the method calculates the tracks\r
+ * @param startFrame\r
+ * the firs frame of tracks (inclusive)\r
+ * @param stopFrame\r
+ * the last frame of the tracks (inclusive)\r
+ * @param fps\r
+ * frame rate (frames per second)\r
+ * @return bone track for the specified bone\r
+ */\r
+ public BoneTrack calculateTrack(int boneIndex, int startFrame, int stopFrame, int fps) {\r
+ //preparing data for track\r
+ int framesAmount = stopFrame - startFrame;\r
+ float start = (startFrame - 1.0f) / fps;\r
+ float timeBetweenFrames = 1.0f / fps;\r
+\r
+ float[] times = new float[framesAmount + 1];\r
+ Vector3f[] translations = new Vector3f[framesAmount + 1];\r
+ float[] translation = new float[3];\r
+ Quaternion[] rotations = new Quaternion[framesAmount + 1];\r
+ float[] quaternionRotation = new float[4];\r
+ float[] objectRotation = new float[3];\r
+ boolean bObjectRotation = false;\r
+ Vector3f[] scales = new Vector3f[framesAmount + 1];\r
+ float[] scale = new float[3];\r
+\r
+ //calculating track data\r
+ for (int frame = startFrame; frame <= stopFrame; ++frame) {\r
+ int index = frame - startFrame;\r
+ times[index] = start + (frame - 1) * timeBetweenFrames;\r
+ for (int j = 0; j < bezierCurves.length; ++j) {\r
+ double value = bezierCurves[j].evaluate(frame, BezierCurve.Y_VALUE);\r
+ switch (bezierCurves[j].getType()) {\r
+ case AC_LOC_X:\r
+ case AC_LOC_Y:\r
+ case AC_LOC_Z:\r
+ translation[bezierCurves[j].getType() - 1] = (float) value;\r
+ break;\r
+ case OB_ROT_X:\r
+ case OB_ROT_Y:\r
+ case OB_ROT_Z:\r
+ objectRotation[bezierCurves[j].getType() - 7] = (float) value;\r
+ bObjectRotation = true;\r
+ break;\r
+ case AC_SIZE_X:\r
+ case AC_SIZE_Y:\r
+ case AC_SIZE_Z:\r
+ scale[bezierCurves[j].getType() - 13] = (float) value;\r
+ break;\r
+ case AC_QUAT_W:\r
+ quaternionRotation[3] = (float) value;\r
+ break;\r
+ case AC_QUAT_X:\r
+ case AC_QUAT_Y:\r
+ case AC_QUAT_Z:\r
+ quaternionRotation[bezierCurves[j].getType() - 26] = (float) value;\r
+ break;\r
+ default:\r
+ //TODO: error? info? warning?\r
+ }\r
+ }\r
+ translations[index] = new Vector3f(translation[0], translation[1], translation[2]);\r
+ rotations[index] = bObjectRotation ? new Quaternion().fromAngles(objectRotation)\r
+ : new Quaternion(quaternionRotation[0], quaternionRotation[1], quaternionRotation[2], quaternionRotation[3]);\r
+ scales[index] = new Vector3f(scale[0], scale[1], scale[2]);\r
+ }\r
+ calculatedTrack = new BoneTrack(boneIndex, times, translations, rotations, scales);\r
+ return calculatedTrack;\r
+ }\r
}
\ No newline at end of file
* @author Marcin Roguski (Kaelthas)\r
*/\r
public class Modifier {\r
- public static final String ARRAY_MODIFIER_DATA = "ArrayModifierData";\r
- public static final String ARMATURE_MODIFIER_DATA = "ArmatureModifierData";\r
- public static final String PARTICLE_MODIFIER_DATA = "ParticleSystemModifierData";\r
\r
- /** Blender's type of modifier. */\r
- private String type;\r
- /** JME modifier representation object. */\r
- private Object jmeModifierRepresentation;\r
- /** Various additional data used by modifiers.*/\r
- private Object additionalData;\r
- /**\r
- * Constructor. Creates modifier object.\r
- * @param type\r
- * blender's type of modifier\r
- * @param modifier\r
- * JME modifier representation object\r
- */\r
- public Modifier(String type, Object modifier, Object additionalData) {\r
- this.type = type;\r
- this.jmeModifierRepresentation = modifier;\r
- this.additionalData = additionalData;\r
- }\r
+ public static final String ARRAY_MODIFIER_DATA = "ArrayModifierData";\r
+ public static final String ARMATURE_MODIFIER_DATA = "ArmatureModifierData";\r
+ public static final String PARTICLE_MODIFIER_DATA = "ParticleSystemModifierData";\r
+ /** Blender's type of modifier. */\r
+ private String type;\r
+ /** JME modifier representation object. */\r
+ private Object jmeModifierRepresentation;\r
+ /** Various additional data used by modifiers.*/\r
+ private Object additionalData;\r
\r
- /**\r
- * This method returns JME modifier representation object.\r
- * @return JME modifier representation object\r
- */\r
- public Object getJmeModifierRepresentation() {\r
- return jmeModifierRepresentation;\r
- }\r
+ /**\r
+ * Constructor. Creates modifier object.\r
+ * @param type\r
+ * blender's type of modifier\r
+ * @param modifier\r
+ * JME modifier representation object\r
+ */\r
+ public Modifier(String type, Object modifier, Object additionalData) {\r
+ this.type = type;\r
+ this.jmeModifierRepresentation = modifier;\r
+ this.additionalData = additionalData;\r
+ }\r
\r
- /**\r
- * This method returns blender's type of modifier.\r
- * @return blender's type of modifier\r
- */\r
- public String getType() {\r
- return type;\r
- }\r
- \r
- /**\r
- * This method returns additional data stored in the modifier.\r
- * @return the additional data stored in the modifier\r
- */\r
- public Object getAdditionalData() {\r
- return additionalData;\r
- }\r
+ /**\r
+ * This method returns JME modifier representation object.\r
+ * @return JME modifier representation object\r
+ */\r
+ public Object getJmeModifierRepresentation() {\r
+ return jmeModifierRepresentation;\r
+ }\r
+\r
+ /**\r
+ * This method returns blender's type of modifier.\r
+ * @return blender's type of modifier\r
+ */\r
+ public String getType() {\r
+ return type;\r
+ }\r
+\r
+ /**\r
+ * This method returns additional data stored in the modifier.\r
+ * @return the additional data stored in the modifier\r
+ */\r
+ public Object getAdditionalData() {\r
+ return additionalData;\r
+ }\r
}
\ No newline at end of file
* @author Marcin Roguski\r
*/\r
public abstract class AbstractBlenderHelper {\r
- /** The version of the blend file. */\r
- protected final int blenderVersion;\r
\r
- /**\r
- * This constructor parses the given blender version and stores the result. Some functionalities may differ in different blender\r
- * versions.\r
- * @param blenderVersion\r
- * the version read from the blend file\r
- */\r
- public AbstractBlenderHelper(String blenderVersion) {\r
- this.blenderVersion = Integer.parseInt(blenderVersion);\r
- }\r
- \r
- /**\r
- * This method clears the state of the helper so that it can be used for different calculations of another feature.\r
- */\r
- public void clearState() { }\r
+ /** The version of the blend file. */\r
+ protected final int blenderVersion;\r
\r
- /**\r
- * This method should be used to check if the text is blank. Avoid using text.trim().length()==0. This causes that more strings are\r
- * being created and stored in the memory. It can be unwise especially inside loops.\r
- * @param text\r
- * the text to be checked\r
- * @return <b>true</b> if the text is blank and <b>false</b> otherwise\r
- */\r
- protected boolean isBlank(String text) {\r
- if (text != null) {\r
- for (int i = 0; i < text.length(); ++i) {\r
- if (!Character.isWhitespace(text.charAt(i))) {\r
- return false;\r
- }\r
- }\r
- }\r
- return true;\r
- }\r
- \r
- /**\r
- * Generate a new FloatBuffer using the given array of float[4] objects. The FloatBuffer will be 4 * data.length\r
- * long and contain the vector data as data[0][0], data[0][1], data[0][2], data[0][3], data[1][0]... etc.\r
- * @param data\r
- * list of float[4] objects to place into a new FloatBuffer\r
- */\r
- protected FloatBuffer createFloatBuffer(List<float[]> data) {\r
- if(data == null) {\r
- return null;\r
- }\r
- FloatBuffer buff = BufferUtils.createFloatBuffer(4 * data.size());\r
- for(float[] v : data) {\r
- if(v != null) {\r
- buff.put(v[0]).put(v[1]).put(v[2]).put(v[3]);\r
- } else {\r
- buff.put(0).put(0).put(0).put(0);\r
- }\r
- }\r
- buff.flip();\r
- return buff;\r
- }\r
+ /**\r
+ * This constructor parses the given blender version and stores the result. Some functionalities may differ in different blender\r
+ * versions.\r
+ * @param blenderVersion\r
+ * the version read from the blend file\r
+ */\r
+ public AbstractBlenderHelper(String blenderVersion) {\r
+ this.blenderVersion = Integer.parseInt(blenderVersion);\r
+ }\r
+\r
+ /**\r
+ * This method clears the state of the helper so that it can be used for different calculations of another feature.\r
+ */\r
+ public void clearState() {\r
+ }\r
+\r
+ /**\r
+ * This method should be used to check if the text is blank. Avoid using text.trim().length()==0. This causes that more strings are\r
+ * being created and stored in the memory. It can be unwise especially inside loops.\r
+ * @param text\r
+ * the text to be checked\r
+ * @return <b>true</b> if the text is blank and <b>false</b> otherwise\r
+ */\r
+ protected boolean isBlank(String text) {\r
+ if (text != null) {\r
+ for (int i = 0; i < text.length(); ++i) {\r
+ if (!Character.isWhitespace(text.charAt(i))) {\r
+ return false;\r
+ }\r
+ }\r
+ }\r
+ return true;\r
+ }\r
+\r
+ /**\r
+ * Generate a new FloatBuffer using the given array of float[4] objects. The FloatBuffer will be 4 * data.length\r
+ * long and contain the vector data as data[0][0], data[0][1], data[0][2], data[0][3], data[1][0]... etc.\r
+ * @param data\r
+ * list of float[4] objects to place into a new FloatBuffer\r
+ */\r
+ protected FloatBuffer createFloatBuffer(List<float[]> data) {\r
+ if (data == null) {\r
+ return null;\r
+ }\r
+ FloatBuffer buff = BufferUtils.createFloatBuffer(4 * data.size());\r
+ for (float[] v : data) {\r
+ if (v != null) {\r
+ buff.put(v[0]).put(v[1]).put(v[2]).put(v[3]);\r
+ } else {\r
+ buff.put(0).put(0).put(0).put(0);\r
+ }\r
+ }\r
+ buff.flip();\r
+ return buff;\r
+ }\r
}\r
* the type of material element\r
*/\r
//TODO: ujednolicić wyrzucane wyjątki\r
-public interface IBlenderConverter<NodeType, CameraType, LightType, ObjectType, MeshType, MaterialType> {\r
- /**\r
- * This method reads converts the given structure into scene. The given structure needs to be filled with the\r
- * appropriate data.\r
- * @param structure\r
- * the structure we read the scene from\r
- * @return the scene feature\r
- */\r
- NodeType toScene(Structure structure);\r
+public interface BlenderConverter<NodeType, CameraType, LightType, ObjectType, MeshType, MaterialType> {\r
\r
- /**\r
- * This method reads converts the given structure into camera. The given structure needs to be filled with the\r
- * appropriate data.\r
- * @param structure\r
- * the structure we read the camera from\r
- * @return the camera feature\r
- */\r
- CameraType toCamera(Structure structure) throws BlenderFileException;\r
+ /**\r
+ * This method reads converts the given structure into scene. The given structure needs to be filled with the\r
+ * appropriate data.\r
+ * @param structure\r
+ * the structure we read the scene from\r
+ * @return the scene feature\r
+ */\r
+ NodeType toScene(Structure structure);\r
\r
- /**\r
- * This method reads converts the given structure into light. The given structure needs to be filled with the\r
- * appropriate data.\r
- * @param structure\r
- * the structure we read the light from\r
- * @return the light feature\r
- */\r
- LightType toLight(Structure structure) throws BlenderFileException;\r
+ /**\r
+ * This method reads converts the given structure into camera. The given structure needs to be filled with the\r
+ * appropriate data.\r
+ * @param structure\r
+ * the structure we read the camera from\r
+ * @return the camera feature\r
+ */\r
+ CameraType toCamera(Structure structure) throws BlenderFileException;\r
\r
- /**\r
- * This method reads converts the given structure into objct. The given structure needs to be filled with the\r
- * appropriate data.\r
- * @param structure\r
- * the structure we read the object from\r
- * @return the object feature\r
- */\r
- ObjectType toObject(Structure structure) throws BlenderFileException;\r
+ /**\r
+ * This method reads converts the given structure into light. The given structure needs to be filled with the\r
+ * appropriate data.\r
+ * @param structure\r
+ * the structure we read the light from\r
+ * @return the light feature\r
+ */\r
+ LightType toLight(Structure structure) throws BlenderFileException;\r
\r
- /**\r
- * This method reads converts the given structure into mesh. The given structure needs to be filled with the\r
- * appropriate data.\r
- * @param structure\r
- * the structure we read the mesh from\r
- * @return the mesh feature\r
- */\r
- MeshType toMesh(Structure structure) throws BlenderFileException;\r
+ /**\r
+ * This method reads converts the given structure into objct. The given structure needs to be filled with the\r
+ * appropriate data.\r
+ * @param structure\r
+ * the structure we read the object from\r
+ * @return the object feature\r
+ */\r
+ ObjectType toObject(Structure structure) throws BlenderFileException;\r
\r
- /**\r
- * This method reads converts the given structure into material. The given structure needs to be filled with the\r
- * appropriate data.\r
- * @param structure\r
- * the structure we read the material from\r
- * @return the material feature\r
- */\r
- MaterialType toMaterial(Structure structure) throws BlenderFileException;\r
+ /**\r
+ * This method reads converts the given structure into mesh. The given structure needs to be filled with the\r
+ * appropriate data.\r
+ * @param structure\r
+ * the structure we read the mesh from\r
+ * @return the mesh feature\r
+ */\r
+ MeshType toMesh(Structure structure) throws BlenderFileException;\r
+\r
+ /**\r
+ * This method reads converts the given structure into material. The given structure needs to be filled with the\r
+ * appropriate data.\r
+ * @param structure\r
+ * the structure we read the material from\r
+ * @return the material feature\r
+ */\r
+ MaterialType toMaterial(Structure structure) throws BlenderFileException;\r
}
\ No newline at end of file
* @author Marcin Roguski\r
*/\r
public class BlenderInputStream extends InputStream {\r
- private static final Logger LOGGER = Logger.getLogger(BlenderInputStream.class.getName());\r
\r
- /** The default size of the blender buffer. */\r
- private static final int DEFAULT_BUFFER_SIZE = 1048576; //1MB\r
- /** The application's asset manager. */\r
- private AssetManager assetManager;\r
- /**\r
- * Size of a pointer; all pointers in the file are stored in this format. '_' means 4 bytes and '-' means 8 bytes.\r
- */\r
- private int pointerSize;\r
- /**\r
- * Type of byte ordering used; 'v' means little endian and 'V' means big endian.\r
- */\r
- private char endianess;\r
- /** Version of Blender the file was created in; '248' means version 2.48. */\r
- private String versionNumber;\r
- /** The buffer we store the read data to. */\r
- protected byte[] cachedBuffer;\r
- /** The total size of the stored data. */\r
- protected int size;\r
- /** The current position of the read cursor. */\r
- protected int position;\r
+ private static final Logger LOGGER = Logger.getLogger(BlenderInputStream.class.getName());\r
+ /** The default size of the blender buffer. */\r
+ private static final int DEFAULT_BUFFER_SIZE = 1048576; //1MB\r
+ /** The application's asset manager. */\r
+ private AssetManager assetManager;\r
+ /**\r
+ * Size of a pointer; all pointers in the file are stored in this format. '_' means 4 bytes and '-' means 8 bytes.\r
+ */\r
+ private int pointerSize;\r
+ /**\r
+ * Type of byte ordering used; 'v' means little endian and 'V' means big endian.\r
+ */\r
+ private char endianess;\r
+ /** Version of Blender the file was created in; '248' means version 2.48. */\r
+ private String versionNumber;\r
+ /** The buffer we store the read data to. */\r
+ protected byte[] cachedBuffer;\r
+ /** The total size of the stored data. */\r
+ protected int size;\r
+ /** The current position of the read cursor. */\r
+ protected int position;\r
\r
- /**\r
- * Constructor. The input stream is stored and used to read data.\r
- * @param inputStream\r
- * the stream we read data from\r
- * @param assetManager\r
- * the application's asset manager\r
- * @param endianess\r
- * type of byte ordering used; 'v' means little endian and 'V' means big endian\r
- * @throws BlenderFileException\r
- * this exception is thrown if the file header has some invalid data\r
- */\r
- public BlenderInputStream(InputStream inputStream, AssetManager assetManager) throws BlenderFileException {\r
- this.assetManager = assetManager;\r
- //the size value will canche while reading the file; the available() method cannot be counted on\r
- try {\r
- size = inputStream.available();\r
- } catch (IOException e) {\r
- size = 0;\r
- }\r
- if(size <= 0) {\r
- size = BlenderInputStream.DEFAULT_BUFFER_SIZE;\r
- }\r
+ /**\r
+ * Constructor. The input stream is stored and used to read data.\r
+ * @param inputStream\r
+ * the stream we read data from\r
+ * @param assetManager\r
+ * the application's asset manager\r
+ * @param endianess\r
+ * type of byte ordering used; 'v' means little endian and 'V' means big endian\r
+ * @throws BlenderFileException\r
+ * this exception is thrown if the file header has some invalid data\r
+ */\r
+ public BlenderInputStream(InputStream inputStream, AssetManager assetManager) throws BlenderFileException {\r
+ this.assetManager = assetManager;\r
+ //the size value will canche while reading the file; the available() method cannot be counted on\r
+ try {\r
+ size = inputStream.available();\r
+ } catch (IOException e) {\r
+ size = 0;\r
+ }\r
+ if (size <= 0) {\r
+ size = BlenderInputStream.DEFAULT_BUFFER_SIZE;\r
+ }\r
\r
- //buffered input stream is used here for much faster file reading\r
- BufferedInputStream bufferedInputStream;\r
- if(inputStream instanceof BufferedInputStream) {\r
- bufferedInputStream = (BufferedInputStream)inputStream;\r
- } else {\r
- bufferedInputStream = new BufferedInputStream(inputStream);\r
- }\r
+ //buffered input stream is used here for much faster file reading\r
+ BufferedInputStream bufferedInputStream;\r
+ if (inputStream instanceof BufferedInputStream) {\r
+ bufferedInputStream = (BufferedInputStream) inputStream;\r
+ } else {\r
+ bufferedInputStream = new BufferedInputStream(inputStream);\r
+ }\r
\r
- try {\r
- this.readStreamToCache(bufferedInputStream);\r
- } catch (IOException e) {\r
- throw new BlenderFileException("Problems occured while caching the file!", e);\r
- }\r
+ try {\r
+ this.readStreamToCache(bufferedInputStream);\r
+ } catch (IOException e) {\r
+ throw new BlenderFileException("Problems occured while caching the file!", e);\r
+ }\r
\r
- try {\r
- this.readFileHeader();\r
- } catch(BlenderFileException e) {//the file might be packed, don't panic, try one more time ;)\r
- this.decompressFile();\r
- this.position = 0;\r
- this.readFileHeader();\r
- }\r
- }\r
+ try {\r
+ this.readFileHeader();\r
+ } catch (BlenderFileException e) {//the file might be packed, don't panic, try one more time ;)\r
+ this.decompressFile();\r
+ this.position = 0;\r
+ this.readFileHeader();\r
+ }\r
+ }\r
\r
- /**\r
- * This method reads the whole stream into a buffer.\r
- * @param inputStream\r
- * the stream to read the file data from\r
- * @throws IOException \r
- * an exception is thrown when data read from the stream is invalid or there are problems with i/o\r
- * operations\r
- */\r
- private void readStreamToCache(InputStream inputStream) throws IOException {\r
- int data = inputStream.read();\r
- cachedBuffer = new byte[size];\r
- size = 0;//this will count the actual size\r
- while(data != -1) {\r
- cachedBuffer[size++] = (byte)data;\r
- if(size >= cachedBuffer.length) {//widen the cached array\r
- byte[] newBuffer = new byte[cachedBuffer.length + (cachedBuffer.length >> 1)];\r
- System.arraycopy(cachedBuffer, 0, newBuffer, 0, cachedBuffer.length);\r
- cachedBuffer = newBuffer;\r
- }\r
- data = inputStream.read();\r
- }\r
- }\r
+ /**\r
+ * This method reads the whole stream into a buffer.\r
+ * @param inputStream\r
+ * the stream to read the file data from\r
+ * @throws IOException \r
+ * an exception is thrown when data read from the stream is invalid or there are problems with i/o\r
+ * operations\r
+ */\r
+ private void readStreamToCache(InputStream inputStream) throws IOException {\r
+ int data = inputStream.read();\r
+ cachedBuffer = new byte[size];\r
+ size = 0;//this will count the actual size\r
+ while (data != -1) {\r
+ cachedBuffer[size++] = (byte) data;\r
+ if (size >= cachedBuffer.length) {//widen the cached array\r
+ byte[] newBuffer = new byte[cachedBuffer.length + (cachedBuffer.length >> 1)];\r
+ System.arraycopy(cachedBuffer, 0, newBuffer, 0, cachedBuffer.length);\r
+ cachedBuffer = newBuffer;\r
+ }\r
+ data = inputStream.read();\r
+ }\r
+ }\r
\r
- /**\r
- * This method is used when the blender file is gzipped. It decompresses the data and stores it back into the\r
- * cachedBuffer field.\r
- */\r
- private void decompressFile() {\r
- GZIPInputStream gis = null;\r
- try {\r
- gis = new GZIPInputStream(new ByteArrayInputStream(cachedBuffer));\r
- this.readStreamToCache(gis);\r
- } catch (IOException e) {\r
- throw new IllegalStateException("IO errors occured where they should NOT! " +\r
- "The data is already buffered at this point!", e);\r
- } finally {\r
- try {\r
- if(gis!=null) {\r
- gis.close();\r
- }\r
- } catch(IOException e) {\r
- LOGGER.warning(e.getMessage());\r
- }\r
- }\r
- }\r
+ /**\r
+ * This method is used when the blender file is gzipped. It decompresses the data and stores it back into the\r
+ * cachedBuffer field.\r
+ */\r
+ private void decompressFile() {\r
+ GZIPInputStream gis = null;\r
+ try {\r
+ gis = new GZIPInputStream(new ByteArrayInputStream(cachedBuffer));\r
+ this.readStreamToCache(gis);\r
+ } catch (IOException e) {\r
+ throw new IllegalStateException("IO errors occured where they should NOT! "\r
+ + "The data is already buffered at this point!", e);\r
+ } finally {\r
+ try {\r
+ if (gis != null) {\r
+ gis.close();\r
+ }\r
+ } catch (IOException e) {\r
+ LOGGER.warning(e.getMessage());\r
+ }\r
+ }\r
+ }\r
\r
- /**\r
- * This method loads the header from the given stream during instance creation.\r
- * @param inputStream\r
- * the stream we read the header from\r
- * @throws BlenderFileException\r
- * this exception is thrown if the file header has some invalid data\r
- */\r
- private void readFileHeader() throws BlenderFileException {\r
- byte[] identifier = new byte[7];\r
- int bytesRead = this.readBytes(identifier);\r
- if(bytesRead != 7) {\r
- throw new BlenderFileException("Error reading header identifier. Only " + bytesRead + " bytes read and there should be 7!");\r
- }\r
- String strIdentifier = new String(identifier);\r
- if(!"BLENDER".equals(strIdentifier)) {\r
- throw new BlenderFileException("Wrong file identifier: " + strIdentifier + "! Should be 'BLENDER'!");\r
- }\r
- char pointerSizeSign = (char)this.readByte();\r
- if(pointerSizeSign == '-') {\r
- pointerSize = 8;\r
- } else if(pointerSizeSign == '_') {\r
- pointerSize = 4;\r
- } else {\r
- throw new BlenderFileException("Invalid pointer size character! Should be '_' or '-' and there is: " + pointerSizeSign);\r
- }\r
- endianess = (char)this.readByte();\r
- if(endianess != 'v' && endianess != 'V') {\r
- throw new BlenderFileException("Unknown endianess value! 'v' or 'V' expected and found: " + endianess);\r
- }\r
- byte[] versionNumber = new byte[3];\r
- bytesRead = this.readBytes(versionNumber);\r
- if(bytesRead != 3) {\r
- throw new BlenderFileException("Error reading version numberr. Only " + bytesRead + " bytes read and there should be 3!");\r
- }\r
- this.versionNumber = new String(versionNumber);\r
- }\r
+ /**\r
+ * This method loads the header from the given stream during instance creation.\r
+ * @param inputStream\r
+ * the stream we read the header from\r
+ * @throws BlenderFileException\r
+ * this exception is thrown if the file header has some invalid data\r
+ */\r
+ private void readFileHeader() throws BlenderFileException {\r
+ byte[] identifier = new byte[7];\r
+ int bytesRead = this.readBytes(identifier);\r
+ if (bytesRead != 7) {\r
+ throw new BlenderFileException("Error reading header identifier. Only " + bytesRead + " bytes read and there should be 7!");\r
+ }\r
+ String strIdentifier = new String(identifier);\r
+ if (!"BLENDER".equals(strIdentifier)) {\r
+ throw new BlenderFileException("Wrong file identifier: " + strIdentifier + "! Should be 'BLENDER'!");\r
+ }\r
+ char pointerSizeSign = (char) this.readByte();\r
+ if (pointerSizeSign == '-') {\r
+ pointerSize = 8;\r
+ } else if (pointerSizeSign == '_') {\r
+ pointerSize = 4;\r
+ } else {\r
+ throw new BlenderFileException("Invalid pointer size character! Should be '_' or '-' and there is: " + pointerSizeSign);\r
+ }\r
+ endianess = (char) this.readByte();\r
+ if (endianess != 'v' && endianess != 'V') {\r
+ throw new BlenderFileException("Unknown endianess value! 'v' or 'V' expected and found: " + endianess);\r
+ }\r
+ byte[] versionNumber = new byte[3];\r
+ bytesRead = this.readBytes(versionNumber);\r
+ if (bytesRead != 3) {\r
+ throw new BlenderFileException("Error reading version numberr. Only " + bytesRead + " bytes read and there should be 3!");\r
+ }\r
+ this.versionNumber = new String(versionNumber);\r
+ }\r
\r
- @Override\r
- public int read() throws IOException {\r
- return this.readByte();\r
- }\r
- \r
- /**\r
- * This method reads 1 byte from the stream.\r
- * It works just in the way the read method does.\r
- * It just not throw an exception because at this moment the whole file\r
- * is loaded into buffer, so no need for IOException to be thrown.\r
- * @return a byte from the stream (1 bytes read)\r
- */\r
- public int readByte() {\r
- return cachedBuffer[position++] & 0xFF;\r
- }\r
- \r
- /**\r
- * This method reads a bytes number big enough to fill the table. \r
- * It does not throw exceptions so it is for internal use only.\r
- * @param bytes\r
- * an array to be filled with data\r
- * @return number of read bytes (a length of array actually)\r
- */\r
- private int readBytes(byte[] bytes) {\r
- for(int i=0;i<bytes.length;++i) {\r
- bytes[i] = (byte) this.readByte();\r
- }\r
- return bytes.length;\r
- }\r
- \r
- /**\r
- * This method reads 2-byte number from the stream.\r
- * @return a number from the stream (2 bytes read)\r
- */\r
- public int readShort() {\r
- int part1 = this.readByte();\r
- int part2 = this.readByte();\r
- if(endianess == 'v') {\r
- return (part2 << 8) + part1;\r
- } else {\r
- return (part1 << 8) + part2;\r
- }\r
- }\r
+ @Override\r
+ public int read() throws IOException {\r
+ return this.readByte();\r
+ }\r
\r
- /**\r
- * This method reads 4-byte number from the stream.\r
- * @return a number from the stream (4 bytes read)\r
- */\r
- public int readInt() {\r
- int part1 = this.readByte();\r
- int part2 = this.readByte();\r
- int part3 = this.readByte();\r
- int part4 = this.readByte();\r
- if(endianess == 'v') {\r
- return (part4 << 24) + (part3 << 16) + (part2 << 8) + part1;\r
- } else {\r
- return (part1 << 24) + (part2 << 16) + (part3 << 8) + part4;\r
- }\r
- }\r
+ /**\r
+ * This method reads 1 byte from the stream.\r
+ * It works just in the way the read method does.\r
+ * It just not throw an exception because at this moment the whole file\r
+ * is loaded into buffer, so no need for IOException to be thrown.\r
+ * @return a byte from the stream (1 bytes read)\r
+ */\r
+ public int readByte() {\r
+ return cachedBuffer[position++] & 0xFF;\r
+ }\r
\r
- /**\r
- * This method reads 4-byte floating point number (float) from the stream.\r
- * @return a number from the stream (4 bytes read)\r
- */\r
- public float readFloat() {\r
- int intValue = this.readInt();\r
- return Float.intBitsToFloat(intValue);\r
- }\r
+ /**\r
+ * This method reads a bytes number big enough to fill the table. \r
+ * It does not throw exceptions so it is for internal use only.\r
+ * @param bytes\r
+ * an array to be filled with data\r
+ * @return number of read bytes (a length of array actually)\r
+ */\r
+ private int readBytes(byte[] bytes) {\r
+ for (int i = 0; i < bytes.length; ++i) {\r
+ bytes[i] = (byte) this.readByte();\r
+ }\r
+ return bytes.length;\r
+ }\r
\r
- /**\r
- * This method reads 8-byte number from the stream.\r
- * @return a number from the stream (8 bytes read)\r
- */\r
- public long readLong() {\r
- long part1 = this.readInt();\r
- long part2 = this.readInt();\r
- long result = -1;\r
- if(endianess == 'v') {\r
- result = part2 << 32 | part1;\r
- } else {\r
- result = part1 << 32 | part2;\r
- }\r
- return result;\r
- }\r
+ /**\r
+ * This method reads 2-byte number from the stream.\r
+ * @return a number from the stream (2 bytes read)\r
+ */\r
+ public int readShort() {\r
+ int part1 = this.readByte();\r
+ int part2 = this.readByte();\r
+ if (endianess == 'v') {\r
+ return (part2 << 8) + part1;\r
+ } else {\r
+ return (part1 << 8) + part2;\r
+ }\r
+ }\r
\r
- /**\r
- * This method reads 8-byte floating point number (double) from the stream.\r
- * @return a number from the stream (8 bytes read)\r
- */\r
- public double readDouble() {\r
- long longValue = this.readLong();\r
- return Double.longBitsToDouble(longValue);\r
- }\r
+ /**\r
+ * This method reads 4-byte number from the stream.\r
+ * @return a number from the stream (4 bytes read)\r
+ */\r
+ public int readInt() {\r
+ int part1 = this.readByte();\r
+ int part2 = this.readByte();\r
+ int part3 = this.readByte();\r
+ int part4 = this.readByte();\r
+ if (endianess == 'v') {\r
+ return (part4 << 24) + (part3 << 16) + (part2 << 8) + part1;\r
+ } else {\r
+ return (part1 << 24) + (part2 << 16) + (part3 << 8) + part4;\r
+ }\r
+ }\r
\r
- /**\r
- * This method reads the pointer value. Depending on the pointer size defined in the header, the stream reads either\r
- * 4 or 8 bytes of data.\r
- * @return the pointer value\r
- */\r
- public long readPointer() {\r
- if(pointerSize == 4) {\r
- return this.readInt();\r
- }\r
- return this.readLong();\r
- }\r
+ /**\r
+ * This method reads 4-byte floating point number (float) from the stream.\r
+ * @return a number from the stream (4 bytes read)\r
+ */\r
+ public float readFloat() {\r
+ int intValue = this.readInt();\r
+ return Float.intBitsToFloat(intValue);\r
+ }\r
\r
- /**\r
- * This method reads the string. It assumes the string is terminated with zero in the stream.\r
- * @return the string read from the stream\r
- */\r
- public String readString() {\r
- StringBuilder stringBuilder = new StringBuilder();\r
- int data = this.readByte();\r
- while(data != 0) {\r
- stringBuilder.append((char)data);\r
- data = this.readByte();\r
- }\r
- return stringBuilder.toString();\r
- }\r
+ /**\r
+ * This method reads 8-byte number from the stream.\r
+ * @return a number from the stream (8 bytes read)\r
+ */\r
+ public long readLong() {\r
+ long part1 = this.readInt();\r
+ long part2 = this.readInt();\r
+ long result = -1;\r
+ if (endianess == 'v') {\r
+ result = part2 << 32 | part1;\r
+ } else {\r
+ result = part1 << 32 | part2;\r
+ }\r
+ return result;\r
+ }\r
\r
- /**\r
- * This method sets the current position of the read cursor.\r
- * @param position\r
- * the position of the read cursor\r
- */\r
- public void setPosition(int position) {\r
- this.position = position;\r
- }\r
+ /**\r
+ * This method reads 8-byte floating point number (double) from the stream.\r
+ * @return a number from the stream (8 bytes read)\r
+ */\r
+ public double readDouble() {\r
+ long longValue = this.readLong();\r
+ return Double.longBitsToDouble(longValue);\r
+ }\r
\r
- /**\r
- * This method returns the position of the read cursor.\r
- * @return the position of the read cursor\r
- */\r
- public int getPosition() {\r
- return position;\r
- }\r
+ /**\r
+ * This method reads the pointer value. Depending on the pointer size defined in the header, the stream reads either\r
+ * 4 or 8 bytes of data.\r
+ * @return the pointer value\r
+ */\r
+ public long readPointer() {\r
+ if (pointerSize == 4) {\r
+ return this.readInt();\r
+ }\r
+ return this.readLong();\r
+ }\r
\r
- /**\r
- * This method returns the blender version number where the file was created.\r
- * @return blender version number\r
- */\r
- public String getVersionNumber() {\r
- return versionNumber;\r
- }\r
+ /**\r
+ * This method reads the string. It assumes the string is terminated with zero in the stream.\r
+ * @return the string read from the stream\r
+ */\r
+ public String readString() {\r
+ StringBuilder stringBuilder = new StringBuilder();\r
+ int data = this.readByte();\r
+ while (data != 0) {\r
+ stringBuilder.append((char) data);\r
+ data = this.readByte();\r
+ }\r
+ return stringBuilder.toString();\r
+ }\r
\r
- /**\r
- * This method returns the size of the pointer.\r
- * @return the size of the pointer\r
- */\r
- public int getPointerSize() {\r
- return pointerSize;\r
- }\r
+ /**\r
+ * This method sets the current position of the read cursor.\r
+ * @param position\r
+ * the position of the read cursor\r
+ */\r
+ public void setPosition(int position) {\r
+ this.position = position;\r
+ }\r
\r
- /**\r
- * This method returns the application's asset manager.\r
- * @return the application's asset manager\r
- */\r
- public AssetManager getAssetManager() {\r
- return assetManager;\r
- }\r
+ /**\r
+ * This method returns the position of the read cursor.\r
+ * @return the position of the read cursor\r
+ */\r
+ public int getPosition() {\r
+ return position;\r
+ }\r
\r
- /**\r
- * This method aligns cursor position forward to a given amount of bytes.\r
- * @param bytesAmount\r
- * the byte amount to which we aligh the cursor\r
- */\r
- public void alignPosition(int bytesAmount) {\r
- if(bytesAmount <= 0) {\r
- throw new IllegalArgumentException("Alignment byte number shoulf be positivbe!");\r
- }\r
- long move = position % bytesAmount;\r
- if(move > 0) {\r
- position += bytesAmount - move;\r
- }\r
- }\r
+ /**\r
+ * This method returns the blender version number where the file was created.\r
+ * @return blender version number\r
+ */\r
+ public String getVersionNumber() {\r
+ return versionNumber;\r
+ }\r
\r
- @Override\r
- public void close() throws IOException {\r
+ /**\r
+ * This method returns the size of the pointer.\r
+ * @return the size of the pointer\r
+ */\r
+ public int getPointerSize() {\r
+ return pointerSize;\r
+ }\r
+\r
+ /**\r
+ * This method returns the application's asset manager.\r
+ * @return the application's asset manager\r
+ */\r
+ public AssetManager getAssetManager() {\r
+ return assetManager;\r
+ }\r
+\r
+ /**\r
+ * This method aligns cursor position forward to a given amount of bytes.\r
+ * @param bytesAmount\r
+ * the byte amount to which we aligh the cursor\r
+ */\r
+ public void alignPosition(int bytesAmount) {\r
+ if (bytesAmount <= 0) {\r
+ throw new IllegalArgumentException("Alignment byte number shoulf be positivbe!");\r
+ }\r
+ long move = position % bytesAmount;\r
+ if (move > 0) {\r
+ position += bytesAmount - move;\r
+ }\r
+ }\r
+\r
+ @Override\r
+ public void close() throws IOException {\r
// cachedBuffer = null;\r
// size = position = 0;\r
- }\r
+ }\r
}\r
* @author Marcin Roguski\r
*/\r
public class DataRepository {\r
- /** The blender key. */\r
- private BlenderKey blenderKey;\r
- /** The header of the file block. */\r
- private DnaBlockData dnaBlockData;\r
- /** The input stream of the blend file. */\r
- private BlenderInputStream inputStream;\r
- /** The asset manager. */\r
- private AssetManager assetManager;\r
- /** A map containing the file block headers. The key is the old pointer address. */\r
- private Map<Long, FileBlockHeader> fileBlockHeadersByOma = new HashMap<Long, FileBlockHeader>();\r
- /** A map containing the file block headers. The key is the block code. */\r
- private Map<Integer, List<FileBlockHeader>> fileBlockHeadersByCode = new HashMap<Integer, List<FileBlockHeader>>();\r
- /**\r
- * This map stores the loaded features by their old memory address. The first object in the value table is the\r
- * loaded structure and the second - the structure already converted into proper data.\r
- */\r
- private Map<Long, Object[]> loadedFeatures = new HashMap<Long, Object[]>();\r
- /**\r
- * This map stores the loaded features by their name. Only features with ID structure can be stored here.\r
- * The first object in the value table is the\r
- * loaded structure and the second - the structure already converted into proper data.\r
- */\r
- private Map<String, Object[]> loadedFeaturesByName = new HashMap<String, Object[]>();\r
- /** A stack that hold the parent structure of currently loaded feature. */\r
- private Stack<Structure> parentStack = new Stack<Structure>();\r
- /** A map storing loaded ipos. The key is the ipo's owner old memory address and the value is the ipo. */\r
- private Map<Long, Ipo> loadedIpos = new HashMap<Long, Ipo>();\r
- /** A list of modifiers for the specified object. */\r
- protected Map<Long, List<Modifier>> modifiers = new HashMap<Long, List<Modifier>>();\r
- /** A map og helpers that perform loading. */\r
- private Map<String, AbstractBlenderHelper> helpers = new HashMap<String, AbstractBlenderHelper>();\r
- \r
- /**\r
- * This method sets the blender key.\r
- * @param blenderKey\r
- * the blender key\r
- */\r
- public void setBlenderKey(BlenderKey blenderKey) {\r
- this.blenderKey = blenderKey;\r
- }\r
- \r
- /**\r
- * This method returns the blender key.\r
- * @return the blender key\r
- */\r
- public BlenderKey getBlenderKey() {\r
- return blenderKey;\r
- }\r
- \r
- /**\r
- * This method sets the dna block data.\r
- * @param dnaBlockData\r
- * the dna block data\r
- */\r
- public void setBlockData(DnaBlockData dnaBlockData) {\r
- this.dnaBlockData = dnaBlockData;\r
- }\r
-\r
- /**\r
- * This method returns the dna block data.\r
- * @return the dna block data\r
- */\r
- public DnaBlockData getDnaBlockData() {\r
- return dnaBlockData;\r
- }\r
-\r
- /**\r
- * This method returns the asset manager.\r
- * @return the asset manager\r
- */\r
- public AssetManager getAssetManager() {\r
- return assetManager;\r
- }\r
-\r
- /**\r
- * This method sets the asset manager.\r
- * @param assetManager\r
- * the asset manager\r
- */\r
- public void setAssetManager(AssetManager assetManager) {\r
- this.assetManager = assetManager;\r
- }\r
- \r
- /**\r
- * This method returns the input stream of the blend file.\r
- * @return the input stream of the blend file\r
- */\r
- public BlenderInputStream getInputStream() {\r
- return inputStream;\r
- }\r
-\r
- /**\r
- * This method sets the input stream of the blend file.\r
- * @param inputStream\r
- * the input stream of the blend file\r
- */\r
- public void setInputStream(BlenderInputStream inputStream) {\r
- this.inputStream = inputStream;\r
- }\r
-\r
- /**\r
- * This method adds a file block header to the map. Its old memory address is the key.\r
- * @param oldMemoryAddress\r
- * the address of the block header\r
- * @param fileBlockHeader\r
- * the block header to store\r
- */\r
- public void addFileBlockHeader(Long oldMemoryAddress, FileBlockHeader fileBlockHeader) {\r
- fileBlockHeadersByOma.put(oldMemoryAddress, fileBlockHeader);\r
- List<FileBlockHeader> headers = fileBlockHeadersByCode.get(Integer.valueOf(fileBlockHeader.getCode()));\r
- if(headers == null) {\r
- headers = new ArrayList<FileBlockHeader>();\r
- fileBlockHeadersByCode.put(Integer.valueOf(fileBlockHeader.getCode()), headers);\r
- }\r
- headers.add(fileBlockHeader);\r
- }\r
-\r
- /**\r
- * This method returns the block header of a given memory address. If the header is not present then null is\r
- * returned.\r
- * @param oldMemoryAddress\r
- * the address of the block header\r
- * @return loaded header or null if it was not yet loaded\r
- */\r
- public FileBlockHeader getFileBlock(Long oldMemoryAddress) {\r
- return fileBlockHeadersByOma.get(oldMemoryAddress);\r
- }\r
-\r
- /**\r
- * This method returns a list of file blocks' headers of a specified code.\r
- * @param code\r
- * the code of file blocks\r
- * @return a list of file blocks' headers of a specified code\r
- */\r
- public List<FileBlockHeader> getFileBlocks(Integer code) {\r
- return fileBlockHeadersByCode.get(code);\r
- }\r
-\r
- /**\r
- * This method clears the saved block headers stored in the features map.\r
- */\r
- public void clearFileBlocks() {\r
- fileBlockHeadersByOma.clear();\r
- fileBlockHeadersByCode.clear();\r
- }\r
-\r
- /**\r
- * This method adds a helper instance to the helpers' map.\r
- * @param <T>\r
- * the type of the helper\r
- * @param clazz\r
- * helper's class definition\r
- * @param helper\r
- * the helper instance\r
- */\r
- public <T> void putHelper(Class<T> clazz, AbstractBlenderHelper helper) {\r
- helpers.put(clazz.getSimpleName(), helper);\r
- }\r
-\r
- @SuppressWarnings("unchecked")\r
- public <T> T getHelper(Class<?> clazz) {\r
- return (T)helpers.get(clazz.getSimpleName());\r
- }\r
- \r
-\r
- /**\r
- * This method adds a loaded feature to the map. The key is its unique old memory address.\r
- * @param oldMemoryAddress\r
- * the address of the feature\r
- * @param featureName the name of the feature\r
- * @param structure\r
- * the filled structure of the feature\r
- * @param feature\r
- * the feature we want to store\r
- */\r
- public void addLoadedFeatures(Long oldMemoryAddress, String featureName, Structure structure, Object feature) {\r
- if(oldMemoryAddress == null || structure == null || feature == null) {\r
- throw new IllegalArgumentException("One of the given arguments is null!");\r
- }\r
- Object[] storedData = new Object[] {structure, feature};\r
- loadedFeatures.put(oldMemoryAddress, storedData);\r
- if(featureName!=null) {\r
- loadedFeaturesByName.put(featureName, storedData);\r
- }\r
- }\r
-\r
- /**\r
- * This method returns the feature of a given memory address. If the feature is not yet loaded then null is\r
- * returned.\r
- * @param oldMemoryAddress\r
- * the address of the feature\r
- * @param loadedFeatureDataType\r
- * the type of data we want to retreive it can be either filled structure or already converted feature\r
- * @return loaded feature or null if it was not yet loaded\r
- */\r
- public Object getLoadedFeature(Long oldMemoryAddress, LoadedFeatureDataType loadedFeatureDataType) {\r
- Object[] result = loadedFeatures.get(oldMemoryAddress);\r
- if(result != null) {\r
- return result[loadedFeatureDataType.getIndex()];\r
- }\r
- return null;\r
- }\r
- \r
- /**\r
- * This method returns the feature of a given name. If the feature is not yet loaded then null is\r
- * returned.\r
- * @param featureName\r
- * the name of the feature\r
- * @param loadedFeatureDataType\r
- * the type of data we want to retreive it can be either filled structure or already converted feature\r
- * @return loaded feature or null if it was not yet loaded\r
- */\r
- public Object getLoadedFeature(String featureName, LoadedFeatureDataType loadedFeatureDataType) {\r
- Object[] result = loadedFeaturesByName.get(featureName);\r
- if(result != null) {\r
- return result[loadedFeatureDataType.getIndex()];\r
- }\r
- return null;\r
- }\r
-\r
- /**\r
- * This method clears the saved features stored in the features map.\r
- */\r
- public void clearLoadedFeatures() {\r
- loadedFeatures.clear();\r
- }\r
-\r
- /**\r
- * This method adds the structure to the parent stack.\r
- * @param parent\r
- * the structure to be added to the stack\r
- */\r
- public void pushParent(Structure parent) {\r
- parentStack.push(parent);\r
- }\r
-\r
- /**\r
- * This method removes the structure from the top of the parent's stack.\r
- * @return the structure that was removed from the stack\r
- */\r
- public Structure popParent() {\r
- try {\r
- return parentStack.pop();\r
- } catch(EmptyStackException e) {\r
- return null;\r
- }\r
- }\r
-\r
- /**\r
- * This method retreives the structure at the top of the parent's stack but does not remove it.\r
- * @return the structure from the top of the stack\r
- */\r
- public Structure peekParent() {\r
- try {\r
- return parentStack.peek();\r
- } catch(EmptyStackException e) {\r
- return null;\r
- }\r
- }\r
-\r
- public void addIpo(Long ownerOMA, Ipo ipo) {\r
- loadedIpos.put(ownerOMA, ipo);\r
- }\r
- \r
- public Ipo removeIpo(Long ownerOma) {\r
- return loadedIpos.remove(ownerOma);\r
- }\r
- \r
- public Ipo getIpo(Long ownerOMA) {\r
- return loadedIpos.get(ownerOMA);\r
- }\r
- \r
- /**\r
- * This method adds a new modifier to the list.\r
- * @param ownerOMA\r
- * the owner's old memory address\r
- * @param modifierType\r
- * the type of the modifier\r
- * @param loadedModifier\r
- * the loaded modifier object\r
- */\r
- public void addModifier(Long ownerOMA, String modifierType, Object loadedModifier, Object additionalModifierData) {\r
- List<Modifier> objectModifiers = this.modifiers.get(ownerOMA);\r
- if(objectModifiers == null) {\r
- objectModifiers = new ArrayList<Modifier>();\r
- this.modifiers.put(ownerOMA, objectModifiers);\r
- }\r
- objectModifiers.add(new Modifier(modifierType, loadedModifier, additionalModifierData));\r
- }\r
- \r
- /**\r
- * This method returns modifiers for the object specified by its old memory address and the modifier type. If no\r
- * modifiers are found - empty list is returned. If the type is null - all modifiers for the object are returned.\r
- * @param objectOMA\r
- * object's old memory address\r
- * @param type\r
- * the type of the modifier\r
- * @return the list of object's modifiers\r
- */\r
- public List<Modifier> getModifiers(Long objectOMA, String type) {\r
- List<Modifier> result = new ArrayList<Modifier>();\r
- List<Modifier> readModifiers = modifiers.get(objectOMA);\r
- if(readModifiers != null && readModifiers.size() > 0) {\r
- for(Modifier modifier : readModifiers) {\r
- if(type==null || type.isEmpty() || modifier.getType().equals(type)) {\r
- result.add(modifier);\r
- }\r
- }\r
- }\r
- return result;\r
- }\r
- \r
- /**\r
- * This metod returns the default material.\r
- * @return the default material\r
- */\r
- public synchronized Material getDefaultMaterial() {\r
- if(blenderKey.getDefaultMaterial() == null) {\r
- Material defaultMaterial = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");\r
- defaultMaterial.setColor("Color", ColorRGBA.DarkGray);\r
- blenderKey.setDefaultMaterial(defaultMaterial);\r
- }\r
- return blenderKey.getDefaultMaterial();\r
- }\r
-\r
- /**\r
- * This enum defines what loaded data type user wants to retreive. It can be either filled structure or already\r
- * converted data.\r
- * @author Marcin Roguski\r
- */\r
- public static enum LoadedFeatureDataType {\r
- LOADED_STRUCTURE(0), LOADED_FEATURE(1);\r
-\r
- private int index;\r
-\r
- private LoadedFeatureDataType(int index) {\r
- this.index = index;\r
- }\r
-\r
- public int getIndex() {\r
- return index;\r
- }\r
- }\r
+\r
+ /** The blender key. */\r
+ private BlenderKey blenderKey;\r
+ /** The header of the file block. */\r
+ private DnaBlockData dnaBlockData;\r
+ /** The input stream of the blend file. */\r
+ private BlenderInputStream inputStream;\r
+ /** The asset manager. */\r
+ private AssetManager assetManager;\r
+ /** A map containing the file block headers. The key is the old pointer address. */\r
+ private Map<Long, FileBlockHeader> fileBlockHeadersByOma = new HashMap<Long, FileBlockHeader>();\r
+ /** A map containing the file block headers. The key is the block code. */\r
+ private Map<Integer, List<FileBlockHeader>> fileBlockHeadersByCode = new HashMap<Integer, List<FileBlockHeader>>();\r
+ /**\r
+ * This map stores the loaded features by their old memory address. The first object in the value table is the\r
+ * loaded structure and the second - the structure already converted into proper data.\r
+ */\r
+ private Map<Long, Object[]> loadedFeatures = new HashMap<Long, Object[]>();\r
+ /**\r
+ * This map stores the loaded features by their name. Only features with ID structure can be stored here.\r
+ * The first object in the value table is the\r
+ * loaded structure and the second - the structure already converted into proper data.\r
+ */\r
+ private Map<String, Object[]> loadedFeaturesByName = new HashMap<String, Object[]>();\r
+ /** A stack that hold the parent structure of currently loaded feature. */\r
+ private Stack<Structure> parentStack = new Stack<Structure>();\r
+ /** A map storing loaded ipos. The key is the ipo's owner old memory address and the value is the ipo. */\r
+ private Map<Long, Ipo> loadedIpos = new HashMap<Long, Ipo>();\r
+ /** A list of modifiers for the specified object. */\r
+ protected Map<Long, List<Modifier>> modifiers = new HashMap<Long, List<Modifier>>();\r
+ /** A map og helpers that perform loading. */\r
+ private Map<String, AbstractBlenderHelper> helpers = new HashMap<String, AbstractBlenderHelper>();\r
+\r
+ /**\r
+ * This method sets the blender key.\r
+ * @param blenderKey\r
+ * the blender key\r
+ */\r
+ public void setBlenderKey(BlenderKey blenderKey) {\r
+ this.blenderKey = blenderKey;\r
+ }\r
+\r
+ /**\r
+ * This method returns the blender key.\r
+ * @return the blender key\r
+ */\r
+ public BlenderKey getBlenderKey() {\r
+ return blenderKey;\r
+ }\r
+\r
+ /**\r
+ * This method sets the dna block data.\r
+ * @param dnaBlockData\r
+ * the dna block data\r
+ */\r
+ public void setBlockData(DnaBlockData dnaBlockData) {\r
+ this.dnaBlockData = dnaBlockData;\r
+ }\r
+\r
+ /**\r
+ * This method returns the dna block data.\r
+ * @return the dna block data\r
+ */\r
+ public DnaBlockData getDnaBlockData() {\r
+ return dnaBlockData;\r
+ }\r
+\r
+ /**\r
+ * This method returns the asset manager.\r
+ * @return the asset manager\r
+ */\r
+ public AssetManager getAssetManager() {\r
+ return assetManager;\r
+ }\r
+\r
+ /**\r
+ * This method sets the asset manager.\r
+ * @param assetManager\r
+ * the asset manager\r
+ */\r
+ public void setAssetManager(AssetManager assetManager) {\r
+ this.assetManager = assetManager;\r
+ }\r
+\r
+ /**\r
+ * This method returns the input stream of the blend file.\r
+ * @return the input stream of the blend file\r
+ */\r
+ public BlenderInputStream getInputStream() {\r
+ return inputStream;\r
+ }\r
+\r
+ /**\r
+ * This method sets the input stream of the blend file.\r
+ * @param inputStream\r
+ * the input stream of the blend file\r
+ */\r
+ public void setInputStream(BlenderInputStream inputStream) {\r
+ this.inputStream = inputStream;\r
+ }\r
+\r
+ /**\r
+ * This method adds a file block header to the map. Its old memory address is the key.\r
+ * @param oldMemoryAddress\r
+ * the address of the block header\r
+ * @param fileBlockHeader\r
+ * the block header to store\r
+ */\r
+ public void addFileBlockHeader(Long oldMemoryAddress, FileBlockHeader fileBlockHeader) {\r
+ fileBlockHeadersByOma.put(oldMemoryAddress, fileBlockHeader);\r
+ List<FileBlockHeader> headers = fileBlockHeadersByCode.get(Integer.valueOf(fileBlockHeader.getCode()));\r
+ if (headers == null) {\r
+ headers = new ArrayList<FileBlockHeader>();\r
+ fileBlockHeadersByCode.put(Integer.valueOf(fileBlockHeader.getCode()), headers);\r
+ }\r
+ headers.add(fileBlockHeader);\r
+ }\r
+\r
+ /**\r
+ * This method returns the block header of a given memory address. If the header is not present then null is\r
+ * returned.\r
+ * @param oldMemoryAddress\r
+ * the address of the block header\r
+ * @return loaded header or null if it was not yet loaded\r
+ */\r
+ public FileBlockHeader getFileBlock(Long oldMemoryAddress) {\r
+ return fileBlockHeadersByOma.get(oldMemoryAddress);\r
+ }\r
+\r
+ /**\r
+ * This method returns a list of file blocks' headers of a specified code.\r
+ * @param code\r
+ * the code of file blocks\r
+ * @return a list of file blocks' headers of a specified code\r
+ */\r
+ public List<FileBlockHeader> getFileBlocks(Integer code) {\r
+ return fileBlockHeadersByCode.get(code);\r
+ }\r
+\r
+ /**\r
+ * This method clears the saved block headers stored in the features map.\r
+ */\r
+ public void clearFileBlocks() {\r
+ fileBlockHeadersByOma.clear();\r
+ fileBlockHeadersByCode.clear();\r
+ }\r
+\r
+ /**\r
+ * This method adds a helper instance to the helpers' map.\r
+ * @param <T>\r
+ * the type of the helper\r
+ * @param clazz\r
+ * helper's class definition\r
+ * @param helper\r
+ * the helper instance\r
+ */\r
+ public <T> void putHelper(Class<T> clazz, AbstractBlenderHelper helper) {\r
+ helpers.put(clazz.getSimpleName(), helper);\r
+ }\r
+\r
+ @SuppressWarnings("unchecked")\r
+ public <T> T getHelper(Class<?> clazz) {\r
+ return (T) helpers.get(clazz.getSimpleName());\r
+ }\r
+\r
+ /**\r
+ * This method adds a loaded feature to the map. The key is its unique old memory address.\r
+ * @param oldMemoryAddress\r
+ * the address of the feature\r
+ * @param featureName the name of the feature\r
+ * @param structure\r
+ * the filled structure of the feature\r
+ * @param feature\r
+ * the feature we want to store\r
+ */\r
+ public void addLoadedFeatures(Long oldMemoryAddress, String featureName, Structure structure, Object feature) {\r
+ if (oldMemoryAddress == null || structure == null || feature == null) {\r
+ throw new IllegalArgumentException("One of the given arguments is null!");\r
+ }\r
+ Object[] storedData = new Object[]{structure, feature};\r
+ loadedFeatures.put(oldMemoryAddress, storedData);\r
+ if (featureName != null) {\r
+ loadedFeaturesByName.put(featureName, storedData);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * This method returns the feature of a given memory address. If the feature is not yet loaded then null is\r
+ * returned.\r
+ * @param oldMemoryAddress\r
+ * the address of the feature\r
+ * @param loadedFeatureDataType\r
+ * the type of data we want to retreive it can be either filled structure or already converted feature\r
+ * @return loaded feature or null if it was not yet loaded\r
+ */\r
+ public Object getLoadedFeature(Long oldMemoryAddress, LoadedFeatureDataType loadedFeatureDataType) {\r
+ Object[] result = loadedFeatures.get(oldMemoryAddress);\r
+ if (result != null) {\r
+ return result[loadedFeatureDataType.getIndex()];\r
+ }\r
+ return null;\r
+ }\r
+\r
+ /**\r
+ * This method returns the feature of a given name. If the feature is not yet loaded then null is\r
+ * returned.\r
+ * @param featureName\r
+ * the name of the feature\r
+ * @param loadedFeatureDataType\r
+ * the type of data we want to retreive it can be either filled structure or already converted feature\r
+ * @return loaded feature or null if it was not yet loaded\r
+ */\r
+ public Object getLoadedFeature(String featureName, LoadedFeatureDataType loadedFeatureDataType) {\r
+ Object[] result = loadedFeaturesByName.get(featureName);\r
+ if (result != null) {\r
+ return result[loadedFeatureDataType.getIndex()];\r
+ }\r
+ return null;\r
+ }\r
+\r
+ /**\r
+ * This method clears the saved features stored in the features map.\r
+ */\r
+ public void clearLoadedFeatures() {\r
+ loadedFeatures.clear();\r
+ }\r
+\r
+ /**\r
+ * This method adds the structure to the parent stack.\r
+ * @param parent\r
+ * the structure to be added to the stack\r
+ */\r
+ public void pushParent(Structure parent) {\r
+ parentStack.push(parent);\r
+ }\r
+\r
+ /**\r
+ * This method removes the structure from the top of the parent's stack.\r
+ * @return the structure that was removed from the stack\r
+ */\r
+ public Structure popParent() {\r
+ try {\r
+ return parentStack.pop();\r
+ } catch (EmptyStackException e) {\r
+ return null;\r
+ }\r
+ }\r
+\r
+ /**\r
+ * This method retreives the structure at the top of the parent's stack but does not remove it.\r
+ * @return the structure from the top of the stack\r
+ */\r
+ public Structure peekParent() {\r
+ try {\r
+ return parentStack.peek();\r
+ } catch (EmptyStackException e) {\r
+ return null;\r
+ }\r
+ }\r
+\r
+ public void addIpo(Long ownerOMA, Ipo ipo) {\r
+ loadedIpos.put(ownerOMA, ipo);\r
+ }\r
+\r
+ public Ipo removeIpo(Long ownerOma) {\r
+ return loadedIpos.remove(ownerOma);\r
+ }\r
+\r
+ public Ipo getIpo(Long ownerOMA) {\r
+ return loadedIpos.get(ownerOMA);\r
+ }\r
+\r
+ /**\r
+ * This method adds a new modifier to the list.\r
+ * @param ownerOMA\r
+ * the owner's old memory address\r
+ * @param modifierType\r
+ * the type of the modifier\r
+ * @param loadedModifier\r
+ * the loaded modifier object\r
+ */\r
+ public void addModifier(Long ownerOMA, String modifierType, Object loadedModifier, Object additionalModifierData) {\r
+ List<Modifier> objectModifiers = this.modifiers.get(ownerOMA);\r
+ if (objectModifiers == null) {\r
+ objectModifiers = new ArrayList<Modifier>();\r
+ this.modifiers.put(ownerOMA, objectModifiers);\r
+ }\r
+ objectModifiers.add(new Modifier(modifierType, loadedModifier, additionalModifierData));\r
+ }\r
+\r
+ /**\r
+ * This method returns modifiers for the object specified by its old memory address and the modifier type. If no\r
+ * modifiers are found - empty list is returned. If the type is null - all modifiers for the object are returned.\r
+ * @param objectOMA\r
+ * object's old memory address\r
+ * @param type\r
+ * the type of the modifier\r
+ * @return the list of object's modifiers\r
+ */\r
+ public List<Modifier> getModifiers(Long objectOMA, String type) {\r
+ List<Modifier> result = new ArrayList<Modifier>();\r
+ List<Modifier> readModifiers = modifiers.get(objectOMA);\r
+ if (readModifiers != null && readModifiers.size() > 0) {\r
+ for (Modifier modifier : readModifiers) {\r
+ if (type == null || type.isEmpty() || modifier.getType().equals(type)) {\r
+ result.add(modifier);\r
+ }\r
+ }\r
+ }\r
+ return result;\r
+ }\r
+\r
+ /**\r
+ * This metod returns the default material.\r
+ * @return the default material\r
+ */\r
+ public synchronized Material getDefaultMaterial() {\r
+ if (blenderKey.getDefaultMaterial() == null) {\r
+ Material defaultMaterial = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");\r
+ defaultMaterial.setColor("Color", ColorRGBA.DarkGray);\r
+ blenderKey.setDefaultMaterial(defaultMaterial);\r
+ }\r
+ return blenderKey.getDefaultMaterial();\r
+ }\r
+\r
+ /**\r
+ * This enum defines what loaded data type user wants to retreive. It can be either filled structure or already\r
+ * converted data.\r
+ * @author Marcin Roguski\r
+ */\r
+ public static enum LoadedFeatureDataType {\r
+\r
+ LOADED_STRUCTURE(0), LOADED_FEATURE(1);\r
+ private int index;\r
+\r
+ private LoadedFeatureDataType(int index) {\r
+ this.index = index;\r
+ }\r
+\r
+ public int getIndex() {\r
+ return index;\r
+ }\r
+ }\r
}\r
* the type of stored data in the array\r
*/\r
public class DynamicArray<T> implements Cloneable {\r
- /** An array object that holds the required data. */\r
- private T[] array;\r
- /**\r
- * This table holds the sizes of dimetions of the dynamic table. It's length specifies the table dimension or a\r
- * pointer level. For example: if tableSizes.length == 3 then it either specifies a dynamic table of fixed lengths:\r
- * dynTable[a][b][c], where a,b,c are stored in the tableSizes table.\r
- */\r
- private int[] tableSizes;\r
\r
- /**\r
- * Constructor. Builds an empty array of the specified sizes.\r
- * @param tableSizes\r
- * the sizes of the table\r
- * @throws BlenderFileException\r
- * an exception is thrown if one of the sizes is not a positive number\r
- */\r
- @SuppressWarnings("unchecked")\r
- public DynamicArray(int[] tableSizes) throws BlenderFileException {\r
- this.tableSizes = tableSizes;\r
- int totalSize = 1;\r
- for(int size : tableSizes) {\r
- if(size <= 0) {\r
- throw new BlenderFileException("The size of the table must be positive!");\r
- }\r
- totalSize *= size;\r
- }\r
- this.array = (T[])new Object[totalSize];\r
- }\r
+ /** An array object that holds the required data. */\r
+ private T[] array;\r
+ /**\r
+ * This table holds the sizes of dimetions of the dynamic table. It's length specifies the table dimension or a\r
+ * pointer level. For example: if tableSizes.length == 3 then it either specifies a dynamic table of fixed lengths:\r
+ * dynTable[a][b][c], where a,b,c are stored in the tableSizes table.\r
+ */\r
+ private int[] tableSizes;\r
\r
- /**\r
- * Constructor. Builds an empty array of the specified sizes.\r
- * @param tableSizes\r
- * the sizes of the table\r
- * @throws BlenderFileException\r
- * an exception is thrown if one of the sizes is not a positive number\r
- */\r
- public DynamicArray(int[] tableSizes, T[] data) throws BlenderFileException {\r
- this.tableSizes = tableSizes;\r
- int totalSize = 1;\r
- for(int size : tableSizes) {\r
- if(size <= 0) {\r
- throw new BlenderFileException("The size of the table must be positive!");\r
- }\r
- totalSize *= size;\r
- }\r
- if(totalSize != data.length) {\r
- throw new IllegalArgumentException("The size of the table does not match the size of the given data!");\r
- }\r
- this.array = data;\r
- }\r
+ /**\r
+ * Constructor. Builds an empty array of the specified sizes.\r
+ * @param tableSizes\r
+ * the sizes of the table\r
+ * @throws BlenderFileException\r
+ * an exception is thrown if one of the sizes is not a positive number\r
+ */\r
+ @SuppressWarnings("unchecked")\r
+ public DynamicArray(int[] tableSizes) throws BlenderFileException {\r
+ this.tableSizes = tableSizes;\r
+ int totalSize = 1;\r
+ for (int size : tableSizes) {\r
+ if (size <= 0) {\r
+ throw new BlenderFileException("The size of the table must be positive!");\r
+ }\r
+ totalSize *= size;\r
+ }\r
+ this.array = (T[]) new Object[totalSize];\r
+ }\r
\r
- @Override\r
- public Object clone() throws CloneNotSupportedException {\r
- return super.clone();\r
- }\r
+ /**\r
+ * Constructor. Builds an empty array of the specified sizes.\r
+ * @param tableSizes\r
+ * the sizes of the table\r
+ * @throws BlenderFileException\r
+ * an exception is thrown if one of the sizes is not a positive number\r
+ */\r
+ public DynamicArray(int[] tableSizes, T[] data) throws BlenderFileException {\r
+ this.tableSizes = tableSizes;\r
+ int totalSize = 1;\r
+ for (int size : tableSizes) {\r
+ if (size <= 0) {\r
+ throw new BlenderFileException("The size of the table must be positive!");\r
+ }\r
+ totalSize *= size;\r
+ }\r
+ if (totalSize != data.length) {\r
+ throw new IllegalArgumentException("The size of the table does not match the size of the given data!");\r
+ }\r
+ this.array = data;\r
+ }\r
\r
- /**\r
- * This method returns a value on the specified position. The dimension of the table is not taken into\r
- * consideration.\r
- * @param position\r
- * the position of the data\r
- * @return required data\r
- */\r
- public T get(int position) {\r
- return array[position];\r
- }\r
+ @Override\r
+ public Object clone() throws CloneNotSupportedException {\r
+ return super.clone();\r
+ }\r
\r
- /**\r
- * This method returns a value on the specified position in multidimensional array. Be careful not to exceed the\r
- * table boundaries. Check the table's dimension first.\r
- * @param position\r
- * the position of the data indices of data position\r
- * @return required data required data\r
- */\r
- public T get(int... position) {\r
- if(position.length != tableSizes.length) {\r
- throw new ArrayIndexOutOfBoundsException("The table accepts " + tableSizes.length + " indexing number(s)!");\r
- }\r
- int index = 0;\r
- for(int i = 0; i < position.length - 1; ++i) {\r
- index += position[i] * tableSizes[i + 1];\r
- }\r
- index += position[position.length - 1];\r
- return array[index];\r
- }\r
+ /**\r
+ * This method returns a value on the specified position. The dimension of the table is not taken into\r
+ * consideration.\r
+ * @param position\r
+ * the position of the data\r
+ * @return required data\r
+ */\r
+ public T get(int position) {\r
+ return array[position];\r
+ }\r
\r
- /**\r
- * This method returns the total amount of data stored in the array.\r
- * @return the total amount of data stored in the array\r
- */\r
- public int getTotalSize() {\r
- return array.length;\r
- }\r
+ /**\r
+ * This method returns a value on the specified position in multidimensional array. Be careful not to exceed the\r
+ * table boundaries. Check the table's dimension first.\r
+ * @param position\r
+ * the position of the data indices of data position\r
+ * @return required data required data\r
+ */\r
+ public T get(int... position) {\r
+ if (position.length != tableSizes.length) {\r
+ throw new ArrayIndexOutOfBoundsException("The table accepts " + tableSizes.length + " indexing number(s)!");\r
+ }\r
+ int index = 0;\r
+ for (int i = 0; i < position.length - 1; ++i) {\r
+ index += position[i] * tableSizes[i + 1];\r
+ }\r
+ index += position[position.length - 1];\r
+ return array[index];\r
+ }\r
\r
- @Override\r
- public String toString() {\r
- StringBuilder result = new StringBuilder();\r
- if(array instanceof Character[]) {//in case of character array we convert it to String\r
- for(int i = 0; i < array.length && (Character)array[i] != '\0'; ++i) {//strings are terminater with '0'\r
- result.append(array[i]);\r
- }\r
- } else {\r
- result.append('[');\r
- for(int i = 0; i < array.length; ++i) {\r
- result.append(array[i].toString());\r
- if(i + 1 < array.length) {\r
- result.append(',');\r
- }\r
- }\r
- result.append(']');\r
- }\r
- return result.toString();\r
- }\r
+ /**\r
+ * This method returns the total amount of data stored in the array.\r
+ * @return the total amount of data stored in the array\r
+ */\r
+ public int getTotalSize() {\r
+ return array.length;\r
+ }\r
+\r
+ @Override\r
+ public String toString() {\r
+ StringBuilder result = new StringBuilder();\r
+ if (array instanceof Character[]) {//in case of character array we convert it to String\r
+ for (int i = 0; i < array.length && (Character) array[i] != '\0'; ++i) {//strings are terminater with '0'\r
+ result.append(array[i]);\r
+ }\r
+ } else {\r
+ result.append('[');\r
+ for (int i = 0; i < array.length; ++i) {\r
+ result.append(array[i].toString());\r
+ if (i + 1 < array.length) {\r
+ result.append(',');\r
+ }\r
+ }\r
+ result.append(']');\r
+ }\r
+ return result.toString();\r
+ }\r
}
\ No newline at end of file
* This class converts blender file blocks into jMonkeyEngine data structures.\r
* @author Marcin Roguski\r
*/\r
-public class JmeConverter implements IBlenderConverter<Node, Camera, Light, Object, List<Geometry>, Material> {\r
- private static final Logger LOGGER = Logger.getLogger(JmeConverter.class.getName());\r
+public class JmeConverter implements BlenderConverter<Node, Camera, Light, Object, List<Geometry>, Material> {\r
\r
- private final DataRepository dataRepository;\r
+ private static final Logger LOGGER = Logger.getLogger(JmeConverter.class.getName());\r
+ private final DataRepository dataRepository;\r
\r
- /**\r
- * Constructor. Creates the loader and checks if the given data is correct.\r
- * @param dataRepository\r
- * the data repository; it should have the following field set: - asset manager - blender key - dna block\r
- * data - blender input stream Otherwise IllegalArgumentException will be thrown.\r
- * @param featuresToLoad\r
- * bitwise flag describing what features are to be loaded\r
- * @see FeaturesToLoad FeaturesToLoad\r
- */\r
- public JmeConverter(DataRepository dataRepository) {\r
- //validating the given data first\r
- if(dataRepository.getAssetManager() == null) {\r
- throw new IllegalArgumentException("Cannot find asset manager!");\r
- }\r
- if(dataRepository.getBlenderKey() == null) {\r
- throw new IllegalArgumentException("Cannot find blender key!");\r
- }\r
- if(dataRepository.getDnaBlockData() == null) {\r
- throw new IllegalArgumentException("Cannot find dna block!");\r
- }\r
- if(dataRepository.getInputStream() == null) {\r
- throw new IllegalArgumentException("Cannot find blender file stream!");\r
- }\r
- this.dataRepository = dataRepository;\r
- }\r
+ /**\r
+ * Constructor. Creates the loader and checks if the given data is correct.\r
+ * @param dataRepository\r
+ * the data repository; it should have the following field set: - asset manager - blender key - dna block\r
+ * data - blender input stream Otherwise IllegalArgumentException will be thrown.\r
+ * @param featuresToLoad\r
+ * bitwise flag describing what features are to be loaded\r
+ * @see FeaturesToLoad FeaturesToLoad\r
+ */\r
+ public JmeConverter(DataRepository dataRepository) {\r
+ //validating the given data first\r
+ if (dataRepository.getAssetManager() == null) {\r
+ throw new IllegalArgumentException("Cannot find asset manager!");\r
+ }\r
+ if (dataRepository.getBlenderKey() == null) {\r
+ throw new IllegalArgumentException("Cannot find blender key!");\r
+ }\r
+ if (dataRepository.getDnaBlockData() == null) {\r
+ throw new IllegalArgumentException("Cannot find dna block!");\r
+ }\r
+ if (dataRepository.getInputStream() == null) {\r
+ throw new IllegalArgumentException("Cannot find blender file stream!");\r
+ }\r
+ this.dataRepository = dataRepository;\r
+ }\r
\r
- @Override\r
- public Node toScene(Structure structure) {//TODO: poprawny import sceny\r
- if((dataRepository.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.SCENES) == 0) {\r
- return null;\r
- }\r
- Structure id = (Structure)structure.getFieldValue("id");\r
- String sceneName = id.getFieldValue("name").toString();\r
- \r
- //veryfying layers to be loaded\r
- if(dataRepository.getBlenderKey().getLayersToLoad()<0) {\r
- int lay = ((Number)structure.getFieldValue("lay")).intValue();\r
- dataRepository.getBlenderKey().setLayersToLoad(lay);//load only current layer\r
- }\r
- return new Node(sceneName);\r
- }\r
+ @Override\r
+ public Node toScene(Structure structure) {//TODO: poprawny import sceny\r
+ if ((dataRepository.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.SCENES) == 0) {\r
+ return null;\r
+ }\r
+ Structure id = (Structure) structure.getFieldValue("id");\r
+ String sceneName = id.getFieldValue("name").toString();\r
\r
- @Override\r
- public Camera toCamera(Structure structure) throws BlenderFileException {\r
- if((dataRepository.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.CAMERAS) == 0) {\r
- return null;\r
- }\r
- CameraHelper cameraHelper = dataRepository.getHelper(CameraHelper.class);\r
- return cameraHelper.toCamera(structure);\r
- }\r
+ //veryfying layers to be loaded\r
+ if (dataRepository.getBlenderKey().getLayersToLoad() < 0) {\r
+ int lay = ((Number) structure.getFieldValue("lay")).intValue();\r
+ dataRepository.getBlenderKey().setLayersToLoad(lay);//load only current layer\r
+ }\r
+ return new Node(sceneName);\r
+ }\r
\r
- @Override\r
- public Light toLight(Structure structure) throws BlenderFileException {\r
- if((dataRepository.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.LIGHTS) == 0) {\r
- return null;\r
- }\r
- LightHelper lightHelper = dataRepository.getHelper(LightHelper.class);\r
- return lightHelper.toLight(structure, dataRepository);\r
- }\r
+ @Override\r
+ public Camera toCamera(Structure structure) throws BlenderFileException {\r
+ if ((dataRepository.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.CAMERAS) == 0) {\r
+ return null;\r
+ }\r
+ CameraHelper cameraHelper = dataRepository.getHelper(CameraHelper.class);\r
+ return cameraHelper.toCamera(structure);\r
+ }\r
\r
- @Override\r
- public Object toObject(Structure structure) throws BlenderFileException {\r
- int lay = ((Number)structure.getFieldValue("lay")).intValue();\r
- if((lay & dataRepository.getBlenderKey().getLayersToLoad()) == 0 ||\r
- (dataRepository.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.OBJECTS) == 0) {\r
- return null;\r
- }\r
- ObjectHelper objectHelper = dataRepository.getHelper(ObjectHelper.class);\r
- return objectHelper.toObject(structure, dataRepository);\r
- }\r
+ @Override\r
+ public Light toLight(Structure structure) throws BlenderFileException {\r
+ if ((dataRepository.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.LIGHTS) == 0) {\r
+ return null;\r
+ }\r
+ LightHelper lightHelper = dataRepository.getHelper(LightHelper.class);\r
+ return lightHelper.toLight(structure, dataRepository);\r
+ }\r
\r
- @Override\r
- public List<Geometry> toMesh(Structure structure) throws BlenderFileException {\r
- MeshHelper meshHelper = dataRepository.getHelper(MeshHelper.class);\r
- return meshHelper.toMesh(structure, dataRepository);\r
- }\r
+ @Override\r
+ public Object toObject(Structure structure) throws BlenderFileException {\r
+ int lay = ((Number) structure.getFieldValue("lay")).intValue();\r
+ if ((lay & dataRepository.getBlenderKey().getLayersToLoad()) == 0\r
+ || (dataRepository.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.OBJECTS) == 0) {\r
+ return null;\r
+ }\r
+ ObjectHelper objectHelper = dataRepository.getHelper(ObjectHelper.class);\r
+ return objectHelper.toObject(structure, dataRepository);\r
+ }\r
\r
- @Override\r
- public Material toMaterial(Structure structure) throws BlenderFileException {\r
- if((dataRepository.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.MATERIALS) == 0) {\r
- return null;\r
- }\r
- MaterialHelper materialHelper = dataRepository.getHelper(MaterialHelper.class);\r
- return materialHelper.toMaterial(structure, dataRepository);\r
- }\r
+ @Override\r
+ public List<Geometry> toMesh(Structure structure) throws BlenderFileException {\r
+ MeshHelper meshHelper = dataRepository.getHelper(MeshHelper.class);\r
+ return meshHelper.toMesh(structure, dataRepository);\r
+ }\r
\r
- /**\r
- * This method returns the data read from the WORLD file block. The block contains data that can be stored as\r
- * separate jme features and therefore cannot be returned as a single jME scene feature.\r
- * @param structure\r
- * the structure with WORLD block data\r
- * @return data read from the WORLD block that can be added to the scene\r
- */\r
- public WorldData toWorldData(Structure structure) {\r
- WorldData result = new WorldData();\r
+ @Override\r
+ public Material toMaterial(Structure structure) throws BlenderFileException {\r
+ if ((dataRepository.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.MATERIALS) == 0) {\r
+ return null;\r
+ }\r
+ MaterialHelper materialHelper = dataRepository.getHelper(MaterialHelper.class);\r
+ return materialHelper.toMaterial(structure, dataRepository);\r
+ }\r
\r
- //reading ambient light\r
- AmbientLight ambientLight = new AmbientLight();\r
- float ambr = ((Number)structure.getFieldValue("ambr")).floatValue();\r
- float ambg = ((Number)structure.getFieldValue("ambg")).floatValue();\r
- float ambb = ((Number)structure.getFieldValue("ambb")).floatValue();\r
- ambientLight.setColor(new ColorRGBA(ambr, ambg, ambb, 0.0f));\r
- result.setAmbientLight(ambientLight);\r
+ /**\r
+ * This method returns the data read from the WORLD file block. The block contains data that can be stored as\r
+ * separate jme features and therefore cannot be returned as a single jME scene feature.\r
+ * @param structure\r
+ * the structure with WORLD block data\r
+ * @return data read from the WORLD block that can be added to the scene\r
+ */\r
+ public WorldData toWorldData(Structure structure) {\r
+ WorldData result = new WorldData();\r
\r
- return result;\r
- }\r
+ //reading ambient light\r
+ AmbientLight ambientLight = new AmbientLight();\r
+ float ambr = ((Number) structure.getFieldValue("ambr")).floatValue();\r
+ float ambg = ((Number) structure.getFieldValue("ambg")).floatValue();\r
+ float ambb = ((Number) structure.getFieldValue("ambb")).floatValue();\r
+ ambientLight.setColor(new ColorRGBA(ambr, ambg, ambb, 0.0f));\r
+ result.setAmbientLight(ambientLight);\r
+\r
+ return result;\r
+ }\r
}\r
* @author Marcin Roguski\r
*/\r
public class Pointer {\r
- /** The data repository. */\r
- private DataRepository dataRepository;\r
- /** The level of the pointer. */\r
- private int pointerLevel;\r
- /** The address in file it points to. */\r
- private long oldMemoryAddress;\r
- /** This variable indicates if the field is a function pointer. */\r
- public boolean function;\r
\r
- /**\r
- * Constructr. Stores the basic data about the pointer.\r
- * @param pointerLevel\r
- * the level of the pointer\r
- * @param function\r
- * this variable indicates if the field is a function pointer\r
- * @param dataRepository\r
- * the repository f data; used in fetching the value that the pointer points\r
- */\r
- public Pointer(int pointerLevel, boolean function, DataRepository dataRepository) {\r
- this.pointerLevel = pointerLevel;\r
- this.function = function;\r
- this.dataRepository = dataRepository;\r
- }\r
+ /** The data repository. */\r
+ private DataRepository dataRepository;\r
+ /** The level of the pointer. */\r
+ private int pointerLevel;\r
+ /** The address in file it points to. */\r
+ private long oldMemoryAddress;\r
+ /** This variable indicates if the field is a function pointer. */\r
+ public boolean function;\r
\r
- /**\r
- * This method fills the pointer with its address value (it doesn't get the actual data yet. Use the 'fetch' method\r
- * for this.\r
- * @param inputStream\r
- * the stream we read the pointer value from\r
- */\r
- public void fill(BlenderInputStream inputStream) {\r
- oldMemoryAddress = inputStream.readPointer();\r
- }\r
+ /**\r
+ * Constructr. Stores the basic data about the pointer.\r
+ * @param pointerLevel\r
+ * the level of the pointer\r
+ * @param function\r
+ * this variable indicates if the field is a function pointer\r
+ * @param dataRepository\r
+ * the repository f data; used in fetching the value that the pointer points\r
+ */\r
+ public Pointer(int pointerLevel, boolean function, DataRepository dataRepository) {\r
+ this.pointerLevel = pointerLevel;\r
+ this.function = function;\r
+ this.dataRepository = dataRepository;\r
+ }\r
\r
- /**\r
- * This method fetches the data stored under the given address.\r
- * @param inputStream\r
- * the stream we read data from\r
- * @param dataIndices\r
- * the offset of the data in the table pointed by the pointer\r
- * @return the data read from the file\r
- * @throws BlenderFileException\r
- * this exception is thrown when the blend file structure is somehow invalid or corrupted\r
- */\r
- public List<Structure> fetchData(BlenderInputStream inputStream) throws BlenderFileException {\r
- if(oldMemoryAddress == 0) {\r
- throw new NullPointerException("The pointer points to nothing!");\r
- }\r
- List<Structure> structures = null;\r
- FileBlockHeader dataFileBlock = dataRepository.getFileBlock(oldMemoryAddress);\r
- if(pointerLevel > 1) {\r
- int pointersAmount = dataFileBlock.getSize() / inputStream.getPointerSize() * dataFileBlock.getCount();\r
- for(int i = 0; i < pointersAmount; ++i) {\r
- inputStream.setPosition(dataFileBlock.getBlockPosition() + inputStream.getPointerSize() * i);\r
- long oldMemoryAddress = inputStream.readPointer();\r
- if(oldMemoryAddress != 0L) {\r
- Pointer p = new Pointer(pointerLevel - 1, this.function, dataRepository);\r
- p.oldMemoryAddress = oldMemoryAddress;\r
- if(structures == null) {\r
- structures = p.fetchData(inputStream);\r
- } else {\r
- structures.addAll(p.fetchData(inputStream));\r
- }\r
- }\r
- }\r
- } else {\r
- inputStream.setPosition(dataFileBlock.getBlockPosition());\r
- structures = new ArrayList<Structure>(dataFileBlock.getCount());\r
- for(int i = 0; i < dataFileBlock.getCount(); ++i) {\r
- Structure structure = dataRepository.getDnaBlockData().getStructure(dataFileBlock.getSdnaIndex());\r
- structure.fill(inputStream);\r
- structures.add(structure);\r
- }\r
- return structures;\r
- }\r
- return structures;\r
- }\r
+ /**\r
+ * This method fills the pointer with its address value (it doesn't get the actual data yet. Use the 'fetch' method\r
+ * for this.\r
+ * @param inputStream\r
+ * the stream we read the pointer value from\r
+ */\r
+ public void fill(BlenderInputStream inputStream) {\r
+ oldMemoryAddress = inputStream.readPointer();\r
+ }\r
\r
- /**\r
- * This method indicates if this pointer points to a function.\r
- * @return <b>true</b> if this is a function pointer and <b>false</b> otherwise\r
- */\r
- public boolean isFunction() {\r
- return function;\r
- }\r
+ /**\r
+ * This method fetches the data stored under the given address.\r
+ * @param inputStream\r
+ * the stream we read data from\r
+ * @param dataIndices\r
+ * the offset of the data in the table pointed by the pointer\r
+ * @return the data read from the file\r
+ * @throws BlenderFileException\r
+ * this exception is thrown when the blend file structure is somehow invalid or corrupted\r
+ */\r
+ public List<Structure> fetchData(BlenderInputStream inputStream) throws BlenderFileException {\r
+ if (oldMemoryAddress == 0) {\r
+ throw new NullPointerException("The pointer points to nothing!");\r
+ }\r
+ List<Structure> structures = null;\r
+ FileBlockHeader dataFileBlock = dataRepository.getFileBlock(oldMemoryAddress);\r
+ if (pointerLevel > 1) {\r
+ int pointersAmount = dataFileBlock.getSize() / inputStream.getPointerSize() * dataFileBlock.getCount();\r
+ for (int i = 0; i < pointersAmount; ++i) {\r
+ inputStream.setPosition(dataFileBlock.getBlockPosition() + inputStream.getPointerSize() * i);\r
+ long oldMemoryAddress = inputStream.readPointer();\r
+ if (oldMemoryAddress != 0L) {\r
+ Pointer p = new Pointer(pointerLevel - 1, this.function, dataRepository);\r
+ p.oldMemoryAddress = oldMemoryAddress;\r
+ if (structures == null) {\r
+ structures = p.fetchData(inputStream);\r
+ } else {\r
+ structures.addAll(p.fetchData(inputStream));\r
+ }\r
+ }\r
+ }\r
+ } else {\r
+ inputStream.setPosition(dataFileBlock.getBlockPosition());\r
+ structures = new ArrayList<Structure>(dataFileBlock.getCount());\r
+ for (int i = 0; i < dataFileBlock.getCount(); ++i) {\r
+ Structure structure = dataRepository.getDnaBlockData().getStructure(dataFileBlock.getSdnaIndex());\r
+ structure.fill(inputStream);\r
+ structures.add(structure);\r
+ }\r
+ return structures;\r
+ }\r
+ return structures;\r
+ }\r
\r
- /**\r
- * This method indicates if this is a null-pointer or not.\r
- * @return <b>true</b> if the pointer is null and <b>false</b> otherwise\r
- */\r
- public boolean isNull() {\r
- return oldMemoryAddress == 0;\r
- }\r
+ /**\r
+ * This method indicates if this pointer points to a function.\r
+ * @return <b>true</b> if this is a function pointer and <b>false</b> otherwise\r
+ */\r
+ public boolean isFunction() {\r
+ return function;\r
+ }\r
\r
- /**\r
- * This method returns the old memory address of the structure pointed by the pointer.\r
- * @return the old memory address of the structure pointed by the pointer\r
- */\r
- public long getOldMemoryAddress() {\r
- return oldMemoryAddress;\r
- }\r
+ /**\r
+ * This method indicates if this is a null-pointer or not.\r
+ * @return <b>true</b> if the pointer is null and <b>false</b> otherwise\r
+ */\r
+ public boolean isNull() {\r
+ return oldMemoryAddress == 0;\r
+ }\r
\r
- @Override\r
- public String toString() {\r
- return oldMemoryAddress == 0 ? "{$null$}" : "{$" + oldMemoryAddress + "$}";\r
- }\r
+ /**\r
+ * This method returns the old memory address of the structure pointed by the pointer.\r
+ * @return the old memory address of the structure pointed by the pointer\r
+ */\r
+ public long getOldMemoryAddress() {\r
+ return oldMemoryAddress;\r
+ }\r
\r
- @Override\r
- public int hashCode() {\r
- return 31 + (int)(oldMemoryAddress ^ oldMemoryAddress >>> 32);\r
- }\r
+ @Override\r
+ public String toString() {\r
+ return oldMemoryAddress == 0 ? "{$null$}" : "{$" + oldMemoryAddress + "$}";\r
+ }\r
\r
- @Override\r
- public boolean equals(Object obj) {\r
- if(this == obj) {\r
- return true;\r
- }\r
- if(obj == null) {\r
- return false;\r
- }\r
- if(this.getClass() != obj.getClass()) {\r
- return false;\r
- }\r
- Pointer other = (Pointer)obj;\r
- if(oldMemoryAddress != other.oldMemoryAddress) {\r
- return false;\r
- }\r
- return true;\r
- }\r
+ @Override\r
+ public int hashCode() {\r
+ return 31 + (int) (oldMemoryAddress ^ oldMemoryAddress >>> 32);\r
+ }\r
+\r
+ @Override\r
+ public boolean equals(Object obj) {\r
+ if (this == obj) {\r
+ return true;\r
+ }\r
+ if (obj == null) {\r
+ return false;\r
+ }\r
+ if (this.getClass() != obj.getClass()) {\r
+ return false;\r
+ }\r
+ Pointer other = (Pointer) obj;\r
+ if (oldMemoryAddress != other.oldMemoryAddress) {\r
+ return false;\r
+ }\r
+ return true;\r
+ }\r
}\r
/**
* Set user transform.
* Combine the given transforms to bone's current transforms
- * @see setUserControl
*/
public void setUserTransforms(Vector3f translation, Quaternion rotation, Vector3f scale) {
if (!userControl) {
}
/**
- * Returns teh local transform of this bone combined with the given position and rotation
+ * Returns the local transform of this bone combined with the given position and rotation
* @param position a position
* @param rotation a rotation
- * @return
*/
public Transform getCombinedTransform(Vector3f position, Quaternion rotation){
rotation.mult(localPos, tmpTransform.getTranslation()).addLocal(position);
* deserialize object\r
* @param compactIndex compacted object index\r
* @param store\r
- * @return\r
*/\r
protected abstract T deserialize(int compactIndex, T store);\r
\r
/**\r
* serialized size of one object element\r
- * @return\r
*/\r
protected abstract int getTupleSize();\r
\r
The <code>com.jme3.application</code> provides a toolset for jME3 applications
to interact with various components of the engine. Typically, the
{@link com.jme3.app.Application} class will be extended and the update() method
-implemented to provide functionality for the main loop. <br/>
+implemented to provide functionality for the main loop. <br>
<p>
An <code>Application</code> will typically provide the following services:
<ul>
<h3>Usage</h3>
-An example use of the Application class is as follows<br/>
-<br/>
+An example use of the Application class is as follows<br>
+<br>
<code>
-public class ExampleUse extends Application {<br/>
-<br/>
- private Node rootNode = new Node("Root Node");<br/>
-<br/>
- public static void main(String[] args){<br/>
- ExampleUse app = new ExampleUse();<br/>
- app.start();<br/>
- }<br/>
-<br/>
- @Override<br/>
- public void initialize(){<br/>
- super.initialize();<br/>
-<br/>
- // attach root node to viewport<br/>
- viewPort.attachScene(rootNode);<br/>
- }<br/>
-<br/>
- @Override<br/>
- public void update(){<br/>
- super.update();<br/>
-<br/>
- float tpf = timer.getTimePerFrame();<br/>
-<br/>
- // update rootNode<br/>
- rootNode.updateLogicalState(tpf);<br/>
- rootNode.updateGeometricState();<br/>
-<br/>
- // render the viewports<br/>
- renderManager.render(tpf);<br/>
- }<br/>
-}<br/>
-<br/>
+public class ExampleUse extends Application {<br>
+<br>
+ private Node rootNode = new Node("Root Node");<br>
+<br>
+ public static void main(String[] args){<br>
+ ExampleUse app = new ExampleUse();<br>
+ app.start();<br>
+ }<br>
+<br>
+ @Override<br>
+ public void initialize(){<br>
+ super.initialize();<br>
+<br>
+ // attach root node to viewport<br>
+ viewPort.attachScene(rootNode);<br>
+ }<br>
+<br>
+ @Override<br>
+ public void update(){<br>
+ super.update();<br>
+<br>
+ float tpf = timer.getTimePerFrame();<br>
+<br>
+ // update rootNode<br>
+ rootNode.updateLogicalState(tpf);<br>
+ rootNode.updateGeometricState();<br>
+<br>
+ // render the viewports<br>
+ renderManager.render(tpf);<br>
+ }<br>
+}<br>
+<br>
</code>
</body>
\r
/**\r
* Calls render for all attached states, do not call directly.\r
- * @param rm The RenderManager\r
*/\r
public void postRender(){\r
AppState[] array = getArray();\r
*
* @param manager
* @param key
- * @return
+ * @return The {@link AssetInfo} that was located, or null if not found.
*/
public AssetInfo locate(AssetManager manager, AssetKey key);
}
* TGA and DDS.
*
* @param name The name of the texture to load.
- * @return
+ * @return The texture that was loaded
*
* @see AssetManager#loadAsset(com.jme3.asset.AssetKey)
*/
/**
* Load audio file, supported types are WAV or OGG.
* @param key
- * @return
+ * @return The audio data loaded
*
* @see AssetManager#loadAsset(com.jme3.asset.AssetKey)
*/
* Load audio file, supported types are WAV or OGG.
* The file is loaded without stream-mode.
* @param name
- * @return
+ * @return The audio data loaded
*
* @see AssetManager#loadAsset(com.jme3.asset.AssetKey)
*/
* Loads a named model. Models can be jME3 object files (J3O) or
* OgreXML/OBJ files.
* @param key
- * @return
+ * @return The model that was loaded
*
* @see AssetManager#loadAsset(com.jme3.asset.AssetKey)
*/
* Loads a named model. Models can be jME3 object files (J3O) or
* OgreXML/OBJ files.
* @param name
- * @return
+ * @return The model that was loaded
*
* @see AssetManager#loadAsset(com.jme3.asset.AssetKey)
*/
/**
* Load a material (J3M) file.
* @param name
- * @return
+ * @return The material that was loaded
*
* @see AssetManager#loadAsset(com.jme3.asset.AssetKey)
*/
* and are with the extension "fnt".
*
* @param name
- * @return
+ * @return The font loaded
*
* @see AssetManager#loadAsset(com.jme3.asset.AssetKey)
*/
/**
* Enable smart caching for textures
- * @return
+ * @return true to enable smart cache
*/
@Override
public boolean useSmartCache(){
/**
* Sets the environment, used for reverb effects.
*
- * @see PointAudioSource#setReverbEnabled(boolean)
+ * @see AudioNode#setReverbEnabled(boolean)
* @param env The environment to set.
*/
public void setEnvironment(Environment env);
* <code>transform</code> modifies the center of the box to reflect the\r
* change made via a rotation, translation and scale.\r
* \r
- * @param rotate\r
- * the rotation change.\r
- * @param translate\r
- * the translation change.\r
- * @param scale\r
- * the size change.\r
+ * @param trans \r
+ * the transform to apply\r
* @param store\r
* box to store result in\r
*/\r
* intersects determines if this Bounding Box intersects with another given\r
* bounding volume. If so, true is returned, otherwise, false is returned.\r
* \r
- * @see com.jme.bounding.BoundingVolume#intersects(com.jme.bounding.BoundingVolume)\r
+ * @see BoundingVolume#intersects(com.jme3.bounding.BoundingVolume) \r
*/\r
public boolean intersects(BoundingVolume bv) {\r
return bv.intersectsBoundingBox(this);\r
/**\r
* determines if this bounding box intersects a given bounding sphere.\r
* \r
- * @see com.jme.bounding.BoundingVolume#intersectsSphere(com.jme.bounding.BoundingSphere)\r
+ * @see BoundingVolume#intersectsSphere(com.jme3.bounding.BoundingSphere)\r
*/\r
public boolean intersectsSphere(BoundingSphere bs) {\r
assert Vector3f.isValidVector(center) && Vector3f.isValidVector(bs.center);\r
* two boxes intersect in any way, true is returned. Otherwise, false is\r
* returned.\r
* \r
- * @see com.jme.bounding.BoundingVolume#intersectsBoundingBox(com.jme.bounding.BoundingBox)\r
+ * @see BoundingVolume#intersectsBoundingBox(com.jme3.bounding.BoundingBox)\r
*/\r
public boolean intersectsBoundingBox(BoundingBox bb) {\r
assert Vector3f.isValidVector(center) && Vector3f.isValidVector(bb.center);\r
* determines if this bounding box intersects with a given ray object. If an\r
* intersection has occurred, true is returned, otherwise false is returned.\r
* \r
- * @see com.jme.bounding.BoundingVolume#intersects(com.jme.math.Ray)\r
+ * @see BoundingVolume#intersects(com.jme3.math.Ray) \r
*/\r
public boolean intersects(Ray ray) {\r
assert Vector3f.isValidVector(center);\r
/**\r
* C code ported from http://www.cs.lth.se/home/Tomas_Akenine_Moller/code/tribox3.txt\r
*\r
- * @param v1\r
- * @param v2\r
- * @param v3\r
- * @return\r
+ * @param v1 The first point in the triangle\r
+ * @param v2 The second point in the triangle\r
+ * @param v3 The third point in the triangle\r
+ * @return True if the bounding box intersects the triangle, false\r
+ * otherwise.\r
*/\r
public boolean intersects(Vector3f v1, Vector3f v2, Vector3f v3){\r
return Intersection.intersect(this, v1, v2, v3);\r
* <code>transform</code> modifies the center of the sphere to reflect the\r
* change made via a rotation, translation and scale.\r
*\r
- * @param rotate\r
- * the rotation change.\r
- * @param translate\r
- * the translation change.\r
- * @param scale\r
- * the size change.\r
+ * @param trans\r
+ * the transform to apply\r
* @param store\r
* sphere to store result in\r
* @return BoundingVolume\r
package com.jme3.input.controls;
+import com.jme3.input.MouseInput;
+
/**
* A <code>MouseButtonTrigger</code> is used as a mapping to receive events
* from mouse buttons. It is generally expected for a mouse to have at least
this.color.set(color);
}
- /**
+
+ /*
* Returns true if the light is enabled
*
* @return true if the light is enabled
}
/**
- * Clones this material. The result
- * @return
+ * Clones this material. The result is returned.
*/
@Override
public Material clone() {
}
}
+ /**
+ * Returns the currently active technique.
+ * <p>
+ * The technique is selected automatically by the {@link RenderManager}
+ * based on system capabilities. Users may select their own
+ * technique by using
+ * {@link #selectTechnique(java.lang.String, com.jme3.renderer.RenderManager) }.
+ *
+ * @return the currently active technique.
+ *
+ * @see #selectTechnique(java.lang.String, com.jme3.renderer.RenderManager)
+ */
public Technique getActiveTechnique() {
return technique;
}
+ /**
+ * Check if the transparent value marker is set on this material.
+ * @return True if the transparent value marker is set on this material.
+ * @see #setTransparent(boolean)
+ */
public boolean isTransparent() {
return transparent;
}
return def;
}
+ /**
+ * Returns the parameter set on this material with the given name,
+ * returns <code>null</code> if the parameter is not set.
+ *
+ * @param name The parameter name to look up.
+ * @return The MatParam if set, or null if not set.
+ */
public MatParam getParam(String name) {
MatParam param = paramValues.get(name);
if (param instanceof MatParam) {
return null;
}
+ /**
+ * Returns the texture parameter set on this material with the given name,
+ * returns <code>null</code> if the parameter is not set.
+ *
+ * @param name The parameter name to look up.
+ * @return The MatParamTexture if set, or null if not set.
+ */
public MatParamTexture getTextureParam(String name) {
MatParam param = paramValues.get(name);
if (param instanceof MatParamTexture) {
return null;
}
+ /**
+ * Returns a collection of all parameters set on this material.
+ *
+ * @return a collection of all parameters set on this material.
+ *
+ * @see #setParam(java.lang.String, com.jme3.shader.VarType, java.lang.Object)
+ */
public Collection<MatParam> getParams() {
return paramValues.values();
}
}
/**
- * Pass a parameter to the material shader
+ * Pass a parameter to the material shader.
+ *
* @param name the name of the parameter defined in the material definition (j3md)
- * @param type the type of the parameter @see com.jme3.shaderVarType
- * @param value the value of the param
+ * @param type the type of the parameter {@link VarType}
+ * @param value the value of the parameter
*/
public void setParam(String name, VarType type, Object value) {
name = checkSetParam(type, name);
}
/**
- * Clear a parameter from this material. The param must exist
+ * Clear a parameter from this material. The parameter must exist
* @param name the name of the parameter to clear
*/
public void clearParam(String name) {
sortingId = -1;
}
+ /**
+ * Set a texture parameter.
+ *
+ * @param name The name of the parameter
+ * @param type The variable type {@link VarType}
+ * @param value The texture value of the parameter.
+ *
+ * @throws IllegalArgumentException is value is null
+ */
public void setTextureParam(String name, VarType type, Texture value) {
if (value == null) {
- throw new NullPointerException();
+ throw new IllegalArgumentException();
}
name = checkSetParam(type, name);
}
/**
- * Pass a texture to the material shader
- * @param name the name of the texture defined in the material definition (j3md) (for example Texture for Lighting.j3md)
+ * Pass a texture to the material shader.
+ *
+ * @param name the name of the texture defined in the material definition
+ * (j3md) (for example Texture for Lighting.j3md)
* @param value the Texture object previously loaded by the asset manager
*/
public void setTexture(String name, Texture value) {
}
/**
- * Pass a Matrix4f to the material shader
+ * Pass a Matrix4f to the material shader.
+ *
* @param name the name of the matrix defined in the material definition (j3md)
* @param value the Matrix4f object
*/
}
/**
- * Pass a boolean to the material shader
+ * Pass a boolean to the material shader.
+ *
* @param name the name of the boolean defined in the material definition (j3md)
* @param value the boolean value
*/
}
/**
- * Pass a float to the material shader
+ * Pass a float to the material shader.
+ *
* @param name the name of the float defined in the material definition (j3md)
* @param value the float value
*/
}
/**
- * Pass an int to the material shader
+ * Pass an int to the material shader.
+ *
* @param name the name of the int defined in the material definition (j3md)
* @param value the int value
*/
}
/**
- * Pass a Color to the material shader
+ * Pass a Color to the material shader.
+ *
* @param name the name of the color defined in the material definition (j3md)
* @param value the ColorRGBA value
*/
}
/**
- * Pass a Vector2f to the material shader
+ * Pass a Vector2f to the material shader.
+ *
* @param name the name of the Vector2f defined in the material definition (j3md)
* @param value the Vector2f value
*/
}
/**
- * Pass a Vector3f to the material shader
+ * Pass a Vector3f to the material shader.
+ *
* @param name the name of the Vector3f defined in the material definition (j3md)
* @param value the Vector3f value
*/
}
/**
- * Pass a Vector4f to the material shader
+ * Pass a Vector4f to the material shader.
+ *
* @param name the name of the Vector4f defined in the material definition (j3md)
* @param value the Vector4f value
*/
* // or the direction of the light (for directional lights).<br/>
* // g_LightPosition.w is the inverse radius (1/r) of the light (for attenuation) <br/>
* </p>
- *
- * @param shader
- * @param lightList
*/
protected void updateLightListUniforms(Shader shader, Geometry g, int numLights) {
if (numLights == 0){ // this shader does not do lighting, ignore.
}
}
+ /**
+ * Select the technique to use for rendering this material.
+ * <p>
+ * If <code>name</code> is "Default", then one of the
+ * {@link MaterialDef#getDefaultTechniques() default techniques}
+ * on the material will be selected. Otherwise, the named technique
+ * will be found in the material definition.
+ * <p>
+ * Any candidate technique for selection (either default or named)
+ * must be verified to be compatible with the system, for that, the
+ * <code>renderManager</code> is queried for capabilities.
+ *
+ * @param name The name of the technique to select, pass "Default" to
+ * select one of the default techniques.
+ * @param renderManager The {@link RenderManager render manager}
+ * to query for capabilities.
+ *
+ * @throws IllegalArgumentException If "Default" is passed and no default
+ * techniques are available on the material definition, or if a name
+ * is passed but there's no technique by that name.
+ * @throws UnsupportedOperationException If no candidate technique supports
+ * the system capabilities.
+ */
public void selectTechnique(String name, RenderManager renderManager) {
// check if already created
Technique tech = techniques.get(name);
if (name.equals("Default")) {
List<TechniqueDef> techDefs = def.getDefaultTechniques();
if (techDefs == null || techDefs.isEmpty()) {
- throw new IllegalStateException("No default techniques are available on material '" + def.getName() + "'");
+ throw new IllegalArgumentException("No default techniques are available on material '" + def.getName() + "'");
}
TechniqueDef lastTech = null;
}
/**
- * "Pre-load" the material, including textures and shaders, to the
- * renderer.
+ * Preloads this material for the given render manager.
+ * <p>
+ * Preloading the material can ensure that when the material is first
+ * used for rendering, there won't be any delay since the material has
+ * been already been setup for rendering.
+ *
+ * @param rm The render manager to preload for
*/
public void preload(RenderManager rm) {
autoSelectTechnique(rm);
}
/**
- * Should be called after selectTechnique()
- * @param geom
- * @param r
+ * Called by {@link RenderManager} to render the geometry by
+ * using this material.
+ *
+ * @param geom The geometry to render
+ * @param rm The render manager requesting the rendering
*/
public void render(Geometry geom, RenderManager rm) {
autoSelectTechnique(rm);
updateUniformParam(paramName, type, value, false);\r
}\r
\r
+ /**\r
+ * Returns true if the technique must be reloaded.\r
+ * <p>\r
+ * If a technique needs to reload, then the {@link Material} should\r
+ * call {@link #makeCurrent(com.jme3.asset.AssetManager) } on this\r
+ * technique.\r
+ * \r
+ * @return true if the technique must be reloaded.\r
+ */\r
public boolean isNeedReload() {\r
return needReload;\r
}\r
/**\r
* Prepares the technique for use by loading the shader and setting\r
* the proper defines based on material parameters.\r
+ * \r
+ * @param assetManager The asset manager to use for loading shaders.\r
*/\r
- public void makeCurrent(AssetManager manager) {\r
+ public void makeCurrent(AssetManager assetManager) {\r
// check if reload is needed..\r
if (def.isUsingShaders()) {\r
DefineList newDefines = new DefineList();\r
defines.clear();\r
defines.addFrom(newDefines);\r
// defines changed, recompile needed\r
- loadShader(manager);\r
+ loadShader(assetManager);\r
}\r
}\r
}\r
allDefines.addFrom(def.getShaderPresetDefines());\r
allDefines.addFrom(defines);\r
\r
- ShaderKey key = new ShaderKey(def.getVertName(),\r
- def.getFragName(),\r
+ ShaderKey key = new ShaderKey(def.getVertexShaderName(),\r
+ def.getFragmentShaderName(),\r
allDefines,\r
def.getShaderLanguage());\r
shader = manager.loadShader(key);\r
import com.jme3.export.OutputCapsule;
import com.jme3.export.Savable;
import com.jme3.renderer.Caps;
+import com.jme3.renderer.Renderer;
import com.jme3.shader.DefineList;
import com.jme3.shader.UniformBinding;
import com.jme3.shader.VarType;
import java.util.HashMap;
import java.util.List;
+/**
+ * Describes a technique definition.
+ *
+ * @author Kirill Vainer
+ */
public class TechniqueDef implements Savable {
+ /**
+ * Describes light rendering mode.
+ */
public enum LightMode {
+ /**
+ * Disable light-based rendering
+ */
Disable,
+
+ /**
+ * Enable light rendering by using a single pass.
+ * <p>
+ * An array of light positions and light colors is passed to the shader
+ * containing the world light list for the geometry being rendered.
+ */
SinglePass,
+
+ /**
+ * Enable light rendering by using multi-pass rendering.
+ * <p>
+ * The geometry will be rendered once for each light. Each time the
+ * light position and light color uniforms are updated to contain
+ * the values for the current light. The ambient light color uniform
+ * is only set to the ambient light color on the first pass, future
+ * passes have it set to black.
+ */
MultiPass,
+
+ /**
+ * Enable light rendering by using the
+ * {@link Renderer#setLighting(com.jme3.light.LightList) renderer's setLighting}
+ * method.
+ * <p>
+ * The specific details of rendering the lighting is up to the
+ * renderer implementation.
+ */
FixedPipeline,
}
private HashMap<String, String> defineParams;
private ArrayList<UniformBinding> worldBinds;
+ /**
+ * Creates a new technique definition.
+ * <p>
+ * Used internally by the J3M/J3MD loader.
+ *
+ * @param name The name of the technique, should be set to <code>null</code>
+ * for default techniques.
+ */
public TechniqueDef(String name){
this.name = name == null ? "Default" : name;
}
/**
- * Do not use this constructor.
+ * Serialization only. Do not use.
*/
public TechniqueDef(){
}
- public void write(JmeExporter ex) throws IOException{
- OutputCapsule oc = ex.getCapsule(this);
- oc.write(name, "name", null);
- oc.write(vertName, "vertName", null);
- oc.write(fragName, "fragName", null);
- oc.write(shaderLang, "shaderLang", null);
- oc.write(presetDefines, "presetDefines", null);
- oc.write(lightMode, "lightMode", LightMode.Disable);
- oc.write(shadowMode, "shadowMode", ShadowMode.Disable);
- oc.write(renderState, "renderState", null);
- oc.write(usesShaders, "usesShaders", false);
- // TODO: Finish this when Map<String, String> export is available
-// oc.write(defineParams, "defineParams", null);
- // TODO: Finish this when List<Enum> export is available
-// oc.write(worldBinds, "worldBinds", null);
- }
-
- public void read(JmeImporter im) throws IOException{
- InputCapsule ic = im.getCapsule(this);
- name = ic.readString("name", null);
- vertName = ic.readString("vertName", null);
- fragName = ic.readString("fragName", null);
- shaderLang = ic.readString("shaderLang", null);
- presetDefines = (DefineList) ic.readSavable("presetDefines", null);
- lightMode = ic.readEnum("lightMode", LightMode.class, LightMode.Disable);
- shadowMode = ic.readEnum("shadowMode", ShadowMode.class, ShadowMode.Disable);
- renderState = (RenderState) ic.readSavable("renderState", null);
- usesShaders = ic.readBoolean("usesShaders", false);
- }
-
+ /**
+ * Returns the name of this technique as specified in the J3MD file.
+ * Default techniques have the name "Default".
+ *
+ * @return the name of this technique
+ */
public String getName(){
return name;
}
+ /**
+ * Returns the light mode.
+ * @return the light mode.
+ * @see LightMode
+ */
public LightMode getLightMode() {
return lightMode;
}
+ /**
+ * Set the light mode
+ *
+ * @param lightMode the light mode
+ *
+ * @see LightMode
+ */
public void setLightMode(LightMode lightMode) {
this.lightMode = lightMode;
}
+ /**
+ * Returns the shadow mode.
+ * @return the shadow mode.
+ */
public ShadowMode getShadowMode() {
return shadowMode;
}
+ /**
+ * Set the shadow mode.
+ *
+ * @param shadowMode the shadow mode.
+ *
+ * @see ShadowMode
+ */
public void setShadowMode(ShadowMode shadowMode) {
this.shadowMode = shadowMode;
}
+ /**
+ * Returns the render state that this technique is using
+ * @return the render state that this technique is using
+ * @see #setRenderState(com.jme3.material.RenderState)
+ */
public RenderState getRenderState() {
return renderState;
}
+ /**
+ * Sets the render state that this technique is using.
+ *
+ * @param renderState the render state that this technique is using.
+ *
+ * @see RenderState
+ */
public void setRenderState(RenderState renderState) {
this.renderState = renderState;
}
+ /**
+ * Returns true if this technique uses shaders, false otherwise.
+ *
+ * @return true if this technique uses shaders, false otherwise.
+ *
+ * @see #setShaderFile(java.lang.String, java.lang.String, java.lang.String)
+ */
public boolean isUsingShaders(){
return usesShaders;
}
+ /**
+ * Gets the {@link Caps renderer capabilities} that are required
+ * by this technique.
+ *
+ * @return the required renderer capabilities
+ */
public EnumSet<Caps> getRequiredCaps() {
return requiredCaps;
}
- public void setShaderFile(String vert, String frag, String lang){
- this.vertName = vert;
- this.fragName = frag;
- this.shaderLang = lang;
+ /**
+ * Sets the shaders that this technique definition will use.
+ *
+ * @param vertexShader The name of the vertex shader
+ * @param fragmentShader The name of the fragment shader
+ * @param shaderLanguage The shader language
+ */
+ public void setShaderFile(String vertexShader, String fragmentShader, String shaderLanguage){
+ this.vertName = vertexShader;
+ this.fragName = fragmentShader;
+ this.shaderLang = shaderLanguage;
- Caps langCap = Caps.valueOf(lang);
+ Caps langCap = Caps.valueOf(shaderLanguage);
requiredCaps.add(langCap);
usesShaders = true;
}
- public DefineList getShaderPresetDefines() {
- return presetDefines;
- }
-
+ /**
+ * Returns the define name which the given material parameter influences.
+ *
+ * @param paramName The parameter name to look up
+ * @return The define name
+ *
+ * @see #addShaderParamDefine(java.lang.String, java.lang.String)
+ */
public String getShaderParamDefine(String paramName){
if (defineParams == null)
return null;
return defineParams.get(paramName);
}
+ /**
+ * Adds a define linked to a material parameter.
+ * <p>
+ * Any time the material parameter on the parent material is altered,
+ * the appropriate define on the technique will be modified as well.
+ * See the method
+ * {@link DefineList#set(java.lang.String, com.jme3.shader.VarType, java.lang.Object) }
+ * on the exact details of how the material parameter changes the define.
+ *
+ * @param paramName The name of the material parameter to link to.
+ * @param defineName The name of the define parameter, e.g. USE_LIGHTING
+ */
public void addShaderParamDefine(String paramName, String defineName){
if (defineParams == null)
defineParams = new HashMap<String, String>();
defineParams.put(paramName, defineName);
}
+ /**
+ * Returns the {@link DefineList} for the preset defines.
+ *
+ * @return the {@link DefineList} for the preset defines.
+ *
+ * @see #addShaderPresetDefine(java.lang.String, com.jme3.shader.VarType, java.lang.Object)
+ */
+ public DefineList getShaderPresetDefines() {
+ return presetDefines;
+ }
+
+ /**
+ * Adds a preset define.
+ * <p>
+ * Preset defines do not depend upon any parameters to be activated,
+ * they are always passed to the shader as long as this technique is used.
+ *
+ * @param defineName The name of the define parameter, e.g. USE_LIGHTING
+ * @param type The type of the define. See
+ * {@link DefineList#set(java.lang.String, com.jme3.shader.VarType, java.lang.Object) }
+ * to see why it matters.
+ *
+ * @param value The value of the define
+ */
public void addShaderPresetDefine(String defineName, VarType type, Object value){
if (presetDefines == null)
presetDefines = new DefineList();
presetDefines.set(defineName, type, value);
}
- public String getFragName() {
+ /**
+ * Returns the name of the fragment shader used by the technique, or null
+ * if no fragment shader is specified.
+ *
+ * @return the name of the fragment shader to be used.
+ */
+ public String getFragmentShaderName() {
return fragName;
}
- public String getVertName() {
+
+ /**
+ * Returns the name of the vertex shader used by the technique, or null
+ * if no vertex shader is specified.
+ *
+ * @return the name of the vertex shader to be used.
+ */
+ public String getVertexShaderName() {
return vertName;
}
+ /**
+ * Returns the shader language of the shaders used in this technique.
+ *
+ * @return the shader language of the shaders used in this technique.
+ */
public String getShaderLanguage() {
return shaderLang;
}
+ /**
+ * Adds a new world parameter by the given name.
+ *
+ * @param name The world parameter to add.
+ * @return True if the world parameter name was found and added
+ * to the list of world parameters, false otherwise.
+ */
public boolean addWorldParam(String name) {
if (worldBinds == null){
worldBinds = new ArrayList<UniformBinding>();
}
- for (UniformBinding binding : UniformBinding.values()) {
- if (binding.name().equals(name)) {
- worldBinds.add(binding);
- return true;
- }
+
+ try {
+ worldBinds.add( UniformBinding.valueOf(name) );
+ return true;
+ } catch (IllegalArgumentException ex){
+ return false;
}
- return false;
}
+ /**
+ * Returns a list of world parameters that are used by this
+ * technique definition.
+ *
+ * @return The list of world parameters
+ */
public List<UniformBinding> getWorldBindings() {
return worldBinds;
}
+ public void write(JmeExporter ex) throws IOException{
+ OutputCapsule oc = ex.getCapsule(this);
+ oc.write(name, "name", null);
+ oc.write(vertName, "vertName", null);
+ oc.write(fragName, "fragName", null);
+ oc.write(shaderLang, "shaderLang", null);
+ oc.write(presetDefines, "presetDefines", null);
+ oc.write(lightMode, "lightMode", LightMode.Disable);
+ oc.write(shadowMode, "shadowMode", ShadowMode.Disable);
+ oc.write(renderState, "renderState", null);
+ oc.write(usesShaders, "usesShaders", false);
+ // TODO: Finish this when Map<String, String> export is available
+// oc.write(defineParams, "defineParams", null);
+ // TODO: Finish this when List<Enum> export is available
+// oc.write(worldBinds, "worldBinds", null);
+ }
+
+ public void read(JmeImporter im) throws IOException{
+ InputCapsule ic = im.getCapsule(this);
+ name = ic.readString("name", null);
+ vertName = ic.readString("vertName", null);
+ fragName = ic.readString("fragName", null);
+ shaderLang = ic.readString("shaderLang", null);
+ presetDefines = (DefineList) ic.readSavable("presetDefines", null);
+ lightMode = ic.readEnum("lightMode", LightMode.class, LightMode.Disable);
+ shadowMode = ic.readEnum("shadowMode", ShadowMode.class, ShadowMode.Disable);
+ renderState = (RenderState) ic.readSavable("renderState", null);
+ usesShaders = ic.readBoolean("usesShaders", false);
+ }
+
}
The <code>com.jme3.material</code> package contains classes for manipulating
jMonkeyEngine materials.
-Materials are applied to {@link com.jme3.scene.Geoemtry geometries} in the
+Materials are applied to {@link com.jme3.scene.Geometry geometries} in the
scene.
Each geometry has a single material which is used to render that
geometry.
*\r
* @param vec\r
* vec to multiply against.\r
- * @param store\r
- * a vector to store the result in. created if null is passed.\r
+ * \r
* @return the rotated vector.\r
*/\r
public Vector4f multAcross(Vector4f vec) {\r
* <code>getLimit</code> returns the limit or the ray, aka the length.\r
* If the limit is not infinity, then this ray is a line with length <code>\r
* limit</code>.\r
- * @return\r
+ * \r
+ * @return the limit or the ray, aka the length.\r
*/\r
public float getLimit(){\r
return limit;\r
/**
* returns the curve tension
- * @return
*/
public float getCurveTension() {
return curveTension;
/**
* returns true if the spline cycle
- * @return
*/
public boolean isCycle() {
return cycle;
/**
* return the total lenght of the spline
- * @return
*/
public float getTotalLength() {
return totalLength;
/**
* return the type of the spline
- * @return
*/
public SplineType getType() {
return type;
/**
* returns this spline control points
- * @return
*/
public List<Vector3f> getControlPoints() {
return controlPoints;
/**
* returns a list of float representing the segments lenght
- * @return
*/
public List<Float> getSegmentsLength() {
return segmentsLength;
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\r
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
*/\r
-\r
package com.jme3.math;\r
\r
import com.jme3.export.JmeExporter;\r
private Vector3f pointa = new Vector3f();\r
private Vector3f pointb = new Vector3f();\r
private Vector3f pointc = new Vector3f();\r
- \r
private transient Vector3f center;\r
private transient Vector3f normal;\r
- \r
private float projection;\r
- \r
private int index;\r
- \r
- public Triangle() {} \r
+\r
+ public Triangle() {\r
+ }\r
\r
/**\r
* Constructor instantiates a new <Code>Triangle</code> object with the\r
*/\r
public Vector3f get(int i) {\r
switch (i) {\r
- case 0: return pointa;\r
- case 1: return pointb;\r
- case 2: return pointc;\r
- default: return null;\r
+ case 0:\r
+ return pointa;\r
+ case 1:\r
+ return pointb;\r
+ case 2:\r
+ return pointc;\r
+ default:\r
+ return null;\r
}\r
}\r
\r
- public Vector3f get1(){\r
+ public Vector3f get1() {\r
return pointa;\r
}\r
\r
- public Vector3f get2(){\r
+ public Vector3f get2() {\r
return pointb;\r
}\r
\r
- public Vector3f get3(){\r
+ public Vector3f get3() {\r
return pointc;\r
}\r
- \r
+\r
/**\r
*\r
* <code>set</code> sets one of the triangles points to that specified as\r
*/\r
public void set(int i, Vector3f point) {\r
switch (i) {\r
- case 0: pointa.set(point); break;\r
- case 1: pointb.set(point); break;\r
- case 2: pointc.set(point); break;\r
+ case 0:\r
+ pointa.set(point);\r
+ break;\r
+ case 1:\r
+ pointb.set(point);\r
+ break;\r
+ case 2:\r
+ pointc.set(point);\r
+ break;\r
}\r
}\r
\r
* <code>set</code> sets one of the triangles points to that specified as\r
* a parameter.\r
* @param i the index to place the point.\r
- * @param point the point to set.\r
*/\r
public void set(int i, float x, float y, float z) {\r
switch (i) {\r
- case 0: pointa.set(x,y,z); break;\r
- case 1: pointb.set(x,y,z); break;\r
- case 2: pointc.set(x,y,z); break;\r
+ case 0:\r
+ pointa.set(x, y, z);\r
+ break;\r
+ case 1:\r
+ pointb.set(x, y, z);\r
+ break;\r
+ case 2:\r
+ pointc.set(x, y, z);\r
+ break;\r
}\r
}\r
\r
- public void set1(Vector3f v){\r
+ public void set1(Vector3f v) {\r
pointa.set(v);\r
}\r
\r
- public void set2(Vector3f v){\r
+ public void set2(Vector3f v) {\r
pointb.set(v);\r
}\r
\r
- public void set3(Vector3f v){\r
+ public void set3(Vector3f v) {\r
pointc.set(v);\r
}\r
\r
- public void set(Vector3f v1, Vector3f v2, Vector3f v3){\r
+ public void set(Vector3f v1, Vector3f v2, Vector3f v3) {\r
pointa.set(v1);\r
pointb.set(v2);\r
pointc.set(v3);\r
}\r
- \r
+\r
/**\r
* calculateCenter finds the average point of the triangle. \r
*\r
*/\r
public void calculateCenter() {\r
- if (center == null)\r
+ if (center == null) {\r
center = new Vector3f(pointa);\r
- else center.set(pointa);\r
+ } else {\r
+ center.set(pointa);\r
+ }\r
center.addLocal(pointb).addLocal(pointc).multLocal(FastMath.ONE_THIRD);\r
}\r
- \r
+\r
/**\r
* calculateCenter finds the average point of the triangle. \r
*\r
*/\r
public void calculateNormal() {\r
- if (normal == null)\r
+ if (normal == null) {\r
normal = new Vector3f(pointb);\r
- else normal.set(pointb);\r
- normal.subtractLocal(pointa).crossLocal(pointc.x-pointa.x, pointc.y-pointa.y, pointc.z-pointa.z);\r
+ } else {\r
+ normal.set(pointb);\r
+ }\r
+ normal.subtractLocal(pointa).crossLocal(pointc.x - pointa.x, pointc.y - pointa.y, pointc.z - pointa.z);\r
normal.normalizeLocal();\r
}\r
- \r
+\r
/**\r
* obtains the center point of this triangle (average of the three triangles)\r
* @return the center point.\r
*/\r
public Vector3f getCenter() {\r
- if(center == null) {\r
- calculateCenter();\r
- }\r
+ if (center == null) {\r
+ calculateCenter();\r
+ }\r
return center;\r
}\r
- \r
+\r
/**\r
* sets the center point of this triangle (average of the three triangles)\r
* @param center the center point.\r
public void setCenter(Vector3f center) {\r
this.center = center;\r
}\r
- \r
+\r
/**\r
* obtains the unit length normal vector of this triangle, if set or\r
* calculated\r
* @return the normal vector\r
*/\r
public Vector3f getNormal() {\r
- if(normal == null) {\r
- calculateNormal();\r
- }\r
+ if (normal == null) {\r
+ calculateNormal();\r
+ }\r
return normal;\r
}\r
- \r
+\r
/**\r
* sets the normal vector of this triangle (to conform, must be unit length)\r
* @param normal the normal vector.\r
public void setNormal(Vector3f normal) {\r
this.normal = normal;\r
}\r
- \r
+\r
/**\r
* obtains the projection of the vertices relative to the line origin.\r
* @return the projection of the triangle.\r
public float getProjection() {\r
return this.projection;\r
}\r
- \r
+\r
/**\r
* sets the projection of the vertices relative to the line origin.\r
* @param projection the projection of the triangle.\r
public void setProjection(float projection) {\r
this.projection = projection;\r
}\r
- \r
+\r
/**\r
* obtains an index that this triangle represents if it is contained in a OBBTree.\r
* @return the index in an OBBtree\r
public int getIndex() {\r
return index;\r
}\r
- \r
+\r
/**\r
* sets an index that this triangle represents if it is contained in a OBBTree.\r
* @param index the index in an OBBtree\r
this.index = index;\r
}\r
\r
- public static Vector3f computeTriangleNormal(Vector3f v1, Vector3f v2, Vector3f v3, Vector3f store){\r
- if (store == null)\r
+ public static Vector3f computeTriangleNormal(Vector3f v1, Vector3f v2, Vector3f v3, Vector3f store) {\r
+ if (store == null) {\r
store = new Vector3f(v2);\r
- else\r
+ } else {\r
store.set(v2);\r
+ }\r
\r
- store.subtractLocal(v1).crossLocal(v3.x-v1.x, v3.y-v1.y, v3.z-v1.z);\r
+ store.subtractLocal(v1).crossLocal(v3.x - v1.x, v3.y - v1.y, v3.z - v1.z);\r
return store.normalizeLocal();\r
}\r
\r
}\r
\r
public void read(JmeImporter e) throws IOException {\r
- pointa = (Vector3f)e.getCapsule(this).readSavable("pointa", Vector3f.ZERO.clone());\r
- pointb = (Vector3f)e.getCapsule(this).readSavable("pointb", Vector3f.ZERO.clone());\r
- pointc = (Vector3f)e.getCapsule(this).readSavable("pointc", Vector3f.ZERO.clone());\r
+ pointa = (Vector3f) e.getCapsule(this).readSavable("pointa", Vector3f.ZERO.clone());\r
+ pointb = (Vector3f) e.getCapsule(this).readSavable("pointb", Vector3f.ZERO.clone());\r
+ pointc = (Vector3f) e.getCapsule(this).readSavable("pointc", Vector3f.ZERO.clone());\r
}\r
- \r
+\r
@Override\r
public Triangle clone() {\r
try {\r
* <code>distanceSquared</code> calculates the distance squared between
* this vector and vector v.
*
- * @param v the second vector to determine the distance squared.
+ * @param otherX The X coordinate of the v vector
+ * @param otherY The Y coordinate of the v vector
* @return the distance squared between the two vectors.
*/
public float distanceSquared(float otherX, float otherY) {
* the y value to subtract.
* @param subtractZ
* the z value to subtract.
- * @param subtract@
+ * @param subtractW
* the w value to subtract.
* @return this
*/
public abstract void cleanUpFilter(Renderer r);
/**
- * this method should return the material used for this filter.
- * this method is called every frames
- * @return
+ * Returns the material used for this filter.
+ * this method is called every frame.
+ *
+ * @return the material used for this filter.
*/
public abstract Material getMaterial();
}
/**
- * Override this method if you want to load extra properties when the filter is loaded else only basic properties of the filter will be loaded
- * This method should always begin by super.read(ex);
- * @param ex
- * @throws IOException
+ * Override this method if you want to load extra properties when the filter
+ * is loaded else only basic properties of the filter will be loaded
+ * This method should always begin by super.read(im);
*/
public void read(JmeImporter im) throws IOException {
InputCapsule ic = im.getCapsule(this);
name = ic.readString("name", "");
enabled = ic.readBoolean("enabled", true);
-
}
public String getName() {
}
/**
- * Override this method and retrun true if your Filter need the depth texture
- * @return
+ * Override this method and return true if your Filter need the depth texture
+ *
+ * @return true if your Filter need the depth texture
*/
public boolean isRequiresDepthTexture() {
return false;
/**
* Override this method and return false if your Filter does not need the scene texture
- * @return
+ *
+ * @return false if your Filter does not need the scene texture
*/
public boolean isRequiresSceneTexture() {
return true;
* <code>setLocation</code> sets the position of the camera.
*
* @param location the position of the camera.
- * @see Camera#setLocation(com.jme.math.Vector3f)
*/
public void setLocation(Vector3f location) {
this.location.set(location);
* @param left the left axis of the camera.
* @param up the up axis of the camera.
* @param direction the direction the camera is facing.
- * @see Camera#setAxes(com.jme.math.Vector3f,com.jme.math.Vector3f,com.jme.math.Vector3f)
+ *
+ * @see Camera#setAxes(com.jme3.math.Quaternion)
*/
public void setAxes(Vector3f left, Vector3f up, Vector3f direction) {
this.rotation.fromAxes(left, up, direction);
/**
* This should create a deep clone. For a shallow clone, use
* createDestructableClone().
- *
- * @return
*/
+ @Override
protected GLObject clone(){
try{
GLObject obj = (GLObject) super.clone();
/**
* Creates a new viewport, to display the given camera's content.
* The view will be processed before the primary viewport.
- * @param viewName
- * @param cam
- * @return
*/
public ViewPort createPreView(String viewName, Camera cam) {
ViewPort vp = new ViewPort(viewName, cam);
/**
* Render scene graph
- * @param s
- * @param r
- * @param cam
*/
public void renderScene(Spatial scene, ViewPort vp) {
if (scene.getParent() == null) {
public void onFrame();
/**
- * @param transform The world transform to use. This changes
+ * @param worldMatrix The world transform to use. This changes
* the world matrix given in the shader.
*/
public void setWorldMatrix(Matrix4f worldMatrix);
/**
* Deletes a texture from the GPU.
- * @param tex
*/
public void deleteImage(Image image);
}
/**
- * Adds a spatial to the list. List size is doubled if there is no room.
+ * Adds a geometry to the list. List size is doubled if there is no room.
*
- * @param s
- * The spatial to add.
+ * @param g
+ * The geometry to add.
*/
public void add(Geometry g) {
if (size == geometries.length) {
* this geometry. The location of the geometry is based on the location of
* all this node's parents.
*
- * @see com.jme.scene.Spatial#updateWorldBound()
+ * @see Spatial#updateWorldBound()
*/
@Override
protected void updateWorldBound() {
* Exception: if the mesh is marked as being a software
* animated mesh, (bind pose is set) then the positions
* and normals are deep copied.
- * @return
*/
@Override
public Geometry clone(boolean cloneMaterial){
* Exception: if the mesh is marked as being a software
* animated mesh, (bind pose is set) then the positions
* and normals are deep copied.
- * @return
*/
+ @Override
public Geometry clone(){
return clone(true);
}
* Creates a deep clone of the geometry,
* this creates an identical copy of the mesh
* with the vertexbuffer data duplicated.
- * @return
*/
@Override
public Spatial deepClone(){
* @return Non-null, but possibly 0-element, list of matching Spatials (also Instances extending Spatials).
*
* @see java.util.regex.Pattern
- * @see Spatial#matches(Class<? extends Spatial>, String)
+ * @see Spatial#matches(java.lang.Class, java.lang.String)
*/
@SuppressWarnings("unchecked")
public <T extends Spatial>List<T> descendantMatches(
/**
* Convenience wrapper.
*
- * @see #descendantMatches(Class<? extends Spatial>, String)
+ * @see #descendantMatches(java.lang.Class, java.lang.String)
*/
public <T extends Spatial>List<T> descendantMatches(
Class<T> spatialSubclass) {
/**
* Convenience wrapper.
*
- * @see #descendantMatches(Class<? extends Spatial>, String)
+ * @see #descendantMatches(java.lang.Class, java.lang.String)
*/
public <T extends Spatial>List<T> descendantMatches(String nameRegex) {
return descendantMatches(null, nameRegex);
/**
* <code>setLocalScale</code> sets the local scale of this node.
- *
- * @param localScale
- * the new local scale
*/
public void setLocalScale(float x, float y, float z) {
localTransform.setScale(x, y, z);
* of the parameters. The buffer will be of the type specified by
* {@link Format format} and would be able to contain the given number
* of elements with the given number of components in each element.
- *
- * @param format
- * @param components
- * @param numElements
- * @return
*/
public static Buffer createBuffer(Format format, int components, int numElements){
if (components < 1 || components > 4)
* Creates a clone of the Control, the given Spatial is the cloned
* version of the spatial to which this control is attached to.
* @param spatial
- * @return
+ * @return A clone of this control for the spatial
*/
public Control cloneForSpatial(Spatial spatial);
}
/**
- * @param camera The Camera to be synced.
+ * @param light The light to be synced.
*/
public LightControl(Light light) {
this.light = light;
}
/**
- * @param camera The Camera to be synced.
+ * @param light The light to be synced.
*/
public LightControl(Light light, ControlDirection controlDir) {
this.light = light;
/**\r
* Returns true if this program and all it's shaders have been compiled,\r
* linked and validated successfuly.\r
- * @return\r
*/\r
public boolean isUsable(){\r
return usable;\r
/**\r
* Called by the object manager to reset all object IDs. This causes\r
* the shader to be reuploaded to the GPU incase the display was restarted.\r
- * @param r\r
*/\r
@Override\r
public void resetObject() {\r
import com.jme3.input.MouseInput;
import com.jme3.input.TouchInput;
import com.jme3.renderer.Renderer;
+import com.jme3.system.JmeCanvasContext;
/**
* Represents a rendering context within the engine.