From 95cdde7f53d521655f18d56ed7e2d21e25d986b3 Mon Sep 17 00:00:00 2001 From: "sha..rd" Date: Sun, 12 Jun 2011 04:19:16 +0000 Subject: [PATCH] * Javadocs for com.jme3.material * Formatted the blender loader files according to NetBeans * Removed any "I" prefixes on interfaces * Small javadoc fixes git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@7592 75d07b2b-3a1a-0410-a2c5-0572b91ccdca --- .../blender/com/jme3/asset/BlenderKey.java | 1456 ++++---- .../scene/plugins/blender/BlenderLoader.java | 227 +- .../plugins/blender/BlenderModelLoader.java | 163 +- .../plugins/blender/data/DnaBlockData.java | 328 +- .../scene/plugins/blender/data/Field.java | 585 ++-- .../plugins/blender/data/FileBlockHeader.java | 311 +- .../scene/plugins/blender/data/Structure.java | 493 +-- .../exception/BlenderFileException.java | 64 +- .../blender/helpers/ArmatureHelper.java | 152 +- .../plugins/blender/helpers/CameraHelper.java | 73 +- .../blender/helpers/ConstraintHelper.java | 47 +- .../plugins/blender/helpers/CurvesHelper.java | 19 +- .../plugins/blender/helpers/IpoHelper.java | 19 +- .../plugins/blender/helpers/LightHelper.java | 19 +- .../blender/helpers/MaterialHelper.java | 19 +- .../plugins/blender/helpers/MeshHelper.java | 19 +- .../blender/helpers/ModifierHelper.java | 19 +- .../plugins/blender/helpers/NoiseHelper.java | 19 +- .../plugins/blender/helpers/ObjectHelper.java | 19 +- .../blender/helpers/ParticlesHelper.java | 19 +- .../blender/helpers/TextureHelper.java | 72 +- .../blender/helpers/v249/ArmatureHelper.java | 585 ++-- .../blender/helpers/v249/CameraHelper.java | 81 +- .../helpers/v249/ConstraintHelper.java | 1423 ++++---- .../blender/helpers/v249/CurvesHelper.java | 1096 +++--- .../blender/helpers/v249/IpoHelper.java | 170 +- .../blender/helpers/v249/LightHelper.java | 93 +- .../blender/helpers/v249/MaterialHelper.java | 1278 +++---- .../blender/helpers/v249/MeshHelper.java | 1059 +++--- .../blender/helpers/v249/ModifierHelper.java | 923 ++--- .../blender/helpers/v249/NoiseHelper.java | 2973 +++++++++-------- .../structures/AbstractInfluenceFunction.java | 394 ++- .../blender/structures/BezierCurve.java | 230 +- .../blender/structures/Constraint.java | 268 +- .../blender/structures/ConstraintType.java | 250 +- .../scene/plugins/blender/structures/Ipo.java | 312 +- .../plugins/blender/structures/Modifier.java | 87 +- .../blender/utils/AbstractBlenderHelper.java | 112 +- ...erConverter.java => BlenderConverter.java} | 99 +- .../blender/utils/BlenderInputStream.java | 618 ++-- .../plugins/blender/utils/DataRepository.java | 686 ++-- .../plugins/blender/utils/DynamicArray.java | 213 +- .../plugins/blender/utils/JmeConverter.java | 200 +- .../scene/plugins/blender/utils/Pointer.java | 241 +- engine/src/core/com/jme3/animation/Bone.java | 4 +- .../core/com/jme3/animation/CompactArray.java | 2 - engine/src/core/com/jme3/app/package.html | 70 +- .../com/jme3/app/state/AppStateManager.java | 1 - .../src/core/com/jme3/asset/AssetLocator.java | 2 +- .../src/core/com/jme3/asset/AssetManager.java | 14 +- .../src/core/com/jme3/asset/TextureKey.java | 2 +- .../core/com/jme3/audio/AudioRenderer.java | 2 +- .../core/com/jme3/bounding/BoundingBox.java | 25 +- .../com/jme3/bounding/BoundingSphere.java | 8 +- .../input/controls/MouseButtonTrigger.java | 2 + engine/src/core/com/jme3/light/Light.java | 3 +- .../src/core/com/jme3/material/Material.java | 136 +- .../src/core/com/jme3/material/Technique.java | 19 +- .../core/com/jme3/material/TechniqueDef.java | 274 +- .../src/core/com/jme3/material/package.html | 2 +- engine/src/core/com/jme3/math/Matrix4f.java | 3 +- engine/src/core/com/jme3/math/Ray.java | 3 +- engine/src/core/com/jme3/math/Spline.java | 6 - engine/src/core/com/jme3/math/Triangle.java | 125 +- engine/src/core/com/jme3/math/Vector2f.java | 3 +- engine/src/core/com/jme3/math/Vector4f.java | 2 +- engine/src/core/com/jme3/post/Filter.java | 23 +- engine/src/core/com/jme3/renderer/Camera.java | 4 +- .../src/core/com/jme3/renderer/GLObject.java | 3 +- .../core/com/jme3/renderer/RenderManager.java | 6 - .../src/core/com/jme3/renderer/Renderer.java | 3 +- .../com/jme3/renderer/queue/GeometryList.java | 6 +- engine/src/core/com/jme3/scene/Geometry.java | 6 +- engine/src/core/com/jme3/scene/Node.java | 6 +- engine/src/core/com/jme3/scene/Spatial.java | 3 - .../src/core/com/jme3/scene/VertexBuffer.java | 5 - .../core/com/jme3/scene/control/Control.java | 2 +- .../com/jme3/scene/control/LightControl.java | 4 +- engine/src/core/com/jme3/shader/Shader.java | 2 - .../src/core/com/jme3/system/JmeContext.java | 1 + 80 files changed, 9331 insertions(+), 8984 deletions(-) rename engine/src/blender/com/jme3/scene/plugins/blender/utils/{IBlenderConverter.java => BlenderConverter.java} (53%) diff --git a/engine/src/blender/com/jme3/asset/BlenderKey.java b/engine/src/blender/com/jme3/asset/BlenderKey.java index 7eddfd137..1af312471 100644 --- a/engine/src/blender/com/jme3/asset/BlenderKey.java +++ b/engine/src/blender/com/jme3/asset/BlenderKey.java @@ -64,728 +64,736 @@ import com.jme3.texture.Texture; * @author Marcin Roguski (Kaelthas) */ public class BlenderKey extends ModelKey { - protected static final int DEFAULT_FPS = 25; - - /** - * Animation definitions. The key is the object name that owns the animation. The value is a map between animation - * name and its start and stop frames. Blender stores a pointer for animation within object. Therefore one object - * can only have one animation at the time. We want to be able to switch between animations for one object so we - * need to map the object name to animation names the object will use. - */ - protected Map> animations; - /** - * FramesPerSecond parameter describe how many frames there are in each second. It allows to calculate the time - * between the frames. - */ - protected int fps = DEFAULT_FPS; - /** Width of generated textures (in pixels). Blender uses 140x140 by default. */ - protected int generatedTextureWidth = 140; - /** Height of generated textures (in pixels). Blender uses 140x140 by default. */ - protected int generatedTextureHeight = 140; - /** - * This variable is a bitwise flag of FeatureToLoad interface values; By default everything is being loaded. - */ - protected int featuresToLoad = FeaturesToLoad.ALL; - /** The root path for all the assets. */ - protected String assetRootPath; - /** This variable indicate if Y axis is UP axis. If not then Z is up. By default set to true. */ - protected boolean fixUpAxis = true; - /** - * The name of world settings that the importer will use. If not set or specified name does not occur in the file - * then the first world settings in the file will be used. - */ - protected String usedWorld; - /** - * User's default material that is set fo objects that have no material definition in blender. The default value is - * null. If the value is null the importer will use its own default material (gray color - like in blender). - */ - protected Material defaultMaterial; - /** Face cull mode. By default it is disabled. */ - protected FaceCullMode faceCullMode = FaceCullMode.Off; - /** - * Variable describes which layers will be loaded. N-th bit set means N-th layer will be loaded. - * If set to -1 then the current layer will be loaded. - */ - protected int layersToLoad = -1; - - /** - * Constructor used by serialization mechanisms. - */ - public BlenderKey() {} - - /** - * Constructor. Creates a key for the given file name. - * @param name - * the name (path) of a file - */ - public BlenderKey(String name) { - super(name); - } - - /** - * This method adds an animation definition. If a definition already eixists in the key then it is replaced. - * @param objectName - * the name of animation's owner - * @param name - * the name of the animation - * @param start - * the start frame of the animation - * @param stop - * the stop frame of the animation - */ - public synchronized void addAnimation(String objectName, String name, int start, int stop) { - if(objectName == null) { - throw new IllegalArgumentException("Object name cannot be null!"); - } - if(name == null) { - throw new IllegalArgumentException("Animation name cannot be null!"); - } - if(start > stop) { - throw new IllegalArgumentException("Start frame cannot be greater than stop frame!"); - } - if(animations == null) { - animations = new HashMap>(); - animations.put(objectName, new HashMap()); - } - Map objectAnimations = animations.get(objectName); - if(objectAnimations == null) { - objectAnimations = new HashMap(); - animations.put(objectName, objectAnimations); - } - objectAnimations.put(name, new int[] {start, stop}); - } - - /** - * This method returns the animation frames boundaries. - * @param objectName - * the name of animation's owner - * @param name - * animation name - * @return animation frame boundaries in a table [start, stop] or null if animation of the given name does not - * exists - */ - public int[] getAnimationFrames(String objectName, String name) { - Map objectAnimations = animations == null ? null : animations.get(objectName); - int[] frames = objectAnimations == null ? null : objectAnimations.get(name); - return frames == null ? null : frames.clone(); - } - - /** - * This method returns the animation names for the given object name. - * @param objectName - * the name of the object - * @return an array of animations for this object - */ - public Set getAnimationNames(String objectName) { - Map objectAnimations = animations == null ? null : animations.get(objectName); - return objectAnimations == null ? null : objectAnimations.keySet(); - } - - /** - * This method returns the animations map. - * @return the animations map - */ - public Map> getAnimations() { - return animations; - } - - /** - * This method returns frames per second amount. The default value is BlenderKey.DEFAULT_FPS = 25. - * @return the frames per second amount - */ - public int getFps() { - return fps; - } - - /** - * This method sets frames per second amount. - * @param fps - * the frames per second amount - */ - public void setFps(int fps) { - this.fps = fps; - } - - /** - * This method sets the width of generated texture (in pixels). By default the value is 140 px. - * @param generatedTextureWidth - * the width of generated texture - */ - public void setGeneratedTextureWidth(int generatedTextureWidth) { - this.generatedTextureWidth = generatedTextureWidth; - } - - /** - * This method returns the width of generated texture (in pixels). By default the value is 140 px. - * @return the width of generated texture - */ - public int getGeneratedTextureWidth() { - return generatedTextureWidth; - } - - /** - * This method sets the height of generated texture (in pixels). By default the value is 140 px. - * @param generatedTextureHeight - * the height of generated texture - */ - public void setGeneratedTextureHeight(int generatedTextureHeight) { - this.generatedTextureHeight = generatedTextureHeight; - } - - /** - * This method returns the height of generated texture (in pixels). By default the value is 140 px. - * @return the height of generated texture - */ - public int getGeneratedTextureHeight() { - return generatedTextureHeight; - } - - /** - * This method returns the face cull mode. - * @return the face cull mode - */ - public FaceCullMode getFaceCullMode() { - return faceCullMode; - } - - /** - * This method sets the face cull mode. - * @param faceCullMode - * the face cull mode - */ - public void setFaceCullMode(FaceCullMode faceCullMode) { - this.faceCullMode = faceCullMode; - } - - /** - * This method sets layers to be loaded. - * @param layersToLoad layers to be loaded - */ - public void setLayersToLoad(int layersToLoad) { - this.layersToLoad = layersToLoad; - } - - /** - * This method returns layers to be loaded. - * @return layers to be loaded - */ - public int getLayersToLoad() { - return layersToLoad; - } - - /** - * This method sets the asset root path. - * @param assetRootPath - * the assets root path - */ - public void setAssetRootPath(String assetRootPath) { - this.assetRootPath = assetRootPath; - } - - /** - * This method returns the asset root path. - * @return the asset root path - */ - public String getAssetRootPath() { - return assetRootPath; - } - - /** - * This method adds features to be loaded. - * @param featuresToLoad - * bitwise flag of FeaturesToLoad interface values - */ - public void includeInLoading(int featuresToLoad) { - this.featuresToLoad |= featuresToLoad; - } - - /** - * This method removes features from being loaded. - * @param featuresToLoad - * bitwise flag of FeaturesToLoad interface values - */ - public void excludeFromLoading(int featuresNotToLoad) { - this.featuresToLoad &= ~featuresNotToLoad; - } - - /** - * This method returns bitwise value of FeaturesToLoad interface value. It describes features that will be loaded by - * the blender file loader. - * @return features that will be loaded by the blender file loader - */ - public int getFeaturesToLoad() { - return featuresToLoad; - } - - /** - * This method creates an object where loading results will be stores. Only those features will be allowed to store - * that were specified by features-to-load flag. - * @return an object to store loading results - */ - public LoadingResults prepareLoadingResults() { - return new LoadingResults(featuresToLoad); - } - - /** - * This method sets the fix up axis state. If set to true then Y is up axis. Otherwise the up i Z axis. By default Y - * is up axis. - * @param fixUpAxis - * the up axis state variable - */ - public void setFixUpAxis(boolean fixUpAxis) { - this.fixUpAxis = fixUpAxis; - } - - /** - * This method returns the fix up axis state. If set to true then Y is up axis. Otherwise the up i Z axis. By - * default Y is up axis. - * @return the up axis state variable - */ - public boolean isFixUpAxis() { - return fixUpAxis; - } - - /** - * This mehtod sets the name of the WORLD data block taht should be used during file loading. By default the name is - * not set. If no name is set or the given name does not occur in the file - the first WORLD data block will be used - * during loading (assumin any exists in the file). - * @param usedWorld - * the name of the WORLD block used during loading - */ - public void setUsedWorld(String usedWorld) { - this.usedWorld = usedWorld; - } - - /** - * This mehtod returns the name of the WORLD data block taht should be used during file loading. - * @return the name of the WORLD block used during loading - */ - public String getUsedWorld() { - return usedWorld; - } - - /** - * This method sets the default material for objects. - * @param defaultMaterial - * the default material - */ - public void setDefaultMaterial(Material defaultMaterial) { - this.defaultMaterial = defaultMaterial; - } - - /** - * This method returns the default material. - * @return the default material - */ - public Material getDefaultMaterial() { - return defaultMaterial; - } - - @Override - public void write(JmeExporter e) throws IOException { - super.write(e); - OutputCapsule oc = e.getCapsule(this); - //saving animations - oc.write(animations == null ? 0 : animations.size(), "anim-size", 0); - if(animations != null) { - int objectCounter = 0; - for(Entry> animEntry : animations.entrySet()) { - oc.write(animEntry.getKey(), "animated-object-" + objectCounter, null); - int animsAmount = animEntry.getValue().size(); - oc.write(animsAmount, "anims-amount-" + objectCounter, 0); - for(Entry animsEntry : animEntry.getValue().entrySet()) { - oc.write(animsEntry.getKey(), "anim-name-" + objectCounter, null); - oc.write(animsEntry.getValue(), "anim-frames-" + objectCounter, null); - } - ++objectCounter; - } - } - //saving the rest of the data - oc.write(fps, "fps", DEFAULT_FPS); - oc.write(featuresToLoad, "features-to-load", FeaturesToLoad.ALL); - oc.write(assetRootPath, "asset-root-path", null); - oc.write(fixUpAxis, "fix-up-axis", true); - oc.write(usedWorld, "used-world", null); - oc.write(defaultMaterial, "default-material", null); - oc.write(faceCullMode, "face-cull-mode", FaceCullMode.Off); - oc.write(layersToLoad, "layers-to-load", -1); - } - - @Override - public void read(JmeImporter e) throws IOException { - super.read(e); - InputCapsule ic = e.getCapsule(this); - //reading animations - int animSize = ic.readInt("anim-size", 0); - if(animSize > 0) { - if(animations == null) { - animations = new HashMap>(animSize); - } else { - animations.clear(); - } - for(int i = 0; i < animSize; ++i) { - String objectName = ic.readString("animated-object-" + i, null); - int animationsAmount = ic.readInt("anims-amount-" + i, 0); - Map objectAnimations = new HashMap(animationsAmount); - for(int j = 0; j < animationsAmount; ++j) { - String animName = ic.readString("anim-name-" + i, null); - int[] animFrames = ic.readIntArray("anim-frames-" + i, null); - objectAnimations.put(animName, animFrames); - } - animations.put(objectName, objectAnimations); - } - } - - //reading the rest of the data - fps = ic.readInt("fps", DEFAULT_FPS); - featuresToLoad = ic.readInt("features-to-load", FeaturesToLoad.ALL); - assetRootPath = ic.readString("asset-root-path", null); - fixUpAxis = ic.readBoolean("fix-up-axis", true); - usedWorld = ic.readString("used-world", null); - defaultMaterial = (Material)ic.readSavable("default-material", null); - faceCullMode = ic.readEnum("face-cull-mode", FaceCullMode.class, FaceCullMode.Off); - layersToLoad = ic.readInt("layers-to=load", -1); - } - - @Override - public int hashCode() { - final int prime = 31; - int result = super.hashCode(); - result = prime * result + (animations == null ? 0 : animations.hashCode()); - result = prime * result + (assetRootPath == null ? 0 : assetRootPath.hashCode()); - result = prime * result + (defaultMaterial == null ? 0 : defaultMaterial.hashCode()); - result = prime * result + (faceCullMode == null ? 0 : faceCullMode.hashCode()); - result = prime * result + featuresToLoad; - result = prime * result + (fixUpAxis ? 1231 : 1237); - result = prime * result + fps; - result = prime * result + generatedTextureHeight; - result = prime * result + generatedTextureWidth; - result = prime * result + layersToLoad; - result = prime * result + (usedWorld == null ? 0 : usedWorld.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (!super.equals(obj)) { - return false; - } - if (this.getClass() != obj.getClass()) { - return false; - } - BlenderKey other = (BlenderKey) obj; - if (animations == null) { - if (other.animations != null) { - return false; - } - } else if (!animations.equals(other.animations)) { - return false; - } - if (assetRootPath == null) { - if (other.assetRootPath != null) { - return false; - } - } else if (!assetRootPath.equals(other.assetRootPath)) { - return false; - } - if (defaultMaterial == null) { - if (other.defaultMaterial != null) { - return false; - } - } else if (!defaultMaterial.equals(other.defaultMaterial)) { - return false; - } - if (faceCullMode != other.faceCullMode) { - return false; - } - if (featuresToLoad != other.featuresToLoad) { - return false; - } - if (fixUpAxis != other.fixUpAxis) { - return false; - } - if (fps != other.fps) { - return false; - } - if (generatedTextureHeight != other.generatedTextureHeight) { - return false; - } - if (generatedTextureWidth != other.generatedTextureWidth) { - return false; - } - if (layersToLoad != other.layersToLoad) { - return false; - } - if (usedWorld == null) { - if (other.usedWorld != null) { - return false; - } - } else if (!usedWorld.equals(other.usedWorld)) { - return false; - } - return true; - } - - /** - * This interface describes the features of the scene that are to be loaded. - * @author Marcin Roguski (Kaelthas) - */ - public static interface FeaturesToLoad { - int SCENES = 0x0000FFFF; - int OBJECTS = 0x0000000B; - int ANIMATIONS = 0x00000004; - int MATERIALS = 0x00000003; - int TEXTURES = 0x00000001; - int CAMERAS = 0x00000020; - int LIGHTS = 0x00000010; - int ALL = 0xFFFFFFFF; - } - - /** - * This class holds the loading results according to the given loading flag. - * @author Marcin Roguski (Kaelthas) - */ - public static class LoadingResults extends Spatial { - /** Bitwise mask of features that are to be loaded. */ - private final int featuresToLoad; - /** The scenes from the file. */ - private List scenes; - /** Objects from all scenes. */ - private List objects; - /** Materials from all objects. */ - private List materials; - /** Textures from all objects. */ - private List textures; - /** Animations of all objects. */ - private List animations; - /** All cameras from the file. */ - private List cameras; - /** All lights from the file. */ - private List lights; - - /** - * Private constructor prevents users to create an instance of this class from outside the - * @param featuresToLoad - * bitwise mask of features that are to be loaded - * @see FeaturesToLoad FeaturesToLoad - */ - private LoadingResults(int featuresToLoad) { - this.featuresToLoad = featuresToLoad; - if((featuresToLoad & FeaturesToLoad.SCENES) != 0) { - scenes = new ArrayList(); - } - if((featuresToLoad & FeaturesToLoad.OBJECTS) != 0) { - objects = new ArrayList(); - if((featuresToLoad & FeaturesToLoad.MATERIALS) != 0) { - materials = new ArrayList(); - if((featuresToLoad & FeaturesToLoad.TEXTURES) != 0) { - textures = new ArrayList(); - } - } - if((featuresToLoad & FeaturesToLoad.ANIMATIONS) != 0) { - animations = new ArrayList(); - } - } - if((featuresToLoad & FeaturesToLoad.CAMERAS) != 0) { - cameras = new ArrayList(); - } - if((featuresToLoad & FeaturesToLoad.LIGHTS) != 0) { - lights = new ArrayList(); - } - } - - /** - * This method returns a bitwise flag describing what features of the blend file will be included in the result. - * @return bitwise mask of features that are to be loaded - * @see FeaturesToLoad FeaturesToLoad - */ - public int getLoadedFeatures() { - return featuresToLoad; - } - - /** - * This method adds a scene to the result set. - * @param scene - * scene to be added to the result set - */ - public void addScene(Node scene) { - if(scenes != null) { - scenes.add(scene); - } - } - - /** - * This method adds an object to the result set. - * @param object - * object to be added to the result set - */ - public void addObject(Node object) { - if(objects != null) { - objects.add(object); - } - } - - /** - * This method adds a material to the result set. - * @param material - * material to be added to the result set - */ - public void addMaterial(Material material) { - if(materials != null) { - materials.add(material); - } - } - - /** - * This method adds a texture to the result set. - * @param texture - * texture to be added to the result set - */ - public void addTexture(Texture texture) { - if(textures != null) { - textures.add(texture); - } - } - - /** - * This method adds a camera to the result set. - * @param camera - * camera to be added to the result set - */ - public void addCamera(Camera camera) { - if(cameras != null) { - cameras.add(camera); - } - } - - /** - * This method adds a light to the result set. - * @param light - * light to be added to the result set - */ - @Override - public void addLight(Light light) { - if(lights != null) { - lights.add(light); - } - } - - /** - * This method returns all loaded scenes. - * @return all loaded scenes - */ - public List getScenes() { - return scenes; - } - - /** - * This method returns all loaded objects. - * @return all loaded objects - */ - public List getObjects() { - return objects; - } - - /** - * This method returns all loaded materials. - * @return all loaded materials - */ - public List getMaterials() { - return materials; - } - - /** - * This method returns all loaded textures. - * @return all loaded textures - */ - public List getTextures() { - return textures; - } - - /** - * This method returns all loaded animations. - * @return all loaded animations - */ - public List getAnimations() { - return animations; - } - - /** - * This method returns all loaded cameras. - * @return all loaded cameras - */ - public List getCameras() { - return cameras; - } - - /** - * This method returns all loaded lights. - * @return all loaded lights - */ - public List getLights() { - return lights; - } - - @Override - public int collideWith(Collidable other, CollisionResults results) throws UnsupportedCollisionException { - return 0; - } - - @Override - public void updateModelBound() {} - - @Override - public void setModelBound(BoundingVolume modelBound) {} - - @Override - public int getVertexCount() { - return 0; - } - - @Override - public int getTriangleCount() { - return 0; - } - - @Override - public Spatial deepClone() { - return null; - } - - @Override - public void depthFirstTraversal(SceneGraphVisitor visitor) {} - - @Override - protected void breadthFirstTraversal(SceneGraphVisitor visitor, Queue queue) {} - } - - /** - * The WORLD file block contains various data that could be added to the scene. The contained data includes: ambient - * light. - * @author Marcin Roguski (Kaelthas) - */ - public static class WorldData { - /** The ambient light. */ - private AmbientLight ambientLight; - - /** - * This method returns the world's ambient light. - * @return the world's ambient light - */ - public AmbientLight getAmbientLight() { - return ambientLight; - } - - /** - * This method sets the world's ambient light. - * @param ambientLight - * the world's ambient light - */ - public void setAmbientLight(AmbientLight ambientLight) { - this.ambientLight = ambientLight; - } - } + + protected static final int DEFAULT_FPS = 25; + /** + * Animation definitions. The key is the object name that owns the animation. The value is a map between animation + * name and its start and stop frames. Blender stores a pointer for animation within object. Therefore one object + * can only have one animation at the time. We want to be able to switch between animations for one object so we + * need to map the object name to animation names the object will use. + */ + protected Map> animations; + /** + * FramesPerSecond parameter describe how many frames there are in each second. It allows to calculate the time + * between the frames. + */ + protected int fps = DEFAULT_FPS; + /** Width of generated textures (in pixels). Blender uses 140x140 by default. */ + protected int generatedTextureWidth = 140; + /** Height of generated textures (in pixels). Blender uses 140x140 by default. */ + protected int generatedTextureHeight = 140; + /** + * This variable is a bitwise flag of FeatureToLoad interface values; By default everything is being loaded. + */ + protected int featuresToLoad = FeaturesToLoad.ALL; + /** The root path for all the assets. */ + protected String assetRootPath; + /** This variable indicate if Y axis is UP axis. If not then Z is up. By default set to true. */ + protected boolean fixUpAxis = true; + /** + * The name of world settings that the importer will use. If not set or specified name does not occur in the file + * then the first world settings in the file will be used. + */ + protected String usedWorld; + /** + * User's default material that is set fo objects that have no material definition in blender. The default value is + * null. If the value is null the importer will use its own default material (gray color - like in blender). + */ + protected Material defaultMaterial; + /** Face cull mode. By default it is disabled. */ + protected FaceCullMode faceCullMode = FaceCullMode.Off; + /** + * Variable describes which layers will be loaded. N-th bit set means N-th layer will be loaded. + * If set to -1 then the current layer will be loaded. + */ + protected int layersToLoad = -1; + + /** + * Constructor used by serialization mechanisms. + */ + public BlenderKey() { + } + + /** + * Constructor. Creates a key for the given file name. + * @param name + * the name (path) of a file + */ + public BlenderKey(String name) { + super(name); + } + + /** + * This method adds an animation definition. If a definition already eixists in the key then it is replaced. + * @param objectName + * the name of animation's owner + * @param name + * the name of the animation + * @param start + * the start frame of the animation + * @param stop + * the stop frame of the animation + */ + public synchronized void addAnimation(String objectName, String name, int start, int stop) { + if (objectName == null) { + throw new IllegalArgumentException("Object name cannot be null!"); + } + if (name == null) { + throw new IllegalArgumentException("Animation name cannot be null!"); + } + if (start > stop) { + throw new IllegalArgumentException("Start frame cannot be greater than stop frame!"); + } + if (animations == null) { + animations = new HashMap>(); + animations.put(objectName, new HashMap()); + } + Map objectAnimations = animations.get(objectName); + if (objectAnimations == null) { + objectAnimations = new HashMap(); + animations.put(objectName, objectAnimations); + } + objectAnimations.put(name, new int[]{start, stop}); + } + + /** + * This method returns the animation frames boundaries. + * @param objectName + * the name of animation's owner + * @param name + * animation name + * @return animation frame boundaries in a table [start, stop] or null if animation of the given name does not + * exists + */ + public int[] getAnimationFrames(String objectName, String name) { + Map objectAnimations = animations == null ? null : animations.get(objectName); + int[] frames = objectAnimations == null ? null : objectAnimations.get(name); + return frames == null ? null : frames.clone(); + } + + /** + * This method returns the animation names for the given object name. + * @param objectName + * the name of the object + * @return an array of animations for this object + */ + public Set getAnimationNames(String objectName) { + Map objectAnimations = animations == null ? null : animations.get(objectName); + return objectAnimations == null ? null : objectAnimations.keySet(); + } + + /** + * This method returns the animations map. + * @return the animations map + */ + public Map> getAnimations() { + return animations; + } + + /** + * This method returns frames per second amount. The default value is BlenderKey.DEFAULT_FPS = 25. + * @return the frames per second amount + */ + public int getFps() { + return fps; + } + + /** + * This method sets frames per second amount. + * @param fps + * the frames per second amount + */ + public void setFps(int fps) { + this.fps = fps; + } + + /** + * This method sets the width of generated texture (in pixels). By default the value is 140 px. + * @param generatedTextureWidth + * the width of generated texture + */ + public void setGeneratedTextureWidth(int generatedTextureWidth) { + this.generatedTextureWidth = generatedTextureWidth; + } + + /** + * This method returns the width of generated texture (in pixels). By default the value is 140 px. + * @return the width of generated texture + */ + public int getGeneratedTextureWidth() { + return generatedTextureWidth; + } + + /** + * This method sets the height of generated texture (in pixels). By default the value is 140 px. + * @param generatedTextureHeight + * the height of generated texture + */ + public void setGeneratedTextureHeight(int generatedTextureHeight) { + this.generatedTextureHeight = generatedTextureHeight; + } + + /** + * This method returns the height of generated texture (in pixels). By default the value is 140 px. + * @return the height of generated texture + */ + public int getGeneratedTextureHeight() { + return generatedTextureHeight; + } + + /** + * This method returns the face cull mode. + * @return the face cull mode + */ + public FaceCullMode getFaceCullMode() { + return faceCullMode; + } + + /** + * This method sets the face cull mode. + * @param faceCullMode + * the face cull mode + */ + public void setFaceCullMode(FaceCullMode faceCullMode) { + this.faceCullMode = faceCullMode; + } + + /** + * This method sets layers to be loaded. + * @param layersToLoad layers to be loaded + */ + public void setLayersToLoad(int layersToLoad) { + this.layersToLoad = layersToLoad; + } + + /** + * This method returns layers to be loaded. + * @return layers to be loaded + */ + public int getLayersToLoad() { + return layersToLoad; + } + + /** + * This method sets the asset root path. + * @param assetRootPath + * the assets root path + */ + public void setAssetRootPath(String assetRootPath) { + this.assetRootPath = assetRootPath; + } + + /** + * This method returns the asset root path. + * @return the asset root path + */ + public String getAssetRootPath() { + return assetRootPath; + } + + /** + * This method adds features to be loaded. + * @param featuresToLoad + * bitwise flag of FeaturesToLoad interface values + */ + public void includeInLoading(int featuresToLoad) { + this.featuresToLoad |= featuresToLoad; + } + + /** + * This method removes features from being loaded. + * @param featuresToLoad + * bitwise flag of FeaturesToLoad interface values + */ + public void excludeFromLoading(int featuresNotToLoad) { + this.featuresToLoad &= ~featuresNotToLoad; + } + + /** + * This method returns bitwise value of FeaturesToLoad interface value. It describes features that will be loaded by + * the blender file loader. + * @return features that will be loaded by the blender file loader + */ + public int getFeaturesToLoad() { + return featuresToLoad; + } + + /** + * This method creates an object where loading results will be stores. Only those features will be allowed to store + * that were specified by features-to-load flag. + * @return an object to store loading results + */ + public LoadingResults prepareLoadingResults() { + return new LoadingResults(featuresToLoad); + } + + /** + * This method sets the fix up axis state. If set to true then Y is up axis. Otherwise the up i Z axis. By default Y + * is up axis. + * @param fixUpAxis + * the up axis state variable + */ + public void setFixUpAxis(boolean fixUpAxis) { + this.fixUpAxis = fixUpAxis; + } + + /** + * This method returns the fix up axis state. If set to true then Y is up axis. Otherwise the up i Z axis. By + * default Y is up axis. + * @return the up axis state variable + */ + public boolean isFixUpAxis() { + return fixUpAxis; + } + + /** + * This mehtod sets the name of the WORLD data block taht should be used during file loading. By default the name is + * not set. If no name is set or the given name does not occur in the file - the first WORLD data block will be used + * during loading (assumin any exists in the file). + * @param usedWorld + * the name of the WORLD block used during loading + */ + public void setUsedWorld(String usedWorld) { + this.usedWorld = usedWorld; + } + + /** + * This mehtod returns the name of the WORLD data block taht should be used during file loading. + * @return the name of the WORLD block used during loading + */ + public String getUsedWorld() { + return usedWorld; + } + + /** + * This method sets the default material for objects. + * @param defaultMaterial + * the default material + */ + public void setDefaultMaterial(Material defaultMaterial) { + this.defaultMaterial = defaultMaterial; + } + + /** + * This method returns the default material. + * @return the default material + */ + public Material getDefaultMaterial() { + return defaultMaterial; + } + + @Override + public void write(JmeExporter e) throws IOException { + super.write(e); + OutputCapsule oc = e.getCapsule(this); + //saving animations + oc.write(animations == null ? 0 : animations.size(), "anim-size", 0); + if (animations != null) { + int objectCounter = 0; + for (Entry> animEntry : animations.entrySet()) { + oc.write(animEntry.getKey(), "animated-object-" + objectCounter, null); + int animsAmount = animEntry.getValue().size(); + oc.write(animsAmount, "anims-amount-" + objectCounter, 0); + for (Entry animsEntry : animEntry.getValue().entrySet()) { + oc.write(animsEntry.getKey(), "anim-name-" + objectCounter, null); + oc.write(animsEntry.getValue(), "anim-frames-" + objectCounter, null); + } + ++objectCounter; + } + } + //saving the rest of the data + oc.write(fps, "fps", DEFAULT_FPS); + oc.write(featuresToLoad, "features-to-load", FeaturesToLoad.ALL); + oc.write(assetRootPath, "asset-root-path", null); + oc.write(fixUpAxis, "fix-up-axis", true); + oc.write(usedWorld, "used-world", null); + oc.write(defaultMaterial, "default-material", null); + oc.write(faceCullMode, "face-cull-mode", FaceCullMode.Off); + oc.write(layersToLoad, "layers-to-load", -1); + } + + @Override + public void read(JmeImporter e) throws IOException { + super.read(e); + InputCapsule ic = e.getCapsule(this); + //reading animations + int animSize = ic.readInt("anim-size", 0); + if (animSize > 0) { + if (animations == null) { + animations = new HashMap>(animSize); + } else { + animations.clear(); + } + for (int i = 0; i < animSize; ++i) { + String objectName = ic.readString("animated-object-" + i, null); + int animationsAmount = ic.readInt("anims-amount-" + i, 0); + Map objectAnimations = new HashMap(animationsAmount); + for (int j = 0; j < animationsAmount; ++j) { + String animName = ic.readString("anim-name-" + i, null); + int[] animFrames = ic.readIntArray("anim-frames-" + i, null); + objectAnimations.put(animName, animFrames); + } + animations.put(objectName, objectAnimations); + } + } + + //reading the rest of the data + fps = ic.readInt("fps", DEFAULT_FPS); + featuresToLoad = ic.readInt("features-to-load", FeaturesToLoad.ALL); + assetRootPath = ic.readString("asset-root-path", null); + fixUpAxis = ic.readBoolean("fix-up-axis", true); + usedWorld = ic.readString("used-world", null); + defaultMaterial = (Material) ic.readSavable("default-material", null); + faceCullMode = ic.readEnum("face-cull-mode", FaceCullMode.class, FaceCullMode.Off); + layersToLoad = ic.readInt("layers-to=load", -1); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + (animations == null ? 0 : animations.hashCode()); + result = prime * result + (assetRootPath == null ? 0 : assetRootPath.hashCode()); + result = prime * result + (defaultMaterial == null ? 0 : defaultMaterial.hashCode()); + result = prime * result + (faceCullMode == null ? 0 : faceCullMode.hashCode()); + result = prime * result + featuresToLoad; + result = prime * result + (fixUpAxis ? 1231 : 1237); + result = prime * result + fps; + result = prime * result + generatedTextureHeight; + result = prime * result + generatedTextureWidth; + result = prime * result + layersToLoad; + result = prime * result + (usedWorld == null ? 0 : usedWorld.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!super.equals(obj)) { + return false; + } + if (this.getClass() != obj.getClass()) { + return false; + } + BlenderKey other = (BlenderKey) obj; + if (animations == null) { + if (other.animations != null) { + return false; + } + } else if (!animations.equals(other.animations)) { + return false; + } + if (assetRootPath == null) { + if (other.assetRootPath != null) { + return false; + } + } else if (!assetRootPath.equals(other.assetRootPath)) { + return false; + } + if (defaultMaterial == null) { + if (other.defaultMaterial != null) { + return false; + } + } else if (!defaultMaterial.equals(other.defaultMaterial)) { + return false; + } + if (faceCullMode != other.faceCullMode) { + return false; + } + if (featuresToLoad != other.featuresToLoad) { + return false; + } + if (fixUpAxis != other.fixUpAxis) { + return false; + } + if (fps != other.fps) { + return false; + } + if (generatedTextureHeight != other.generatedTextureHeight) { + return false; + } + if (generatedTextureWidth != other.generatedTextureWidth) { + return false; + } + if (layersToLoad != other.layersToLoad) { + return false; + } + if (usedWorld == null) { + if (other.usedWorld != null) { + return false; + } + } else if (!usedWorld.equals(other.usedWorld)) { + return false; + } + return true; + } + + /** + * This interface describes the features of the scene that are to be loaded. + * @author Marcin Roguski (Kaelthas) + */ + public static interface FeaturesToLoad { + + int SCENES = 0x0000FFFF; + int OBJECTS = 0x0000000B; + int ANIMATIONS = 0x00000004; + int MATERIALS = 0x00000003; + int TEXTURES = 0x00000001; + int CAMERAS = 0x00000020; + int LIGHTS = 0x00000010; + int ALL = 0xFFFFFFFF; + } + + /** + * This class holds the loading results according to the given loading flag. + * @author Marcin Roguski (Kaelthas) + */ + public static class LoadingResults extends Spatial { + + /** Bitwise mask of features that are to be loaded. */ + private final int featuresToLoad; + /** The scenes from the file. */ + private List scenes; + /** Objects from all scenes. */ + private List objects; + /** Materials from all objects. */ + private List materials; + /** Textures from all objects. */ + private List textures; + /** Animations of all objects. */ + private List animations; + /** All cameras from the file. */ + private List cameras; + /** All lights from the file. */ + private List lights; + + /** + * Private constructor prevents users to create an instance of this class from outside the + * @param featuresToLoad + * bitwise mask of features that are to be loaded + * @see FeaturesToLoad FeaturesToLoad + */ + private LoadingResults(int featuresToLoad) { + this.featuresToLoad = featuresToLoad; + if ((featuresToLoad & FeaturesToLoad.SCENES) != 0) { + scenes = new ArrayList(); + } + if ((featuresToLoad & FeaturesToLoad.OBJECTS) != 0) { + objects = new ArrayList(); + if ((featuresToLoad & FeaturesToLoad.MATERIALS) != 0) { + materials = new ArrayList(); + if ((featuresToLoad & FeaturesToLoad.TEXTURES) != 0) { + textures = new ArrayList(); + } + } + if ((featuresToLoad & FeaturesToLoad.ANIMATIONS) != 0) { + animations = new ArrayList(); + } + } + if ((featuresToLoad & FeaturesToLoad.CAMERAS) != 0) { + cameras = new ArrayList(); + } + if ((featuresToLoad & FeaturesToLoad.LIGHTS) != 0) { + lights = new ArrayList(); + } + } + + /** + * This method returns a bitwise flag describing what features of the blend file will be included in the result. + * @return bitwise mask of features that are to be loaded + * @see FeaturesToLoad FeaturesToLoad + */ + public int getLoadedFeatures() { + return featuresToLoad; + } + + /** + * This method adds a scene to the result set. + * @param scene + * scene to be added to the result set + */ + public void addScene(Node scene) { + if (scenes != null) { + scenes.add(scene); + } + } + + /** + * This method adds an object to the result set. + * @param object + * object to be added to the result set + */ + public void addObject(Node object) { + if (objects != null) { + objects.add(object); + } + } + + /** + * This method adds a material to the result set. + * @param material + * material to be added to the result set + */ + public void addMaterial(Material material) { + if (materials != null) { + materials.add(material); + } + } + + /** + * This method adds a texture to the result set. + * @param texture + * texture to be added to the result set + */ + public void addTexture(Texture texture) { + if (textures != null) { + textures.add(texture); + } + } + + /** + * This method adds a camera to the result set. + * @param camera + * camera to be added to the result set + */ + public void addCamera(Camera camera) { + if (cameras != null) { + cameras.add(camera); + } + } + + /** + * This method adds a light to the result set. + * @param light + * light to be added to the result set + */ + @Override + public void addLight(Light light) { + if (lights != null) { + lights.add(light); + } + } + + /** + * This method returns all loaded scenes. + * @return all loaded scenes + */ + public List getScenes() { + return scenes; + } + + /** + * This method returns all loaded objects. + * @return all loaded objects + */ + public List getObjects() { + return objects; + } + + /** + * This method returns all loaded materials. + * @return all loaded materials + */ + public List getMaterials() { + return materials; + } + + /** + * This method returns all loaded textures. + * @return all loaded textures + */ + public List getTextures() { + return textures; + } + + /** + * This method returns all loaded animations. + * @return all loaded animations + */ + public List getAnimations() { + return animations; + } + + /** + * This method returns all loaded cameras. + * @return all loaded cameras + */ + public List getCameras() { + return cameras; + } + + /** + * This method returns all loaded lights. + * @return all loaded lights + */ + public List getLights() { + return lights; + } + + @Override + public int collideWith(Collidable other, CollisionResults results) throws UnsupportedCollisionException { + return 0; + } + + @Override + public void updateModelBound() { + } + + @Override + public void setModelBound(BoundingVolume modelBound) { + } + + @Override + public int getVertexCount() { + return 0; + } + + @Override + public int getTriangleCount() { + return 0; + } + + @Override + public Spatial deepClone() { + return null; + } + + @Override + public void depthFirstTraversal(SceneGraphVisitor visitor) { + } + + @Override + protected void breadthFirstTraversal(SceneGraphVisitor visitor, Queue queue) { + } + } + + /** + * The WORLD file block contains various data that could be added to the scene. The contained data includes: ambient + * light. + * @author Marcin Roguski (Kaelthas) + */ + public static class WorldData { + + /** The ambient light. */ + private AmbientLight ambientLight; + + /** + * This method returns the world's ambient light. + * @return the world's ambient light + */ + public AmbientLight getAmbientLight() { + return ambientLight; + } + + /** + * This method sets the world's ambient light. + * @param ambientLight + * the world's ambient light + */ + public void setAmbientLight(AmbientLight ambientLight) { + this.ambientLight = ambientLight; + } + } } diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/BlenderLoader.java b/engine/src/blender/com/jme3/scene/plugins/blender/BlenderLoader.java index bc8a51b5d..e8dee730e 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/BlenderLoader.java +++ b/engine/src/blender/com/jme3/scene/plugins/blender/BlenderLoader.java @@ -72,122 +72,123 @@ import com.jme3.scene.plugins.blender.utils.JmeConverter; * @author Marcin Roguski */ public class BlenderLoader implements AssetLoader { - private static final Logger LOGGER = Logger.getLogger(BlenderLoader.class.getName()); - @Override - public LoadingResults load(AssetInfo assetInfo) throws IOException { - try { - //registering loaders - ModelKey modelKey = (ModelKey)assetInfo.getKey(); - BlenderKey blenderKey; - if(modelKey instanceof BlenderKey) { - blenderKey = (BlenderKey)modelKey; - } else { - blenderKey = new BlenderKey(modelKey.getName()); - blenderKey.setAssetRootPath(modelKey.getFolder()); - } + private static final Logger LOGGER = Logger.getLogger(BlenderLoader.class.getName()); - //opening stream - BlenderInputStream inputStream = new BlenderInputStream(assetInfo.openStream(), assetInfo.getManager()); + @Override + public LoadingResults load(AssetInfo assetInfo) throws IOException { + try { + //registering loaders + ModelKey modelKey = (ModelKey) assetInfo.getKey(); + BlenderKey blenderKey; + if (modelKey instanceof BlenderKey) { + blenderKey = (BlenderKey) modelKey; + } else { + blenderKey = new BlenderKey(modelKey.getName()); + blenderKey.setAssetRootPath(modelKey.getFolder()); + } - //reading blocks - List blocks = new ArrayList(); - FileBlockHeader fileBlock; - DataRepository dataRepository = new DataRepository(); - dataRepository.setAssetManager(assetInfo.getManager()); - dataRepository.setInputStream(inputStream); - dataRepository.setBlenderKey(blenderKey); - - //creating helpers - 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()); + //opening stream + BlenderInputStream inputStream = new BlenderInputStream(assetInfo.openStream(), assetInfo.getManager()); - //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()); + //reading blocks + List blocks = new ArrayList(); + FileBlockHeader fileBlock; + DataRepository dataRepository = new DataRepository(); + dataRepository.setAssetManager(assetInfo.getManager()); + dataRepository.setInputStream(inputStream); + dataRepository.setBlenderKey(blenderKey); - JmeConverter converter = new JmeConverter(dataRepository); - LoadingResults loadingResults = blenderKey.prepareLoadingResults(); - WorldData worldData = null;//a set of data used in different scene aspects - for(FileBlockHeader block : blocks) { - switch(block.getCode()) { - case FileBlockHeader.BLOCK_OB00://Object - Object object = converter.toObject(block.getStructure(dataRepository)); - if(object instanceof Node) { - if((blenderKey.getFeaturesToLoad() & FeaturesToLoad.OBJECTS) != 0) { - 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); - } - } - } else if(object instanceof Camera) { - if((blenderKey.getFeaturesToLoad() & FeaturesToLoad.CAMERAS) != 0) { - loadingResults.addCamera((Camera)object); - } - } else if(object instanceof Light) { - if((blenderKey.getFeaturesToLoad() & FeaturesToLoad.LIGHTS) != 0) { - loadingResults.addLight((Light)object); - } - } - break; - case FileBlockHeader.BLOCK_MA00://Material - if((blenderKey.getFeaturesToLoad() & FeaturesToLoad.MATERIALS) != 0) { - loadingResults.addMaterial(converter.toMaterial(block.getStructure(dataRepository))); - } - break; - case FileBlockHeader.BLOCK_SC00://Scene - if((blenderKey.getFeaturesToLoad() & FeaturesToLoad.SCENES) != 0) { - loadingResults.addScene(converter.toScene(block.getStructure(dataRepository))); - } - break; - case FileBlockHeader.BLOCK_WO00://World - if(worldData == null) {//onlu one world data is used - Structure worldStructure = block.getStructure(dataRepository); - String worldName = worldStructure.getName(); - if(blenderKey.getUsedWorld() == null || blenderKey.getUsedWorld().equals(worldName)) { - worldData = converter.toWorldData(worldStructure); - if((blenderKey.getFeaturesToLoad() & FeaturesToLoad.LIGHTS) != 0) { - loadingResults.addLight(worldData.getAmbientLight()); - } - } - } - break; - } - } - try { - inputStream.close(); - } catch(IOException e) { - LOGGER.log(Level.SEVERE, e.getMessage(), e); - } - return loadingResults; - } catch(BlenderFileException e) { - LOGGER.log(Level.SEVERE, e.getMessage(), e); - } - return null; - } + //creating helpers + 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()); + + //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(); + WorldData worldData = null;//a set of data used in different scene aspects + for (FileBlockHeader block : blocks) { + switch (block.getCode()) { + case FileBlockHeader.BLOCK_OB00://Object + Object object = converter.toObject(block.getStructure(dataRepository)); + if (object instanceof Node) { + if ((blenderKey.getFeaturesToLoad() & FeaturesToLoad.OBJECTS) != 0) { + LOGGER.log(Level.INFO, "{0}: {1}--> {2}", new Object[]{((Node) object).getName(), ((Node) object).getLocalTranslation().toString(), ((Node) object).getParent() == null ? "null" : ((Node) object).getParent().getName()}); + if (((Node) object).getParent() == null) { + loadingResults.addObject((Node) object); + } + } + } else if (object instanceof Camera) { + if ((blenderKey.getFeaturesToLoad() & FeaturesToLoad.CAMERAS) != 0) { + loadingResults.addCamera((Camera) object); + } + } else if (object instanceof Light) { + if ((blenderKey.getFeaturesToLoad() & FeaturesToLoad.LIGHTS) != 0) { + loadingResults.addLight((Light) object); + } + } + break; + case FileBlockHeader.BLOCK_MA00://Material + if ((blenderKey.getFeaturesToLoad() & FeaturesToLoad.MATERIALS) != 0) { + loadingResults.addMaterial(converter.toMaterial(block.getStructure(dataRepository))); + } + break; + case FileBlockHeader.BLOCK_SC00://Scene + if ((blenderKey.getFeaturesToLoad() & FeaturesToLoad.SCENES) != 0) { + loadingResults.addScene(converter.toScene(block.getStructure(dataRepository))); + } + break; + case FileBlockHeader.BLOCK_WO00://World + if (worldData == null) {//onlu one world data is used + Structure worldStructure = block.getStructure(dataRepository); + String worldName = worldStructure.getName(); + if (blenderKey.getUsedWorld() == null || blenderKey.getUsedWorld().equals(worldName)) { + worldData = converter.toWorldData(worldStructure); + if ((blenderKey.getFeaturesToLoad() & FeaturesToLoad.LIGHTS) != 0) { + loadingResults.addLight(worldData.getAmbientLight()); + } + } + } + break; + } + } + try { + inputStream.close(); + } catch (IOException e) { + LOGGER.log(Level.SEVERE, e.getMessage(), e); + } + return loadingResults; + } catch (BlenderFileException e) { + LOGGER.log(Level.SEVERE, e.getMessage(), e); + } + return null; + } } diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/BlenderModelLoader.java b/engine/src/blender/com/jme3/scene/plugins/blender/BlenderModelLoader.java index 096ec23e1..a6e0a0304 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/BlenderModelLoader.java +++ b/engine/src/blender/com/jme3/scene/plugins/blender/BlenderModelLoader.java @@ -69,89 +69,90 @@ import com.jme3.scene.plugins.blender.utils.JmeConverter; * @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 blocks = new ArrayList(); - 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 blocks = new ArrayList(); + 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 objects = loadingResults.getObjects(); - if(objects.size() > 0) { - Node modelNode = new Node(blenderKey.getName()); - for(Iterator 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 objects = loadingResults.getObjects(); + if (objects.size() > 0) { + Node modelNode = new Node(blenderKey.getName()); + for (Iterator 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; + } } diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/data/DnaBlockData.java b/engine/src/blender/com/jme3/scene/plugins/blender/data/DnaBlockData.java index 5872039ac..8f72f6d9f 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/data/DnaBlockData.java +++ b/engine/src/blender/com/jme3/scene/plugins/blender/data/DnaBlockData.java @@ -43,168 +43,168 @@ import com.jme3.scene.plugins.blender.utils.DataRepository; * @author Marcin Roguski */ public class DnaBlockData { - private static final int SDNA_ID = 'S' << 24 | 'D' << 16 | 'N' << 8 | 'A'; //SDNA - private static final int NAME_ID = 'N' << 24 | 'A' << 16 | 'M' << 8 | 'E'; //NAME - private static final int TYPE_ID = 'T' << 24 | 'Y' << 16 | 'P' << 8 | 'E'; //TYPE - private static final int TLEN_ID = 'T' << 24 | 'L' << 16 | 'E' << 8 | 'N'; //TLEN - private static final int STRC_ID = 'S' << 24 | 'T' << 16 | 'R' << 8 | 'C'; //STRC - - /** Structures available inside the file. */ - private final Structure[] structures; - /** A map that helps finding a structure by type. */ - private final Map structuresMap; - - /** - * Constructor. Loads the block from the given stream during instance creation. - * @param inputStream - * the stream we read the block from - * @param dataRepository - * the data repository - * @throws BlenderFileException - * this exception is throw if the blend file is invalid or somehow corrupted - */ - public DnaBlockData(BlenderInputStream inputStream, DataRepository dataRepository) throws BlenderFileException { - int identifier; - - //reading 'SDNA' identifier - identifier = inputStream.readByte() << 24 | inputStream.readByte() << 16 | - inputStream.readByte() << 8 | inputStream.readByte(); - - if(identifier != SDNA_ID) { - throw new BlenderFileException("Invalid identifier! '" + this.toString(SDNA_ID) + "' expected and found: " + this.toString(identifier)); - } - - //reading names - identifier = inputStream.readByte() << 24 | inputStream.readByte() << 16 | - inputStream.readByte() << 8 | inputStream.readByte(); - if(identifier != NAME_ID) { - throw new BlenderFileException("Invalid identifier! '" + this.toString(NAME_ID) + "' expected and found: " + this.toString(identifier)); - } - int amount = inputStream.readInt(); - if(amount <= 0) { - throw new BlenderFileException("The names amount number should be positive!"); - } - String[] names = new String[amount]; - for(int i = 0; i < amount; ++i) { - names[i] = inputStream.readString(); - } - - //reding types - inputStream.alignPosition(4); - identifier = inputStream.readByte() << 24 | inputStream.readByte() << 16 | - inputStream.readByte() << 8 | inputStream.readByte(); - if(identifier != TYPE_ID) { - throw new BlenderFileException("Invalid identifier! '" + this.toString(TYPE_ID) + "' expected and found: " + this.toString(identifier)); - } - amount = inputStream.readInt(); - if(amount <= 0) { - throw new BlenderFileException("The types amount number should be positive!"); - } - String[] types = new String[amount]; - for(int i = 0; i < amount; ++i) { - types[i] = inputStream.readString(); - } - - //reading lengths - inputStream.alignPosition(4); - identifier = inputStream.readByte() << 24 | inputStream.readByte() << 16 | - inputStream.readByte() << 8 | inputStream.readByte(); - if(identifier != TLEN_ID) { - throw new BlenderFileException("Invalid identifier! '" + this.toString(TLEN_ID) + "' expected and found: " + this.toString(identifier)); - } - int[] lengths = new int[amount];//theamount is the same as int types - for(int i = 0; i < amount; ++i) { - lengths[i] = inputStream.readShort(); - } - - //reading structures - inputStream.alignPosition(4); - identifier = inputStream.readByte() << 24 | inputStream.readByte() << 16 | - inputStream.readByte() << 8 | inputStream.readByte(); - if(identifier != STRC_ID) { - throw new BlenderFileException("Invalid identifier! '" + this.toString(STRC_ID) + "' expected and found: " + this.toString(identifier)); - } - amount = inputStream.readInt(); - if(amount <= 0) { - throw new BlenderFileException("The structures amount number should be positive!"); - } - structures = new Structure[amount]; - structuresMap = new HashMap(amount); - for(int i = 0; i < amount; ++i) { - structures[i] = new Structure(inputStream, names, types, dataRepository); - if(structuresMap.containsKey(structures[i].getType())) { - throw new BlenderFileException("Blend file seems to be corrupted! The type " + structures[i].getType() + " is defined twice!"); - } - structuresMap.put(structures[i].getType(), structures[i]); - } - } - - /** - * This method returns the amount of the structures. - * @return the amount of the structures - */ - public int getStructuresCount() { - return structures.length; - } - - /** - * This method returns the structure of the given index. - * @param index - * the index of the structure - * @return the structure of the given index - */ - public Structure getStructure(int index) { - try { - return (Structure)structures[index].clone(); - } catch(CloneNotSupportedException e) { - throw new IllegalStateException("Structure should be clonable!!!", e); - } - } - - /** - * This method returns a structure of the given name. If the name does not exists then null is returned. - * @param name - * the name of the structure - * @return the required structure or null if the given name is inapropriate - */ - public Structure getStructure(String name) { - try { - return (Structure)structuresMap.get(name).clone(); - } catch(CloneNotSupportedException e) { - throw new IllegalStateException(e.getMessage(), e); - } - } - - /** - * This method indicates if the structure of the given name exists. - * @param name - * the name of the structure - * @return true if the structure exists and false otherwise - */ - public boolean hasStructure(String name) { - return structuresMap.containsKey(name); - } - - /** - * This method converts the given identifier code to string. - * @param code - * the code taht is to be converted - * @return the string value of the identifier - */ - private String toString(int code) { - char c1 = (char)((code & 0xFF000000) >> 24); - char c2 = (char)((code & 0xFF0000) >> 16); - char c3 = (char)((code & 0xFF00) >> 8); - char c4 = (char)(code & 0xFF); - return String.valueOf(c1) + c2 + c3 + c4; - } - - @Override - public String toString() { - StringBuilder stringBuilder = new StringBuilder("=============== ").append(SDNA_ID).append('\n'); - for(Structure structure : structures) { - stringBuilder.append(structure.toString()).append('\n'); - } - return stringBuilder.append("===============").toString(); - } + + private static final int SDNA_ID = 'S' << 24 | 'D' << 16 | 'N' << 8 | 'A'; //SDNA + private static final int NAME_ID = 'N' << 24 | 'A' << 16 | 'M' << 8 | 'E'; //NAME + private static final int TYPE_ID = 'T' << 24 | 'Y' << 16 | 'P' << 8 | 'E'; //TYPE + private static final int TLEN_ID = 'T' << 24 | 'L' << 16 | 'E' << 8 | 'N'; //TLEN + private static final int STRC_ID = 'S' << 24 | 'T' << 16 | 'R' << 8 | 'C'; //STRC + /** Structures available inside the file. */ + private final Structure[] structures; + /** A map that helps finding a structure by type. */ + private final Map structuresMap; + + /** + * Constructor. Loads the block from the given stream during instance creation. + * @param inputStream + * the stream we read the block from + * @param dataRepository + * the data repository + * @throws BlenderFileException + * this exception is throw if the blend file is invalid or somehow corrupted + */ + public DnaBlockData(BlenderInputStream inputStream, DataRepository dataRepository) throws BlenderFileException { + int identifier; + + //reading 'SDNA' identifier + identifier = inputStream.readByte() << 24 | inputStream.readByte() << 16 + | inputStream.readByte() << 8 | inputStream.readByte(); + + if (identifier != SDNA_ID) { + throw new BlenderFileException("Invalid identifier! '" + this.toString(SDNA_ID) + "' expected and found: " + this.toString(identifier)); + } + + //reading names + identifier = inputStream.readByte() << 24 | inputStream.readByte() << 16 + | inputStream.readByte() << 8 | inputStream.readByte(); + if (identifier != NAME_ID) { + throw new BlenderFileException("Invalid identifier! '" + this.toString(NAME_ID) + "' expected and found: " + this.toString(identifier)); + } + int amount = inputStream.readInt(); + if (amount <= 0) { + throw new BlenderFileException("The names amount number should be positive!"); + } + String[] names = new String[amount]; + for (int i = 0; i < amount; ++i) { + names[i] = inputStream.readString(); + } + + //reding types + inputStream.alignPosition(4); + identifier = inputStream.readByte() << 24 | inputStream.readByte() << 16 + | inputStream.readByte() << 8 | inputStream.readByte(); + if (identifier != TYPE_ID) { + throw new BlenderFileException("Invalid identifier! '" + this.toString(TYPE_ID) + "' expected and found: " + this.toString(identifier)); + } + amount = inputStream.readInt(); + if (amount <= 0) { + throw new BlenderFileException("The types amount number should be positive!"); + } + String[] types = new String[amount]; + for (int i = 0; i < amount; ++i) { + types[i] = inputStream.readString(); + } + + //reading lengths + inputStream.alignPosition(4); + identifier = inputStream.readByte() << 24 | inputStream.readByte() << 16 + | inputStream.readByte() << 8 | inputStream.readByte(); + if (identifier != TLEN_ID) { + throw new BlenderFileException("Invalid identifier! '" + this.toString(TLEN_ID) + "' expected and found: " + this.toString(identifier)); + } + int[] lengths = new int[amount];//theamount is the same as int types + for (int i = 0; i < amount; ++i) { + lengths[i] = inputStream.readShort(); + } + + //reading structures + inputStream.alignPosition(4); + identifier = inputStream.readByte() << 24 | inputStream.readByte() << 16 + | inputStream.readByte() << 8 | inputStream.readByte(); + if (identifier != STRC_ID) { + throw new BlenderFileException("Invalid identifier! '" + this.toString(STRC_ID) + "' expected and found: " + this.toString(identifier)); + } + amount = inputStream.readInt(); + if (amount <= 0) { + throw new BlenderFileException("The structures amount number should be positive!"); + } + structures = new Structure[amount]; + structuresMap = new HashMap(amount); + for (int i = 0; i < amount; ++i) { + structures[i] = new Structure(inputStream, names, types, dataRepository); + if (structuresMap.containsKey(structures[i].getType())) { + throw new BlenderFileException("Blend file seems to be corrupted! The type " + structures[i].getType() + " is defined twice!"); + } + structuresMap.put(structures[i].getType(), structures[i]); + } + } + + /** + * This method returns the amount of the structures. + * @return the amount of the structures + */ + public int getStructuresCount() { + return structures.length; + } + + /** + * This method returns the structure of the given index. + * @param index + * the index of the structure + * @return the structure of the given index + */ + public Structure getStructure(int index) { + try { + return (Structure) structures[index].clone(); + } catch (CloneNotSupportedException e) { + throw new IllegalStateException("Structure should be clonable!!!", e); + } + } + + /** + * This method returns a structure of the given name. If the name does not exists then null is returned. + * @param name + * the name of the structure + * @return the required structure or null if the given name is inapropriate + */ + public Structure getStructure(String name) { + try { + return (Structure) structuresMap.get(name).clone(); + } catch (CloneNotSupportedException e) { + throw new IllegalStateException(e.getMessage(), e); + } + } + + /** + * This method indicates if the structure of the given name exists. + * @param name + * the name of the structure + * @return true if the structure exists and false otherwise + */ + public boolean hasStructure(String name) { + return structuresMap.containsKey(name); + } + + /** + * This method converts the given identifier code to string. + * @param code + * the code taht is to be converted + * @return the string value of the identifier + */ + private String toString(int code) { + char c1 = (char) ((code & 0xFF000000) >> 24); + char c2 = (char) ((code & 0xFF0000) >> 16); + char c3 = (char) ((code & 0xFF00) >> 8); + char c4 = (char) (code & 0xFF); + return String.valueOf(c1) + c2 + c3 + c4; + } + + @Override + public String toString() { + StringBuilder stringBuilder = new StringBuilder("=============== ").append(SDNA_ID).append('\n'); + for (Structure structure : structures) { + stringBuilder.append(structure.toString()).append('\n'); + } + return stringBuilder.append("===============").toString(); + } } diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/data/Field.java b/engine/src/blender/com/jme3/scene/plugins/blender/data/Field.java index e57862ece..4823af7cd 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/data/Field.java +++ b/engine/src/blender/com/jme3/scene/plugins/blender/data/Field.java @@ -15,305 +15,306 @@ import com.jme3.scene.plugins.blender.utils.Pointer; * another structure. * @author Marcin Roguski */ -/*package*/class Field implements Cloneable { - private static final int NAME_LENGTH = 24; - private static final int TYPE_LENGTH = 16; +/*package*/ +class Field implements Cloneable { - /** The data repository. */ - public DataRepository dataRepository; - /** The type of the field. */ - public String type; - /** The name of the field. */ - public String name; - /** The value of the field. Filled during data reading. */ - public Object value; - /** This variable indicates the level of the pointer. */ - public int pointerLevel; - /** - * This variable determines the sizes of the array. If the value is null the n the field is not an array. - */ - public int[] tableSizes; - /** This variable indicates if the field is a function pointer. */ - public boolean function; + private static final int NAME_LENGTH = 24; + private static final int TYPE_LENGTH = 16; + /** The data repository. */ + public DataRepository dataRepository; + /** The type of the field. */ + public String type; + /** The name of the field. */ + public String name; + /** The value of the field. Filled during data reading. */ + public Object value; + /** This variable indicates the level of the pointer. */ + public int pointerLevel; + /** + * This variable determines the sizes of the array. If the value is null the n the field is not an array. + */ + public int[] tableSizes; + /** This variable indicates if the field is a function pointer. */ + public boolean function; - /** - * Constructor. Saves the field data and parses its name. - * @param name - * the name of the field - * @param type - * the type of the field - * @param dataRepository - * the data repository - * @throws BlenderFileException - * this exception is thrown if the names contain errors - */ - public Field(String name, String type, DataRepository dataRepository) throws BlenderFileException { - this.type = type; - this.dataRepository = dataRepository; - this.parseField(new StringBuilder(name)); - } + /** + * Constructor. Saves the field data and parses its name. + * @param name + * the name of the field + * @param type + * the type of the field + * @param dataRepository + * the data repository + * @throws BlenderFileException + * this exception is thrown if the names contain errors + */ + public Field(String name, String type, DataRepository dataRepository) throws BlenderFileException { + this.type = type; + this.dataRepository = dataRepository; + this.parseField(new StringBuilder(name)); + } - /** - * Copy constructor. Used in clone method. Copying is not full. The value in the new object is not set so that we - * have a clead empty copy of the filed to fill with data. - * @param field - * the object that we copy - */ - private Field(Field field) { - type = field.type; - name = field.name; - dataRepository = field.dataRepository; - pointerLevel = field.pointerLevel; - if(field.tableSizes != null) { - tableSizes = field.tableSizes.clone(); - } - function = field.function; - } + /** + * Copy constructor. Used in clone method. Copying is not full. The value in the new object is not set so that we + * have a clead empty copy of the filed to fill with data. + * @param field + * the object that we copy + */ + private Field(Field field) { + type = field.type; + name = field.name; + dataRepository = field.dataRepository; + pointerLevel = field.pointerLevel; + if (field.tableSizes != null) { + tableSizes = field.tableSizes.clone(); + } + function = field.function; + } - @Override - public Object clone() throws CloneNotSupportedException { - return new Field(this); - } + @Override + public Object clone() throws CloneNotSupportedException { + return new Field(this); + } - /** - * This method fills the field wth data read from the input stream. - * @param blenderInputStream - * the stream we read data from - * @throws BlenderFileException - * an exception is thrown when the blend file is somehow invalid or corrupted - */ - public void fill(BlenderInputStream blenderInputStream) throws BlenderFileException { - int dataToRead = 1; - if(tableSizes != null && tableSizes.length > 0) { - for(int size : tableSizes) { - if(size <= 0) { - throw new BlenderFileException("The field " + name + " has invalid table size: " + size); - } - dataToRead *= size; - } - } - DataType dataType = pointerLevel == 0 ? DataType.getDataType(type, dataRepository) : DataType.POINTER; - switch(dataType) { - case POINTER: - if(dataToRead == 1) { - Pointer pointer = new Pointer(pointerLevel, function, dataRepository); - pointer.fill(blenderInputStream); - value = pointer; - } else { - Pointer[] data = new Pointer[dataToRead]; - for(int i = 0; i < dataToRead; ++i) { - Pointer pointer = new Pointer(pointerLevel, function, dataRepository); - pointer.fill(blenderInputStream); - data[i] = pointer; - } - value = new DynamicArray(tableSizes, data); - } - break; - case CHARACTER: - //character is also stored as a number, because sometimes the new blender version uses - //other number type instead of character as a field type - //and characters are very often used as byte number stores instead of real chars - if(dataToRead == 1) { - value = Byte.valueOf((byte)blenderInputStream.readByte()); - } else { - Character[] data = new Character[dataToRead]; - for(int i = 0; i < dataToRead; ++i) { - data[i] = Character.valueOf((char)blenderInputStream.readByte()); - } - value = new DynamicArray(tableSizes, data); - } - break; - case SHORT: - if(dataToRead == 1) { - value = Integer.valueOf(blenderInputStream.readShort()); - } else { - Number[] data = new Number[dataToRead]; - for(int i = 0; i < dataToRead; ++i) { - data[i] = Integer.valueOf(blenderInputStream.readShort()); - } - value = new DynamicArray(tableSizes, data); - } - break; - case INTEGER: - if(dataToRead == 1) { - value = Integer.valueOf(blenderInputStream.readInt()); - } else { - Number[] data = new Number[dataToRead]; - for(int i = 0; i < dataToRead; ++i) { - data[i] = Integer.valueOf(blenderInputStream.readInt()); - } - value = new DynamicArray(tableSizes, data); - } - break; - case LONG: - if(dataToRead == 1) { - value = Long.valueOf(blenderInputStream.readLong()); - } else { - Number[] data = new Number[dataToRead]; - for(int i = 0; i < dataToRead; ++i) { - data[i] = Long.valueOf(blenderInputStream.readLong()); - } - value = new DynamicArray(tableSizes, data); - } - break; - case FLOAT: - if(dataToRead == 1) { - value = Float.valueOf(blenderInputStream.readFloat()); - } else { - Number[] data = new Number[dataToRead]; - for(int i = 0; i < dataToRead; ++i) { - data[i] = Float.valueOf(blenderInputStream.readFloat()); - } - value = new DynamicArray(tableSizes, data); - } - break; - case DOUBLE: - if(dataToRead == 1) { - value = Double.valueOf(blenderInputStream.readDouble()); - } else { - Number[] data = new Number[dataToRead]; - for(int i = 0; i < dataToRead; ++i) { - data[i] = Double.valueOf(blenderInputStream.readDouble()); - } - value = new DynamicArray(tableSizes, data); - } - break; - case VOID: - break; - case STRUCTURE: - if(dataToRead == 1) { - Structure structure = dataRepository.getDnaBlockData().getStructure(type); - structure.fill(blenderInputStream); - value = structure; - } else { - Structure[] data = new Structure[dataToRead]; - for(int i = 0; i < dataToRead; ++i) { - Structure structure = dataRepository.getDnaBlockData().getStructure(type); - structure.fill(blenderInputStream); - data[i] = structure; - } - value = new DynamicArray(tableSizes, data); - } - break; - default: - throw new IllegalStateException("Unimplemented filling of type: " + type); - } - } + /** + * This method fills the field wth data read from the input stream. + * @param blenderInputStream + * the stream we read data from + * @throws BlenderFileException + * an exception is thrown when the blend file is somehow invalid or corrupted + */ + public void fill(BlenderInputStream blenderInputStream) throws BlenderFileException { + int dataToRead = 1; + if (tableSizes != null && tableSizes.length > 0) { + for (int size : tableSizes) { + if (size <= 0) { + throw new BlenderFileException("The field " + name + " has invalid table size: " + size); + } + dataToRead *= size; + } + } + DataType dataType = pointerLevel == 0 ? DataType.getDataType(type, dataRepository) : DataType.POINTER; + switch (dataType) { + case POINTER: + if (dataToRead == 1) { + Pointer pointer = new Pointer(pointerLevel, function, dataRepository); + pointer.fill(blenderInputStream); + value = pointer; + } else { + Pointer[] data = new Pointer[dataToRead]; + for (int i = 0; i < dataToRead; ++i) { + Pointer pointer = new Pointer(pointerLevel, function, dataRepository); + pointer.fill(blenderInputStream); + data[i] = pointer; + } + value = new DynamicArray(tableSizes, data); + } + break; + case CHARACTER: + //character is also stored as a number, because sometimes the new blender version uses + //other number type instead of character as a field type + //and characters are very often used as byte number stores instead of real chars + if (dataToRead == 1) { + value = Byte.valueOf((byte) blenderInputStream.readByte()); + } else { + Character[] data = new Character[dataToRead]; + for (int i = 0; i < dataToRead; ++i) { + data[i] = Character.valueOf((char) blenderInputStream.readByte()); + } + value = new DynamicArray(tableSizes, data); + } + break; + case SHORT: + if (dataToRead == 1) { + value = Integer.valueOf(blenderInputStream.readShort()); + } else { + Number[] data = new Number[dataToRead]; + for (int i = 0; i < dataToRead; ++i) { + data[i] = Integer.valueOf(blenderInputStream.readShort()); + } + value = new DynamicArray(tableSizes, data); + } + break; + case INTEGER: + if (dataToRead == 1) { + value = Integer.valueOf(blenderInputStream.readInt()); + } else { + Number[] data = new Number[dataToRead]; + for (int i = 0; i < dataToRead; ++i) { + data[i] = Integer.valueOf(blenderInputStream.readInt()); + } + value = new DynamicArray(tableSizes, data); + } + break; + case LONG: + if (dataToRead == 1) { + value = Long.valueOf(blenderInputStream.readLong()); + } else { + Number[] data = new Number[dataToRead]; + for (int i = 0; i < dataToRead; ++i) { + data[i] = Long.valueOf(blenderInputStream.readLong()); + } + value = new DynamicArray(tableSizes, data); + } + break; + case FLOAT: + if (dataToRead == 1) { + value = Float.valueOf(blenderInputStream.readFloat()); + } else { + Number[] data = new Number[dataToRead]; + for (int i = 0; i < dataToRead; ++i) { + data[i] = Float.valueOf(blenderInputStream.readFloat()); + } + value = new DynamicArray(tableSizes, data); + } + break; + case DOUBLE: + if (dataToRead == 1) { + value = Double.valueOf(blenderInputStream.readDouble()); + } else { + Number[] data = new Number[dataToRead]; + for (int i = 0; i < dataToRead; ++i) { + data[i] = Double.valueOf(blenderInputStream.readDouble()); + } + value = new DynamicArray(tableSizes, data); + } + break; + case VOID: + break; + case STRUCTURE: + if (dataToRead == 1) { + Structure structure = dataRepository.getDnaBlockData().getStructure(type); + structure.fill(blenderInputStream); + value = structure; + } else { + Structure[] data = new Structure[dataToRead]; + for (int i = 0; i < dataToRead; ++i) { + Structure structure = dataRepository.getDnaBlockData().getStructure(type); + structure.fill(blenderInputStream); + data[i] = structure; + } + value = new DynamicArray(tableSizes, data); + } + break; + default: + throw new IllegalStateException("Unimplemented filling of type: " + type); + } + } - /** - * This method parses the field name to determine how the field should be used. - * @param nameBuilder - * the name of the field (given as StringBuilder) - * @throws BlenderFileException - * this exception is thrown if the names contain errors - */ - private void parseField(StringBuilder nameBuilder) throws BlenderFileException { - this.removeWhitespaces(nameBuilder); - //veryfying if the name is a pointer - int pointerIndex = nameBuilder.indexOf("*"); - while(pointerIndex >= 0) { - ++pointerLevel; - nameBuilder.deleteCharAt(pointerIndex); - pointerIndex = nameBuilder.indexOf("*"); - } - //veryfying if the name is a function pointer - if(nameBuilder.indexOf("(") >= 0) { - function = true; - this.removeCharacter(nameBuilder, '('); - this.removeCharacter(nameBuilder, ')'); - } else { - //veryfying if the name is a table - int tableStartIndex = 0; - List lengths = new ArrayList(3);//3 dimensions will be enough in most cases - do { - tableStartIndex = nameBuilder.indexOf("["); - if(tableStartIndex > 0) { - int tableStopIndex = nameBuilder.indexOf("]"); - if(tableStopIndex < 0) { - throw new BlenderFileException("Invalid structure name: " + name); - } - try { - lengths.add(Integer.valueOf(nameBuilder.substring(tableStartIndex + 1, tableStopIndex))); - } catch(NumberFormatException e) { - throw new BlenderFileException("Invalid structure name caused by invalid table length: " + name, e); - } - nameBuilder.delete(tableStartIndex, tableStopIndex + 1); - } - } while(tableStartIndex > 0); - if(!lengths.isEmpty()) { - tableSizes = new int[lengths.size()]; - for(int i = 0; i < tableSizes.length; ++i) { - tableSizes[i] = lengths.get(i).intValue(); - } - } - } - name = nameBuilder.toString(); - } + /** + * This method parses the field name to determine how the field should be used. + * @param nameBuilder + * the name of the field (given as StringBuilder) + * @throws BlenderFileException + * this exception is thrown if the names contain errors + */ + private void parseField(StringBuilder nameBuilder) throws BlenderFileException { + this.removeWhitespaces(nameBuilder); + //veryfying if the name is a pointer + int pointerIndex = nameBuilder.indexOf("*"); + while (pointerIndex >= 0) { + ++pointerLevel; + nameBuilder.deleteCharAt(pointerIndex); + pointerIndex = nameBuilder.indexOf("*"); + } + //veryfying if the name is a function pointer + if (nameBuilder.indexOf("(") >= 0) { + function = true; + this.removeCharacter(nameBuilder, '('); + this.removeCharacter(nameBuilder, ')'); + } else { + //veryfying if the name is a table + int tableStartIndex = 0; + List lengths = new ArrayList(3);//3 dimensions will be enough in most cases + do { + tableStartIndex = nameBuilder.indexOf("["); + if (tableStartIndex > 0) { + int tableStopIndex = nameBuilder.indexOf("]"); + if (tableStopIndex < 0) { + throw new BlenderFileException("Invalid structure name: " + name); + } + try { + lengths.add(Integer.valueOf(nameBuilder.substring(tableStartIndex + 1, tableStopIndex))); + } catch (NumberFormatException e) { + throw new BlenderFileException("Invalid structure name caused by invalid table length: " + name, e); + } + nameBuilder.delete(tableStartIndex, tableStopIndex + 1); + } + } while (tableStartIndex > 0); + if (!lengths.isEmpty()) { + tableSizes = new int[lengths.size()]; + for (int i = 0; i < tableSizes.length; ++i) { + tableSizes[i] = lengths.get(i).intValue(); + } + } + } + name = nameBuilder.toString(); + } - /** - * This method removes the required character from the text. - * @param text - * the text we remove characters from - * @param toRemove - * the character to be removed - */ - private void removeCharacter(StringBuilder text, char toRemove) { - for(int i = 0; i < text.length(); ++i) { - if(text.charAt(i) == toRemove) { - text.deleteCharAt(i); - --i; - } - } - } + /** + * This method removes the required character from the text. + * @param text + * the text we remove characters from + * @param toRemove + * the character to be removed + */ + private void removeCharacter(StringBuilder text, char toRemove) { + for (int i = 0; i < text.length(); ++i) { + if (text.charAt(i) == toRemove) { + text.deleteCharAt(i); + --i; + } + } + } - /** - * This method removes all whitespaces from the text. - * @param text - * the text we remove whitespaces from - */ - private void removeWhitespaces(StringBuilder text) { - for(int i = 0; i < text.length(); ++i) { - if(Character.isWhitespace(text.charAt(i))) { - text.deleteCharAt(i); - --i; - } - } - } + /** + * This method removes all whitespaces from the text. + * @param text + * the text we remove whitespaces from + */ + private void removeWhitespaces(StringBuilder text) { + for (int i = 0; i < text.length(); ++i) { + if (Character.isWhitespace(text.charAt(i))) { + text.deleteCharAt(i); + --i; + } + } + } - @Override - public String toString() { - StringBuilder result = new StringBuilder(); - if(function) { - result.append('('); - } - for(int i = 0; i < pointerLevel; ++i) { - result.append('*'); - } - result.append(name); - if(tableSizes != null) { - for(int i = 0; i < tableSizes.length; ++i) { - result.append('[').append(tableSizes[i]).append(']'); - } - } - if(function) { - result.append(")()"); - } - //insert appropriate amount of spaces to format the output corrently - int nameLength = result.length(); - result.append(' ');//at least one space is a must - for(int i = 1; i < NAME_LENGTH - nameLength; ++i) {//we start from i=1 because one space is already added - result.append(' '); - } - result.append(type); - nameLength = result.length(); - for(int i = 0; i < NAME_LENGTH + TYPE_LENGTH - nameLength; ++i) { - result.append(' '); - } - if(value instanceof Character) { - result.append(" = ").append((int)((Character)value).charValue()); - } else { - result.append(" = ").append(value != null ? value.toString() : "null"); - } - return result.toString(); - } + @Override + public String toString() { + StringBuilder result = new StringBuilder(); + if (function) { + result.append('('); + } + for (int i = 0; i < pointerLevel; ++i) { + result.append('*'); + } + result.append(name); + if (tableSizes != null) { + for (int i = 0; i < tableSizes.length; ++i) { + result.append('[').append(tableSizes[i]).append(']'); + } + } + if (function) { + result.append(")()"); + } + //insert appropriate amount of spaces to format the output corrently + int nameLength = result.length(); + result.append(' ');//at least one space is a must + for (int i = 1; i < NAME_LENGTH - nameLength; ++i) {//we start from i=1 because one space is already added + result.append(' '); + } + result.append(type); + nameLength = result.length(); + for (int i = 0; i < NAME_LENGTH + TYPE_LENGTH - nameLength; ++i) { + result.append(' '); + } + if (value instanceof Character) { + result.append(" = ").append((int) ((Character) value).charValue()); + } else { + result.append(" = ").append(value != null ? value.toString() : "null"); + } + return result.toString(); + } } \ No newline at end of file diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/data/FileBlockHeader.java b/engine/src/blender/com/jme3/scene/plugins/blender/data/FileBlockHeader.java index b69f92661..cf6e5672c 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/data/FileBlockHeader.java +++ b/engine/src/blender/com/jme3/scene/plugins/blender/data/FileBlockHeader.java @@ -41,161 +41,160 @@ import com.jme3.scene.plugins.blender.utils.DataRepository; * @author Marcin Roguski */ public class FileBlockHeader { - public static final int BLOCK_TE00 = 'T' << 24 | 'E' << 16; //TE00 - public static final int BLOCK_ME00 = 'M' << 24 | 'E' << 16; //ME00 - public static final int BLOCK_SR00 = 'S' << 24 | 'R' << 16; //SR00 - public static final int BLOCK_CA00 = 'C' << 24 | 'A' << 16; //CA00 - public static final int BLOCK_LA00 = 'L' << 24 | 'A' << 16; //LA00 - public static final int BLOCK_OB00 = 'O' << 24 | 'B' << 16; //OB00 - public static final int BLOCK_MA00 = 'M' << 24 | 'A' << 16; //MA00 - public static final int BLOCK_SC00 = 'S' << 24 | 'C' << 16; //SC00 - public static final int BLOCK_WO00 = 'W' << 24 | 'O' << 16; //WO00 - public static final int BLOCK_TX00 = 'T' << 24 | 'X' << 16; //TX00 - public static final int BLOCK_IP00 = 'I' << 24 | 'P' << 16; //IP00 - public static final int BLOCK_AC00 = 'A' << 24 | 'C' << 16; //AC00 - public static final int BLOCK_GLOB = 'G' << 24 | 'L' << 16 | 'O' << 8 | 'B'; //GLOB - public static final int BLOCK_REND = 'R' << 24 | 'E' << 16 | 'N' << 8 | 'D'; //REND - public static final int BLOCK_DATA = 'D' << 24 | 'A' << 16 | 'T' << 8 | 'A'; //DATA - public static final int BLOCK_DNA1 = 'D' << 24 | 'N' << 16 | 'A' << 8 | '1'; //DNA1 - public static final int BLOCK_ENDB = 'E' << 24 | 'N' << 16 | 'D' << 8 | 'B'; //ENDB - - /** Identifier of the file-block [4 bytes]. */ - private int code; - /** Total length of the data after the file-block-header [4 bytes]. */ - private int size; - /** - * Memory address the structure was located when written to disk [4 or 8 bytes (defined in file header as a pointer - * size)]. - */ - private long oldMemoryAddress; - /** Index of the SDNA structure [4 bytes]. */ - private int sdnaIndex; - /** Number of structure located in this file-block [4 bytes]. */ - private int count; - /** Start position of the block's data in the stream. */ - private int blockPosition; - - /** - * Constructor. Loads the block header from the given stream during instance creation. - * @param inputStream - * the stream we read the block header from - * @param dataRepository - * the data repository - * @throws BlenderFileException - * this exception is thrown when the pointer size is neither 4 nor 8 - */ - public FileBlockHeader(BlenderInputStream inputStream, DataRepository dataRepository) throws BlenderFileException { - inputStream.alignPosition(4); - code = inputStream.readByte() << 24 | inputStream.readByte() << 16 | - inputStream.readByte() << 8 | inputStream.readByte(); - size = inputStream.readInt(); - oldMemoryAddress = inputStream.readPointer(); - sdnaIndex = inputStream.readInt(); - count = inputStream.readInt(); - blockPosition = inputStream.getPosition(); - if(FileBlockHeader.BLOCK_DNA1 == code) { - dataRepository.setBlockData(new DnaBlockData(inputStream, dataRepository)); - } else { - inputStream.setPosition(blockPosition + size); - dataRepository.addFileBlockHeader(Long.valueOf(oldMemoryAddress), this); - } - } - - /** - * This method returns the structure described by the header filled with appropriate data. - * @param dataRepository - * the data repository - * @return structure filled with data - * @throws BlenderFileException - */ - public Structure getStructure(DataRepository dataRepository) throws BlenderFileException { - dataRepository.getInputStream().setPosition(blockPosition); - Structure structure = dataRepository.getDnaBlockData().getStructure(sdnaIndex); - structure.fill(dataRepository.getInputStream()); - return structure; - } - - /** - * This method returns the code of this data block. - * @return the code of this data block - */ - public int getCode() { - return code; - } - - /** - * This method returns the size of the data stored in this block. - * @return the size of the data stored in this block - */ - public int getSize() { - return size; - } - - /** - * This method returns the memory address. - * @return the memory address - */ - public long getOldMemoryAddress() { - return oldMemoryAddress; - } - - /** - * This method returns the sdna index. - * @return the sdna index - */ - public int getSdnaIndex() { - return sdnaIndex; - } - - /** - * This data returns the number of structure stored in the data block after this header. - * @return the number of structure stored in the data block after this header - */ - public int getCount() { - return count; - } - - /** - * This method returns the start position of the data block in the blend file stream. - * @return the start position of the data block - */ - public int getBlockPosition() { - return blockPosition; - } - - /** - * This method indicates if the block is the last block in the file. - * @return true if this block is the last one in the file nad false otherwise - */ - public boolean isLastBlock() { - return FileBlockHeader.BLOCK_ENDB == code; - } - - /** - * This method indicates if the block is the SDNA block. - * @return true if this block is the SDNA block and false otherwise - */ - public boolean isDnaBlock() { - return FileBlockHeader.BLOCK_DNA1 == code; - } - - @Override - public String toString() { - return "FILE BLOCK HEADER [" + this.codeToString(code) + " : " + size + " : " + oldMemoryAddress + " : " + sdnaIndex + " : " + count + "]"; - } - - /** - * This method transforms the coded bloch id into a string value. - * @param code - * the id of the block - * @return the string value of the block id - */ - protected String codeToString(int code) { - char c1 = (char)((code & 0xFF000000) >> 24); - char c2 = (char)((code & 0xFF0000) >> 16); - char c3 = (char)((code & 0xFF00) >> 8); - char c4 = (char)(code & 0xFF); - return String.valueOf(c1) + c2 + c3 + c4; - } + public static final int BLOCK_TE00 = 'T' << 24 | 'E' << 16; //TE00 + public static final int BLOCK_ME00 = 'M' << 24 | 'E' << 16; //ME00 + public static final int BLOCK_SR00 = 'S' << 24 | 'R' << 16; //SR00 + public static final int BLOCK_CA00 = 'C' << 24 | 'A' << 16; //CA00 + public static final int BLOCK_LA00 = 'L' << 24 | 'A' << 16; //LA00 + public static final int BLOCK_OB00 = 'O' << 24 | 'B' << 16; //OB00 + public static final int BLOCK_MA00 = 'M' << 24 | 'A' << 16; //MA00 + public static final int BLOCK_SC00 = 'S' << 24 | 'C' << 16; //SC00 + public static final int BLOCK_WO00 = 'W' << 24 | 'O' << 16; //WO00 + public static final int BLOCK_TX00 = 'T' << 24 | 'X' << 16; //TX00 + public static final int BLOCK_IP00 = 'I' << 24 | 'P' << 16; //IP00 + public static final int BLOCK_AC00 = 'A' << 24 | 'C' << 16; //AC00 + public static final int BLOCK_GLOB = 'G' << 24 | 'L' << 16 | 'O' << 8 | 'B'; //GLOB + public static final int BLOCK_REND = 'R' << 24 | 'E' << 16 | 'N' << 8 | 'D'; //REND + public static final int BLOCK_DATA = 'D' << 24 | 'A' << 16 | 'T' << 8 | 'A'; //DATA + public static final int BLOCK_DNA1 = 'D' << 24 | 'N' << 16 | 'A' << 8 | '1'; //DNA1 + public static final int BLOCK_ENDB = 'E' << 24 | 'N' << 16 | 'D' << 8 | 'B'; //ENDB + /** Identifier of the file-block [4 bytes]. */ + private int code; + /** Total length of the data after the file-block-header [4 bytes]. */ + private int size; + /** + * Memory address the structure was located when written to disk [4 or 8 bytes (defined in file header as a pointer + * size)]. + */ + private long oldMemoryAddress; + /** Index of the SDNA structure [4 bytes]. */ + private int sdnaIndex; + /** Number of structure located in this file-block [4 bytes]. */ + private int count; + /** Start position of the block's data in the stream. */ + private int blockPosition; + + /** + * Constructor. Loads the block header from the given stream during instance creation. + * @param inputStream + * the stream we read the block header from + * @param dataRepository + * the data repository + * @throws BlenderFileException + * this exception is thrown when the pointer size is neither 4 nor 8 + */ + public FileBlockHeader(BlenderInputStream inputStream, DataRepository dataRepository) throws BlenderFileException { + inputStream.alignPosition(4); + code = inputStream.readByte() << 24 | inputStream.readByte() << 16 + | inputStream.readByte() << 8 | inputStream.readByte(); + size = inputStream.readInt(); + oldMemoryAddress = inputStream.readPointer(); + sdnaIndex = inputStream.readInt(); + count = inputStream.readInt(); + blockPosition = inputStream.getPosition(); + if (FileBlockHeader.BLOCK_DNA1 == code) { + dataRepository.setBlockData(new DnaBlockData(inputStream, dataRepository)); + } else { + inputStream.setPosition(blockPosition + size); + dataRepository.addFileBlockHeader(Long.valueOf(oldMemoryAddress), this); + } + } + + /** + * This method returns the structure described by the header filled with appropriate data. + * @param dataRepository + * the data repository + * @return structure filled with data + * @throws BlenderFileException + */ + public Structure getStructure(DataRepository dataRepository) throws BlenderFileException { + dataRepository.getInputStream().setPosition(blockPosition); + Structure structure = dataRepository.getDnaBlockData().getStructure(sdnaIndex); + structure.fill(dataRepository.getInputStream()); + return structure; + } + + /** + * This method returns the code of this data block. + * @return the code of this data block + */ + public int getCode() { + return code; + } + + /** + * This method returns the size of the data stored in this block. + * @return the size of the data stored in this block + */ + public int getSize() { + return size; + } + + /** + * This method returns the memory address. + * @return the memory address + */ + public long getOldMemoryAddress() { + return oldMemoryAddress; + } + + /** + * This method returns the sdna index. + * @return the sdna index + */ + public int getSdnaIndex() { + return sdnaIndex; + } + + /** + * This data returns the number of structure stored in the data block after this header. + * @return the number of structure stored in the data block after this header + */ + public int getCount() { + return count; + } + + /** + * This method returns the start position of the data block in the blend file stream. + * @return the start position of the data block + */ + public int getBlockPosition() { + return blockPosition; + } + + /** + * This method indicates if the block is the last block in the file. + * @return true if this block is the last one in the file nad false otherwise + */ + public boolean isLastBlock() { + return FileBlockHeader.BLOCK_ENDB == code; + } + + /** + * This method indicates if the block is the SDNA block. + * @return true if this block is the SDNA block and false otherwise + */ + public boolean isDnaBlock() { + return FileBlockHeader.BLOCK_DNA1 == code; + } + + @Override + public String toString() { + return "FILE BLOCK HEADER [" + this.codeToString(code) + " : " + size + " : " + oldMemoryAddress + " : " + sdnaIndex + " : " + count + "]"; + } + + /** + * This method transforms the coded bloch id into a string value. + * @param code + * the id of the block + * @return the string value of the block id + */ + protected String codeToString(int code) { + char c1 = (char) ((code & 0xFF000000) >> 24); + char c2 = (char) ((code & 0xFF0000) >> 16); + char c3 = (char) ((code & 0xFF00) >> 8); + char c4 = (char) (code & 0xFF); + return String.valueOf(c1) + c2 + c3 + c4; + } } diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/data/Structure.java b/engine/src/blender/com/jme3/scene/plugins/blender/data/Structure.java index 340a0183d..6e9aff18d 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/data/Structure.java +++ b/engine/src/blender/com/jme3/scene/plugins/blender/data/Structure.java @@ -46,266 +46,269 @@ import com.jme3.scene.plugins.blender.utils.Pointer; * @author Marcin Roguski */ public class Structure implements Cloneable { - /** The data repository. */ - private DataRepository dataRepository; - /** The address of the block that fills the structure. */ - private transient Long oldMemoryAddress; - /** The type of the structure. */ - private String type; - /** - * The fields of the structure. Each field consists of a pair: name-type. - */ - private Field[] fields; - /** - * Constructor that copies the data of the structure. - * @param structure - * the structure to copy. - * @param dataRepository - * the data repository of the structure - * @throws CloneNotSupportedException - * this exception should never be thrown - */ - private Structure(Structure structure, DataRepository dataRepository) throws CloneNotSupportedException { - type = structure.type; - fields = new Field[structure.fields.length]; - for(int i = 0; i < fields.length; ++i) { - fields[i] = (Field)structure.fields[i].clone(); - } - this.dataRepository = dataRepository; - this.oldMemoryAddress = structure.oldMemoryAddress; - } + /** The data repository. */ + private DataRepository dataRepository; + /** The address of the block that fills the structure. */ + private transient Long oldMemoryAddress; + /** The type of the structure. */ + private String type; + /** + * The fields of the structure. Each field consists of a pair: name-type. + */ + private Field[] fields; - /** - * Constructor. Loads the structure from the given stream during instance creation. - * @param inputStream - * the stream we read the structure from - * @param names - * the names from which the name of structure and its fields will be taken - * @param types - * the names of types for the structure - * @param dataRepository - * the data repository - * @throws BlenderFileException - * this exception occurs if the amount of fields, defined in the file, is negative - */ - public Structure(BlenderInputStream inputStream, String[] names, String[] types, DataRepository dataRepository) throws BlenderFileException { - int nameIndex = inputStream.readShort(); - type = types[nameIndex]; - this.dataRepository = dataRepository; - int fieldsAmount = inputStream.readShort(); - if(fieldsAmount < 0) { - throw new BlenderFileException("The amount of fields of " + this.type + " structure cannot be negative!"); - } - if(fieldsAmount > 0) { - fields = new Field[fieldsAmount]; - for(int i = 0; i < fieldsAmount; ++i) { - int typeIndex = inputStream.readShort(); - nameIndex = inputStream.readShort(); - fields[i] = new Field(names[nameIndex], types[typeIndex], dataRepository); - } - } - this.oldMemoryAddress = Long.valueOf(-1L); - } + /** + * Constructor that copies the data of the structure. + * @param structure + * the structure to copy. + * @param dataRepository + * the data repository of the structure + * @throws CloneNotSupportedException + * this exception should never be thrown + */ + private Structure(Structure structure, DataRepository dataRepository) throws CloneNotSupportedException { + type = structure.type; + fields = new Field[structure.fields.length]; + for (int i = 0; i < fields.length; ++i) { + fields[i] = (Field) structure.fields[i].clone(); + } + this.dataRepository = dataRepository; + this.oldMemoryAddress = structure.oldMemoryAddress; + } - /** - * This method fills the structure with data. - * @param inputStream - * the stream we read data from, its read cursor should be placed at the start position of the data for the - * structure - * @throws BlenderFileException - * an exception is thrown when the blend file is somehow invalid or corrupted - */ - public void fill(BlenderInputStream inputStream) throws BlenderFileException { - int position = inputStream.getPosition(); - inputStream.setPosition(position - 8 - inputStream.getPointerSize()); - this.oldMemoryAddress = Long.valueOf(inputStream.readPointer()); - inputStream.setPosition(position); - for(Field field : fields) { - field.fill(inputStream); - } - } + /** + * Constructor. Loads the structure from the given stream during instance creation. + * @param inputStream + * the stream we read the structure from + * @param names + * the names from which the name of structure and its fields will be taken + * @param types + * the names of types for the structure + * @param dataRepository + * the data repository + * @throws BlenderFileException + * this exception occurs if the amount of fields, defined in the file, is negative + */ + public Structure(BlenderInputStream inputStream, String[] names, String[] types, DataRepository dataRepository) throws BlenderFileException { + int nameIndex = inputStream.readShort(); + type = types[nameIndex]; + this.dataRepository = dataRepository; + int fieldsAmount = inputStream.readShort(); + if (fieldsAmount < 0) { + throw new BlenderFileException("The amount of fields of " + this.type + " structure cannot be negative!"); + } + if (fieldsAmount > 0) { + fields = new Field[fieldsAmount]; + for (int i = 0; i < fieldsAmount; ++i) { + int typeIndex = inputStream.readShort(); + nameIndex = inputStream.readShort(); + fields[i] = new Field(names[nameIndex], types[typeIndex], dataRepository); + } + } + this.oldMemoryAddress = Long.valueOf(-1L); + } - /** - * This method returns the value of the filed with a given name. - * @param fieldName - * the name of the field - * @return the value of the field or null if no field with a given name is found - */ - public Object getFieldValue(String fieldName) { - for(Field field : fields) { - if(field.name.equalsIgnoreCase(fieldName)) { - return field.value; - } - } - return null; - } + /** + * This method fills the structure with data. + * @param inputStream + * the stream we read data from, its read cursor should be placed at the start position of the data for the + * structure + * @throws BlenderFileException + * an exception is thrown when the blend file is somehow invalid or corrupted + */ + public void fill(BlenderInputStream inputStream) throws BlenderFileException { + int position = inputStream.getPosition(); + inputStream.setPosition(position - 8 - inputStream.getPointerSize()); + this.oldMemoryAddress = Long.valueOf(inputStream.readPointer()); + inputStream.setPosition(position); + for (Field field : fields) { + field.fill(inputStream); + } + } - /** - * This method returns the value of the filed with a given name. The structure is considered to have flat fields - * only (no substructures). - * @param fieldName - * the name of the field - * @return the value of the field or null if no field with a given name is found - */ - public Object getFlatFieldValue(String fieldName) { - for(Field field : fields) { - Object value = field.value; - if(field.name.equalsIgnoreCase(fieldName)) { - return value; - } else if(value instanceof Structure) { - value = ((Structure)value).getFlatFieldValue(fieldName); - if(value != null) {//we can compare references here, since we use one static object as a NULL field value - return value; - } - } - } - return null; - } + /** + * This method returns the value of the filed with a given name. + * @param fieldName + * the name of the field + * @return the value of the field or null if no field with a given name is found + */ + public Object getFieldValue(String fieldName) { + for (Field field : fields) { + if (field.name.equalsIgnoreCase(fieldName)) { + return field.value; + } + } + return null; + } - /** - * This methos should be used on structures that are of a 'ListBase' type. It creates a List of structures that are - * held by this structure within the blend file. - * @param dataRepository - * the data repository - * @return a list of filled structures - * @throws BlenderFileException - * this exception is thrown when the blend file structure is somehow invalid or corrupted - * @throws IllegalArgumentException - * this exception is thrown if the type of the structure is not 'ListBase' - */ - public List evaluateListBase(DataRepository dataRepository) throws BlenderFileException { - if(!"ListBase".equals(this.type)) { - throw new IllegalStateException("This structure is not of type: 'ListBase'"); - } - Pointer first = (Pointer)this.getFieldValue("first"); - Pointer last = (Pointer)this.getFieldValue("last"); - long currentAddress = 0; - long lastAddress = last.getOldMemoryAddress(); - List result = new LinkedList(); - while(currentAddress != lastAddress) { - currentAddress = first.getOldMemoryAddress(); - Structure structure = first.fetchData(dataRepository.getInputStream()).get(0); - result.add(structure); - first = (Pointer)structure.getFlatFieldValue("next"); - } - return result; - } + /** + * This method returns the value of the filed with a given name. The structure is considered to have flat fields + * only (no substructures). + * @param fieldName + * the name of the field + * @return the value of the field or null if no field with a given name is found + */ + public Object getFlatFieldValue(String fieldName) { + for (Field field : fields) { + Object value = field.value; + if (field.name.equalsIgnoreCase(fieldName)) { + return value; + } else if (value instanceof Structure) { + value = ((Structure) value).getFlatFieldValue(fieldName); + if (value != null) {//we can compare references here, since we use one static object as a NULL field value + return value; + } + } + } + return null; + } - /** - * This method returns the type of the structure. - * @return the type of the structure - */ - public String getType() { - return type; - } + /** + * This methos should be used on structures that are of a 'ListBase' type. It creates a List of structures that are + * held by this structure within the blend file. + * @param dataRepository + * the data repository + * @return a list of filled structures + * @throws BlenderFileException + * this exception is thrown when the blend file structure is somehow invalid or corrupted + * @throws IllegalArgumentException + * this exception is thrown if the type of the structure is not 'ListBase' + */ + public List evaluateListBase(DataRepository dataRepository) throws BlenderFileException { + if (!"ListBase".equals(this.type)) { + throw new IllegalStateException("This structure is not of type: 'ListBase'"); + } + Pointer first = (Pointer) this.getFieldValue("first"); + Pointer last = (Pointer) this.getFieldValue("last"); + long currentAddress = 0; + long lastAddress = last.getOldMemoryAddress(); + List result = new LinkedList(); + while (currentAddress != lastAddress) { + currentAddress = first.getOldMemoryAddress(); + Structure structure = first.fetchData(dataRepository.getInputStream()).get(0); + result.add(structure); + first = (Pointer) structure.getFlatFieldValue("next"); + } + return result; + } - /** - * This method returns the amount of fields for the current structure. - * @return the amount of fields for the current structure - */ - public int getFieldsAmount() { - return fields.length; - } + /** + * This method returns the type of the structure. + * @return the type of the structure + */ + public String getType() { + return type; + } - /** - * This method returns the field name of the given index. - * @param fieldIndex - * the index of the field - * @return the field name of the given index - */ - public String getFieldName(int fieldIndex) { - return fields[fieldIndex].name; - } + /** + * This method returns the amount of fields for the current structure. + * @return the amount of fields for the current structure + */ + public int getFieldsAmount() { + return fields.length; + } - /** - * This method returns the field type of the given index. - * @param fieldIndex - * the index of the field - * @return the field type of the given index - */ - public String getFieldType(int fieldIndex) { - return fields[fieldIndex].type; - } + /** + * This method returns the field name of the given index. + * @param fieldIndex + * the index of the field + * @return the field name of the given index + */ + public String getFieldName(int fieldIndex) { + return fields[fieldIndex].name; + } - /** - * This method returns the address of the structure. The strucutre should be filled with data otherwise an exception - * is thrown. - * @return the address of the feature stored in this structure - */ - public Long getOldMemoryAddress() { - if(oldMemoryAddress.longValue() == -1L) { - throw new IllegalStateException("Call the 'fill' method and fill the structure with data first!"); - } - return oldMemoryAddress; - } + /** + * This method returns the field type of the given index. + * @param fieldIndex + * the index of the field + * @return the field type of the given index + */ + public String getFieldType(int fieldIndex) { + return fields[fieldIndex].type; + } - /** - * This method returns the name of the structure. If the structure has an ID field then the name is returned. - * Otherwise the name does not exists and the method returns null. - * @return the name of the structure read from the ID field or null - */ - public String getName() { - Structure id = (Structure)this.getFieldValue("ID"); - return id == null ? null : id.getFieldValue("name").toString().substring(2);//blender adds 2-charactes as a name prefix - } + /** + * This method returns the address of the structure. The strucutre should be filled with data otherwise an exception + * is thrown. + * @return the address of the feature stored in this structure + */ + public Long getOldMemoryAddress() { + if (oldMemoryAddress.longValue() == -1L) { + throw new IllegalStateException("Call the 'fill' method and fill the structure with data first!"); + } + return oldMemoryAddress; + } - @Override - public String toString() { - StringBuilder result = new StringBuilder("struct ").append(type).append(" {\n"); - for(int i = 0; i < fields.length; ++i) { - result.append(fields[i].toString()).append('\n'); - } - return result.append('}').toString(); - } + /** + * This method returns the name of the structure. If the structure has an ID field then the name is returned. + * Otherwise the name does not exists and the method returns null. + * @return the name of the structure read from the ID field or null + */ + public String getName() { + Structure id = (Structure) this.getFieldValue("ID"); + return id == null ? null : id.getFieldValue("name").toString().substring(2);//blender adds 2-charactes as a name prefix + } - @Override - public Object clone() throws CloneNotSupportedException { - return new Structure(this, dataRepository); - } + @Override + public String toString() { + StringBuilder result = new StringBuilder("struct ").append(type).append(" {\n"); + for (int i = 0; i < fields.length; ++i) { + result.append(fields[i].toString()).append('\n'); + } + return result.append('}').toString(); + } - /** - * This enum enumerates all known data types that can be found in the blend file. - * @author Marcin Roguski - */ - /*package*/static enum DataType { - CHARACTER, SHORT, INTEGER, LONG, FLOAT, DOUBLE, VOID, STRUCTURE, POINTER; + @Override + public Object clone() throws CloneNotSupportedException { + return new Structure(this, dataRepository); + } - /** The map containing the known primary types. */ - private static final Map PRIMARY_TYPES = new HashMap(10); - static { - PRIMARY_TYPES.put("char", CHARACTER); - PRIMARY_TYPES.put("uchar", CHARACTER); - PRIMARY_TYPES.put("short", SHORT); - PRIMARY_TYPES.put("ushort", SHORT); - PRIMARY_TYPES.put("int", INTEGER); - PRIMARY_TYPES.put("long", LONG); - PRIMARY_TYPES.put("ulong", LONG); - PRIMARY_TYPES.put("float", FLOAT); - PRIMARY_TYPES.put("double", DOUBLE); - PRIMARY_TYPES.put("void", VOID); - } + /** + * This enum enumerates all known data types that can be found in the blend file. + * @author Marcin Roguski + */ + /*package*/ + static enum DataType { - /** - * This method returns the data type that is appropriate to the given type name. WARNING! The type recognition - * is case sensitive! - * @param type - * the type name of the data - * @param dataRepository - * the data repository - * @return appropriate enum value to the given type name - * @throws BlenderFileException - * this exception is thrown if the given type name does not exist in the blend file - */ - public static DataType getDataType(String type, DataRepository dataRepository) throws BlenderFileException { - DataType result = PRIMARY_TYPES.get(type); - if(result != null) { - return result; - } - if(dataRepository.getDnaBlockData().hasStructure(type)) { - return STRUCTURE; - } - throw new BlenderFileException("Unknown data type: " + type); - } - } + CHARACTER, SHORT, INTEGER, LONG, FLOAT, DOUBLE, VOID, STRUCTURE, POINTER; + /** The map containing the known primary types. */ + private static final Map PRIMARY_TYPES = new HashMap(10); + + static { + PRIMARY_TYPES.put("char", CHARACTER); + PRIMARY_TYPES.put("uchar", CHARACTER); + PRIMARY_TYPES.put("short", SHORT); + PRIMARY_TYPES.put("ushort", SHORT); + PRIMARY_TYPES.put("int", INTEGER); + PRIMARY_TYPES.put("long", LONG); + PRIMARY_TYPES.put("ulong", LONG); + PRIMARY_TYPES.put("float", FLOAT); + PRIMARY_TYPES.put("double", DOUBLE); + PRIMARY_TYPES.put("void", VOID); + } + + /** + * This method returns the data type that is appropriate to the given type name. WARNING! The type recognition + * is case sensitive! + * @param type + * the type name of the data + * @param dataRepository + * the data repository + * @return appropriate enum value to the given type name + * @throws BlenderFileException + * this exception is thrown if the given type name does not exist in the blend file + */ + public static DataType getDataType(String type, DataRepository dataRepository) throws BlenderFileException { + DataType result = PRIMARY_TYPES.get(type); + if (result != null) { + return result; + } + if (dataRepository.getDnaBlockData().hasStructure(type)) { + return STRUCTURE; + } + throw new BlenderFileException("Unknown data type: " + type); + } + } } diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/exception/BlenderFileException.java b/engine/src/blender/com/jme3/scene/plugins/blender/exception/BlenderFileException.java index 7b1b52267..378cedd1e 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/exception/BlenderFileException.java +++ b/engine/src/blender/com/jme3/scene/plugins/blender/exception/BlenderFileException.java @@ -36,39 +36,41 @@ package com.jme3.scene.plugins.blender.exception; * @author Marcin Roguski */ public class BlenderFileException extends Exception { - private static final long serialVersionUID = 7573482836437866767L; - /** - * Constructor. Creates an exception with no description. - */ - public BlenderFileException() {} + private static final long serialVersionUID = 7573482836437866767L; - /** - * Constructor. Creates an exception containing the given message. - * @param message - * the message describing the problem that occured - */ - public BlenderFileException(String message) { - super(message); - } + /** + * Constructor. Creates an exception with no description. + */ + public BlenderFileException() { + } - /** - * Constructor. Creates an exception that is based upon other thrown object. It contains the whole stacktrace then. - * @param throwable - * an exception/error that occured - */ - public BlenderFileException(Throwable throwable) { - super(throwable); - } + /** + * Constructor. Creates an exception containing the given message. + * @param message + * the message describing the problem that occured + */ + public BlenderFileException(String message) { + super(message); + } - /** - * Constructor. Creates an exception with both a message and stacktrace. - * @param message - * the message describing the problem that occured - * @param throwable - * an exception/error that occured - */ - public BlenderFileException(String message, Throwable throwable) { - super(message, throwable); - } + /** + * Constructor. Creates an exception that is based upon other thrown object. It contains the whole stacktrace then. + * @param throwable + * an exception/error that occured + */ + public BlenderFileException(Throwable throwable) { + super(throwable); + } + + /** + * Constructor. Creates an exception with both a message and stacktrace. + * @param message + * the message describing the problem that occured + * @param throwable + * an exception/error that occured + */ + public BlenderFileException(String message, Throwable throwable) { + super(message, throwable); + } } diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/helpers/ArmatureHelper.java b/engine/src/blender/com/jme3/scene/plugins/blender/helpers/ArmatureHelper.java index 3312784de..ec01a5126 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/helpers/ArmatureHelper.java +++ b/engine/src/blender/com/jme3/scene/plugins/blender/helpers/ArmatureHelper.java @@ -46,87 +46,87 @@ import com.jme3.scene.plugins.blender.utils.BlenderInputStream; import com.jme3.scene.plugins.blender.utils.DataRepository; import com.jme3.scene.plugins.blender.utils.Pointer; - /** * This class defines the methods to calculate certain aspects of animation and armature functionalities. * @author Marcin Roguski */ public class ArmatureHelper extends com.jme3.scene.plugins.blender.helpers.v249.ArmatureHelper { - private static final Logger LOGGER = Logger.getLogger(ArmatureHelper.class.getName()); - - /** - * This constructor parses the given blender version and stores the result. Some functionalities may differ in - * different blender versions. - * @param blenderVersion - * the version read from the blend file - */ - public ArmatureHelper(String blenderVersion) { - super(blenderVersion); - } - - @Override - public BoneTrack[] getTracks(Structure actionStructure, DataRepository dataRepository, String objectName, String animationName) throws BlenderFileException { - if(blenderVersion<250) { - return super.getTracks(actionStructure, dataRepository, objectName, animationName); - } - LOGGER.log(Level.INFO, "Getting tracks!"); - int fps = dataRepository.getBlenderKey().getFps(); - int[] animationFrames = dataRepository.getBlenderKey().getAnimationFrames(objectName, animationName); - Structure groups = (Structure)actionStructure.getFieldValue("groups"); - List actionGroups = groups.evaluateListBase(dataRepository);//bActionGroup - if(actionGroups != null && actionGroups.size() > 0 && (bonesMap == null || bonesMap.size() == 0)) { - throw new IllegalStateException("No bones found! Cannot proceed to calculating tracks!"); - } - - List tracks = new ArrayList(); - for(Structure actionGroup : actionGroups) { - String name = actionGroup.getFieldValue("name").toString(); - Integer boneIndex = bonesMap.get(name); - if(boneIndex != null) { - List channels = ((Structure)actionGroup.getFieldValue("channels")).evaluateListBase(dataRepository); - BezierCurve[] bezierCurves = new BezierCurve[channels.size()]; - int channelCounter = 0; - for(Structure c : channels) { - //reading rna path first - BlenderInputStream bis = dataRepository.getInputStream(); - int currentPosition = bis.getPosition(); - Pointer pRnaPath = (Pointer) c.getFieldValue("rna_path"); - FileBlockHeader dataFileBlock = dataRepository.getFileBlock(pRnaPath.getOldMemoryAddress()); - bis.setPosition(dataFileBlock.getBlockPosition()); - String rnaPath = bis.readString(); - bis.setPosition(currentPosition); - int arrayIndex = ((Number)c.getFieldValue("array_index")).intValue(); - int type = this.getCurveType(rnaPath, arrayIndex); - Pointer pBezTriple = (Pointer)c.getFieldValue("bezt"); - List bezTriples = pBezTriple.fetchData(dataRepository.getInputStream()); - bezierCurves[channelCounter++] = new BezierCurve(type, bezTriples, 2); - } + private static final Logger LOGGER = Logger.getLogger(ArmatureHelper.class.getName()); + + /** + * This constructor parses the given blender version and stores the result. Some functionalities may differ in + * different blender versions. + * @param blenderVersion + * the version read from the blend file + */ + public ArmatureHelper(String blenderVersion) { + super(blenderVersion); + } + + @Override + public BoneTrack[] getTracks(Structure actionStructure, DataRepository dataRepository, String objectName, String animationName) throws BlenderFileException { + if (blenderVersion < 250) { + return super.getTracks(actionStructure, dataRepository, objectName, animationName); + } + LOGGER.log(Level.INFO, "Getting tracks!"); + int fps = dataRepository.getBlenderKey().getFps(); + int[] animationFrames = dataRepository.getBlenderKey().getAnimationFrames(objectName, animationName); + Structure groups = (Structure) actionStructure.getFieldValue("groups"); + List actionGroups = groups.evaluateListBase(dataRepository);//bActionGroup + if (actionGroups != null && actionGroups.size() > 0 && (bonesMap == null || bonesMap.size() == 0)) { + throw new IllegalStateException("No bones found! Cannot proceed to calculating tracks!"); + } + + List tracks = new ArrayList(); + for (Structure actionGroup : actionGroups) { + String name = actionGroup.getFieldValue("name").toString(); + Integer boneIndex = bonesMap.get(name); + if (boneIndex != null) { + List channels = ((Structure) actionGroup.getFieldValue("channels")).evaluateListBase(dataRepository); + BezierCurve[] bezierCurves = new BezierCurve[channels.size()]; + int channelCounter = 0; + for (Structure c : channels) { + //reading rna path first + BlenderInputStream bis = dataRepository.getInputStream(); + int currentPosition = bis.getPosition(); + Pointer pRnaPath = (Pointer) c.getFieldValue("rna_path"); + FileBlockHeader dataFileBlock = dataRepository.getFileBlock(pRnaPath.getOldMemoryAddress()); + bis.setPosition(dataFileBlock.getBlockPosition()); + String rnaPath = bis.readString(); + bis.setPosition(currentPosition); + int arrayIndex = ((Number) c.getFieldValue("array_index")).intValue(); + int type = this.getCurveType(rnaPath, arrayIndex); + + Pointer pBezTriple = (Pointer) c.getFieldValue("bezt"); + List bezTriples = pBezTriple.fetchData(dataRepository.getInputStream()); + bezierCurves[channelCounter++] = new BezierCurve(type, bezTriples, 2); + } + + Ipo ipo = new Ipo(bezierCurves); + tracks.add(ipo.calculateTrack(boneIndex.intValue(), animationFrames[0], animationFrames[1], fps)); + } + } + return tracks.toArray(new BoneTrack[tracks.size()]); + } - Ipo ipo = new Ipo(bezierCurves); - tracks.add(ipo.calculateTrack(boneIndex.intValue(), animationFrames[0], animationFrames[1], fps)); - } - } - return tracks.toArray(new BoneTrack[tracks.size()]); - } - - /** - * This method parses the information stored inside the curve rna path and returns the proper type - * of the curve. - * @param rnaPath the curve's rna path - * @param arrayIndex the array index of the stored data - * @return the type of the curve - */ - protected int getCurveType(String rnaPath, int arrayIndex) { - if(rnaPath.endsWith(".location")) { - return Ipo.AC_LOC_X + arrayIndex; - } - if(rnaPath.endsWith(".rotation_quaternion")) { - return Ipo.AC_QUAT_W + arrayIndex; - } - if(rnaPath.endsWith(".scale")) { - return Ipo.AC_SIZE_X + arrayIndex; - } - throw new IllegalStateException("Unknown curve rna path: " + rnaPath); - } + /** + * This method parses the information stored inside the curve rna path and returns the proper type + * of the curve. + * @param rnaPath the curve's rna path + * @param arrayIndex the array index of the stored data + * @return the type of the curve + */ + protected int getCurveType(String rnaPath, int arrayIndex) { + if (rnaPath.endsWith(".location")) { + return Ipo.AC_LOC_X + arrayIndex; + } + if (rnaPath.endsWith(".rotation_quaternion")) { + return Ipo.AC_QUAT_W + arrayIndex; + } + if (rnaPath.endsWith(".scale")) { + return Ipo.AC_SIZE_X + arrayIndex; + } + throw new IllegalStateException("Unknown curve rna path: " + rnaPath); + } } diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/helpers/CameraHelper.java b/engine/src/blender/com/jme3/scene/plugins/blender/helpers/CameraHelper.java index 1576419c0..30829b079 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/helpers/CameraHelper.java +++ b/engine/src/blender/com/jme3/scene/plugins/blender/helpers/CameraHelper.java @@ -12,40 +12,41 @@ import com.jme3.scene.plugins.blender.exception.BlenderFileException; * @author Marcin Roguski */ public class CameraHelper extends com.jme3.scene.plugins.blender.helpers.v249.CameraHelper { - private static final Logger LOGGER = Logger.getLogger(CameraHelper.class.getName()); - - /** - * This constructor parses the given blender version and stores the result. Some functionalities may differ in - * different blender versions. - * @param blenderVersion - * the version read from the blend file - */ - public CameraHelper(String blenderVersion) { - super(blenderVersion); - } - - @Override - public Camera toCamera(Structure structure) throws BlenderFileException { - if(blenderVersion<250) { - return super.toCamera(structure); - } - Camera result = new Camera(DEFAULT_CAM_WIDTH, DEFAULT_CAM_HEIGHT); - int type = ((Number)structure.getFieldValue("type")).intValue(); - if(type != 0 && type != 1) { - LOGGER.log(Level.WARNING, "Unknown camera type: " + type + ". Perspective camera is being used!"); - type = 0; - } - //type==0 - perspective; type==1 - orthographic; perspective is used as default - result.setParallelProjection(type == 1); - float aspect = 0; - float clipsta = ((Number)structure.getFieldValue("clipsta")).floatValue(); - float clipend = ((Number)structure.getFieldValue("clipend")).floatValue(); - if(type == 0) { - aspect = ((Number)structure.getFieldValue("lens")).floatValue(); - } else { - aspect = ((Number)structure.getFieldValue("ortho_scale")).floatValue(); - } - result.setFrustumPerspective(45, aspect, clipsta, clipend); - return result; - } + + private static final Logger LOGGER = Logger.getLogger(CameraHelper.class.getName()); + + /** + * This constructor parses the given blender version and stores the result. Some functionalities may differ in + * different blender versions. + * @param blenderVersion + * the version read from the blend file + */ + public CameraHelper(String blenderVersion) { + super(blenderVersion); + } + + @Override + public Camera toCamera(Structure structure) throws BlenderFileException { + if (blenderVersion < 250) { + return super.toCamera(structure); + } + Camera result = new Camera(DEFAULT_CAM_WIDTH, DEFAULT_CAM_HEIGHT); + int type = ((Number) structure.getFieldValue("type")).intValue(); + if (type != 0 && type != 1) { + LOGGER.log(Level.WARNING, "Unknown camera type: {0}. Perspective camera is being used!", type); + type = 0; + } + //type==0 - perspective; type==1 - orthographic; perspective is used as default + result.setParallelProjection(type == 1); + float aspect = 0; + float clipsta = ((Number) structure.getFieldValue("clipsta")).floatValue(); + float clipend = ((Number) structure.getFieldValue("clipend")).floatValue(); + if (type == 0) { + aspect = ((Number) structure.getFieldValue("lens")).floatValue(); + } else { + aspect = ((Number) structure.getFieldValue("ortho_scale")).floatValue(); + } + result.setFrustumPerspective(45, aspect, clipsta, clipend); + return result; + } } diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/helpers/ConstraintHelper.java b/engine/src/blender/com/jme3/scene/plugins/blender/helpers/ConstraintHelper.java index 48473101c..d60da53ff 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/helpers/ConstraintHelper.java +++ b/engine/src/blender/com/jme3/scene/plugins/blender/helpers/ConstraintHelper.java @@ -11,27 +11,28 @@ import com.jme3.scene.plugins.blender.utils.DataRepository; * @author Marcin Roguski */ public class ConstraintHelper extends com.jme3.scene.plugins.blender.helpers.v249.ConstraintHelper { - private static final Logger LOGGER = Logger.getLogger(ConstraintHelper.class.getName()); - - /** - * Helper constructor. It's main task is to generate the affection functions. These functions are common to all - * ConstraintHelper instances. Unfortunately this constructor might grow large. If it becomes too large - I shall - * consider refactoring. The constructor parses the given blender version and stores the result. Some - * functionalities may differ in different blender versions. - * @param blenderVersion - * the version read from the blend file - */ - public ConstraintHelper(String blenderVersion, DataRepository dataRepository) { - super(blenderVersion, dataRepository); - } - - @Override - public void loadConstraints(Structure objectStructure, DataRepository dataRepository) throws BlenderFileException { - if(blenderVersion<250) { - super.loadConstraints(objectStructure, dataRepository); - } else { - LOGGER.warning("Loading of constraints not yet implemented for version 2.5x !"); - //TODO: to implement - } - } + + private static final Logger LOGGER = Logger.getLogger(ConstraintHelper.class.getName()); + + /** + * Helper constructor. It's main task is to generate the affection functions. These functions are common to all + * ConstraintHelper instances. Unfortunately this constructor might grow large. If it becomes too large - I shall + * consider refactoring. The constructor parses the given blender version and stores the result. Some + * functionalities may differ in different blender versions. + * @param blenderVersion + * the version read from the blend file + */ + public ConstraintHelper(String blenderVersion, DataRepository dataRepository) { + super(blenderVersion, dataRepository); + } + + @Override + public void loadConstraints(Structure objectStructure, DataRepository dataRepository) throws BlenderFileException { + if (blenderVersion < 250) { + super.loadConstraints(objectStructure, dataRepository); + } else { + LOGGER.warning("Loading of constraints not yet implemented for version 2.5x !"); + //TODO: to implement + } + } } diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/helpers/CurvesHelper.java b/engine/src/blender/com/jme3/scene/plugins/blender/helpers/CurvesHelper.java index 1f3f38d6f..cb4f42de9 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/helpers/CurvesHelper.java +++ b/engine/src/blender/com/jme3/scene/plugins/blender/helpers/CurvesHelper.java @@ -5,13 +5,14 @@ package com.jme3.scene.plugins.blender.helpers; * @author Marcin Roguski */ public class CurvesHelper extends com.jme3.scene.plugins.blender.helpers.v249.CurvesHelper { - /** - * This constructor parses the given blender version and stores the result. Some functionalities may differ in - * different blender versions. - * @param blenderVersion - * the version read from the blend file - */ - public CurvesHelper(String blenderVersion) { - super(blenderVersion); - } + + /** + * This constructor parses the given blender version and stores the result. Some functionalities may differ in + * different blender versions. + * @param blenderVersion + * the version read from the blend file + */ + public CurvesHelper(String blenderVersion) { + super(blenderVersion); + } } diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/helpers/IpoHelper.java b/engine/src/blender/com/jme3/scene/plugins/blender/helpers/IpoHelper.java index 65d5f6173..96ab48aa3 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/helpers/IpoHelper.java +++ b/engine/src/blender/com/jme3/scene/plugins/blender/helpers/IpoHelper.java @@ -6,13 +6,14 @@ package com.jme3.scene.plugins.blender.helpers; * @author Marcin Roguski */ public class IpoHelper extends com.jme3.scene.plugins.blender.helpers.v249.IpoHelper { - /** - * This constructor parses the given blender version and stores the result. Some functionalities may differ in - * different blender versions. - * @param blenderVersion - * the version read from the blend file - */ - public IpoHelper(String blenderVersion) { - super(blenderVersion); - } + + /** + * This constructor parses the given blender version and stores the result. Some functionalities may differ in + * different blender versions. + * @param blenderVersion + * the version read from the blend file + */ + public IpoHelper(String blenderVersion) { + super(blenderVersion); + } } diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/helpers/LightHelper.java b/engine/src/blender/com/jme3/scene/plugins/blender/helpers/LightHelper.java index fe11d1ccf..8cdcf5bdd 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/helpers/LightHelper.java +++ b/engine/src/blender/com/jme3/scene/plugins/blender/helpers/LightHelper.java @@ -36,13 +36,14 @@ package com.jme3.scene.plugins.blender.helpers; * @author Marcin Roguski */ public class LightHelper extends com.jme3.scene.plugins.blender.helpers.v249.LightHelper { - /** - * This constructor parses the given blender version and stores the result. Some functionalities may differ in - * different blender versions. - * @param blenderVersion - * the version read from the blend file - */ - public LightHelper(String blenderVersion) { - super(blenderVersion); - } + + /** + * This constructor parses the given blender version and stores the result. Some functionalities may differ in + * different blender versions. + * @param blenderVersion + * the version read from the blend file + */ + public LightHelper(String blenderVersion) { + super(blenderVersion); + } } diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/helpers/MaterialHelper.java b/engine/src/blender/com/jme3/scene/plugins/blender/helpers/MaterialHelper.java index a10590c5a..b2b117754 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/helpers/MaterialHelper.java +++ b/engine/src/blender/com/jme3/scene/plugins/blender/helpers/MaterialHelper.java @@ -32,13 +32,14 @@ package com.jme3.scene.plugins.blender.helpers; public class MaterialHelper extends com.jme3.scene.plugins.blender.helpers.v249.MaterialHelper { - /** - * This constructor parses the given blender version and stores the result. Some functionalities may differ in - * different blender versions. - * @param blenderVersion - * the version read from the blend file - */ - public MaterialHelper(String blenderVersion) { - super(blenderVersion); - } + + /** + * This constructor parses the given blender version and stores the result. Some functionalities may differ in + * different blender versions. + * @param blenderVersion + * the version read from the blend file + */ + public MaterialHelper(String blenderVersion) { + super(blenderVersion); + } } diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/helpers/MeshHelper.java b/engine/src/blender/com/jme3/scene/plugins/blender/helpers/MeshHelper.java index 904af02f4..58b2e04b7 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/helpers/MeshHelper.java +++ b/engine/src/blender/com/jme3/scene/plugins/blender/helpers/MeshHelper.java @@ -36,13 +36,14 @@ package com.jme3.scene.plugins.blender.helpers; * @author Marcin Roguski */ public class MeshHelper extends com.jme3.scene.plugins.blender.helpers.v249.MeshHelper { - /** - * This constructor parses the given blender version and stores the result. Some functionalities may differ in - * different blender versions. - * @param blenderVersion - * the version read from the blend file - */ - public MeshHelper(String blenderVersion) { - super(blenderVersion); - } + + /** + * This constructor parses the given blender version and stores the result. Some functionalities may differ in + * different blender versions. + * @param blenderVersion + * the version read from the blend file + */ + public MeshHelper(String blenderVersion) { + super(blenderVersion); + } } diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/helpers/ModifierHelper.java b/engine/src/blender/com/jme3/scene/plugins/blender/helpers/ModifierHelper.java index 4c39b9347..821c90790 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/helpers/ModifierHelper.java +++ b/engine/src/blender/com/jme3/scene/plugins/blender/helpers/ModifierHelper.java @@ -36,13 +36,14 @@ package com.jme3.scene.plugins.blender.helpers; * @author Marcin Roguski */ public class ModifierHelper extends com.jme3.scene.plugins.blender.helpers.v249.ModifierHelper { - /** - * This constructor parses the given blender version and stores the result. Some functionalities may differ in - * different blender versions. - * @param blenderVersion - * the version read from the blend file - */ - public ModifierHelper(String blenderVersion) { - super(blenderVersion); - } + + /** + * This constructor parses the given blender version and stores the result. Some functionalities may differ in + * different blender versions. + * @param blenderVersion + * the version read from the blend file + */ + public ModifierHelper(String blenderVersion) { + super(blenderVersion); + } } diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/helpers/NoiseHelper.java b/engine/src/blender/com/jme3/scene/plugins/blender/helpers/NoiseHelper.java index 640349928..babd416a6 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/helpers/NoiseHelper.java +++ b/engine/src/blender/com/jme3/scene/plugins/blender/helpers/NoiseHelper.java @@ -39,13 +39,14 @@ package com.jme3.scene.plugins.blender.helpers; * @author Marcin Roguski (Kaelthas) */ public class NoiseHelper extends com.jme3.scene.plugins.blender.helpers.v249.NoiseHelper { - /** - * Constructor. Stores the blender version number and loads the constants needed for computations. - * - * @param blenderVersion - * the number of blender version - */ - public NoiseHelper(String blenderVersion) { - super(blenderVersion); - } + + /** + * Constructor. Stores the blender version number and loads the constants needed for computations. + * + * @param blenderVersion + * the number of blender version + */ + public NoiseHelper(String blenderVersion) { + super(blenderVersion); + } } diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/helpers/ObjectHelper.java b/engine/src/blender/com/jme3/scene/plugins/blender/helpers/ObjectHelper.java index b469a9ed4..a735067c2 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/helpers/ObjectHelper.java +++ b/engine/src/blender/com/jme3/scene/plugins/blender/helpers/ObjectHelper.java @@ -36,13 +36,14 @@ package com.jme3.scene.plugins.blender.helpers; * @author Marcin Roguski */ public class ObjectHelper extends com.jme3.scene.plugins.blender.helpers.v249.ObjectHelper { - /** - * This constructor parses the given blender version and stores the result. Some functionalities may differ in - * different blender versions. - * @param blenderVersion - * the version read from the blend file - */ - public ObjectHelper(String blenderVersion) { - super(blenderVersion); - } + + /** + * This constructor parses the given blender version and stores the result. Some functionalities may differ in + * different blender versions. + * @param blenderVersion + * the version read from the blend file + */ + public ObjectHelper(String blenderVersion) { + super(blenderVersion); + } } diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/helpers/ParticlesHelper.java b/engine/src/blender/com/jme3/scene/plugins/blender/helpers/ParticlesHelper.java index c0c16121b..9993d757e 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/helpers/ParticlesHelper.java +++ b/engine/src/blender/com/jme3/scene/plugins/blender/helpers/ParticlesHelper.java @@ -5,13 +5,14 @@ package com.jme3.scene.plugins.blender.helpers; * @author Marcin Roguski (Kaelthas) */ public class ParticlesHelper extends com.jme3.scene.plugins.blender.helpers.v249.ParticlesHelper { - /** - * This constructor parses the given blender version and stores the result. Some functionalities may differ in - * different blender versions. - * @param blenderVersion - * the version read from the blend file - */ - public ParticlesHelper(String blenderVersion) { - super(blenderVersion); - } + + /** + * This constructor parses the given blender version and stores the result. Some functionalities may differ in + * different blender versions. + * @param blenderVersion + * the version read from the blend file + */ + public ParticlesHelper(String blenderVersion) { + super(blenderVersion); + } } diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/helpers/TextureHelper.java b/engine/src/blender/com/jme3/scene/plugins/blender/helpers/TextureHelper.java index 7f9c3992d..a29801bd9 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/helpers/TextureHelper.java +++ b/engine/src/blender/com/jme3/scene/plugins/blender/helpers/TextureHelper.java @@ -44,39 +44,41 @@ import com.jme3.texture.Texture; * @author Marcin Roguski */ public class TextureHelper extends com.jme3.scene.plugins.blender.helpers.v249.TextureHelper { - private static final Logger LOGGER = Logger.getLogger(TextureHelper.class.getName()); - public static final int TEX_POINTDENSITY = 14; - public static final int TEX_VOXELDATA = 15; - /** - * This constructor parses the given blender version and stores the result. Some functionalities may differ in - * different blender versions. - * @param blenderVersion - * the version read from the blend file - */ - public TextureHelper(String blenderVersion) { - super(blenderVersion); - } - - @Override - public Texture getTexture(Structure tex, DataRepository dataRepository) throws BlenderFileException { - if(blenderVersion<250) { - return super.getTexture(tex, dataRepository); - } - Texture result = (Texture) dataRepository.getLoadedFeature(tex.getOldMemoryAddress(), LoadedFeatureDataType.LOADED_FEATURE); - if (result != null) { - return result; - } - int type = ((Number)tex.getFieldValue("type")).intValue(); - switch(type) { - case TEX_POINTDENSITY: - LOGGER.warning("Point density texture loading currently not supported!"); - break; - case TEX_VOXELDATA: - LOGGER.warning("Voxel data texture loading currently not supported!"); - break; - default: - result = super.getTexture(tex, dataRepository); - } - return result; - } + + private static final Logger LOGGER = Logger.getLogger(TextureHelper.class.getName()); + public static final int TEX_POINTDENSITY = 14; + public static final int TEX_VOXELDATA = 15; + + /** + * This constructor parses the given blender version and stores the result. Some functionalities may differ in + * different blender versions. + * @param blenderVersion + * the version read from the blend file + */ + public TextureHelper(String blenderVersion) { + super(blenderVersion); + } + + @Override + public Texture getTexture(Structure tex, DataRepository dataRepository) throws BlenderFileException { + if (blenderVersion < 250) { + return super.getTexture(tex, dataRepository); + } + Texture result = (Texture) dataRepository.getLoadedFeature(tex.getOldMemoryAddress(), LoadedFeatureDataType.LOADED_FEATURE); + if (result != null) { + return result; + } + int type = ((Number) tex.getFieldValue("type")).intValue(); + switch (type) { + case TEX_POINTDENSITY: + LOGGER.warning("Point density texture loading currently not supported!"); + break; + case TEX_VOXELDATA: + LOGGER.warning("Voxel data texture loading currently not supported!"); + break; + default: + result = super.getTexture(tex, dataRepository); + } + return result; + } } diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/helpers/v249/ArmatureHelper.java b/engine/src/blender/com/jme3/scene/plugins/blender/helpers/v249/ArmatureHelper.java index 1118e6ba5..bec0324d8 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/helpers/v249/ArmatureHelper.java +++ b/engine/src/blender/com/jme3/scene/plugins/blender/helpers/v249/ArmatureHelper.java @@ -60,311 +60,312 @@ import com.jme3.scene.plugins.blender.utils.Pointer; * @author Marcin Roguski */ public class ArmatureHelper extends AbstractBlenderHelper { - private static final Logger LOGGER = Logger.getLogger(ArmatureHelper.class.getName()); - /** - * The map of the bones. Maps a bone name to its index in the armature. Should be cleared after the object had been - * read. TODO: probably bones can have identical names in different armatures - */ - protected Map bonesMap = new HashMap(); - /** A map of bones and their old memory addresses. */ - protected Map bonesOMAs = new HashMap(); - /** This list contains bones hierarchy and their matrices. It is later converted into jme bones. */ - protected List boneDataRoots = new ArrayList(); - - /** - * This constructor parses the given blender version and stores the result. Some functionalities may differ in - * different blender versions. - * @param blenderVersion - * the version read from the blend file - */ - public ArmatureHelper(String blenderVersion) { - super(blenderVersion); - } + private static final Logger LOGGER = Logger.getLogger(ArmatureHelper.class.getName()); + /** + * The map of the bones. Maps a bone name to its index in the armature. Should be cleared after the object had been + * read. TODO: probably bones can have identical names in different armatures + */ + protected Map bonesMap = new HashMap(); + /** A map of bones and their old memory addresses. */ + protected Map bonesOMAs = new HashMap(); + /** This list contains bones hierarchy and their matrices. It is later converted into jme bones. */ + protected List boneDataRoots = new ArrayList(); - /** - * This method returns the old memory address of a bone. If the bone does not exist in the blend file - zero is - * returned. - * @param bone - * the bone whose old memory address we seek - * @return the old memory address of the given bone - */ - public Long getBoneOMA(Bone bone) { - Long result = bonesOMAs.get(bone); - if(result == null) { - result = Long.valueOf(0); - } - return result; - } + /** + * This constructor parses the given blender version and stores the result. Some functionalities may differ in + * different blender versions. + * @param blenderVersion + * the version read from the blend file + */ + public ArmatureHelper(String blenderVersion) { + super(blenderVersion); + } - /** - * This method reads the bones and returns an empty skeleton. Bones should be assigned later. - * @param structure - * armature structure - * @param dataRepository - * the data repository - * @return an empty skeleton, bones are stored within the helper object - * @throws BlenderFileException - * this exception is thrown when the blender file is somehow corrupted - */ - public Skeleton toArmature(Structure structure, DataRepository dataRepository) throws BlenderFileException { - LOGGER.log(Level.INFO, "Converting structure to Armature!"); - Structure bonebase = (Structure)structure.getFieldValue("bonebase"); - List bonesStructures = bonebase.evaluateListBase(dataRepository); - for(Structure boneStructure : bonesStructures) { - BoneTransformationData rootBoneTransformationData = this.readBoneAndItsChildren(boneStructure, null, dataRepository); - boneDataRoots.add(rootBoneTransformationData); - } - return new Skeleton();//bones are assigned later - } + /** + * This method returns the old memory address of a bone. If the bone does not exist in the blend file - zero is + * returned. + * @param bone + * the bone whose old memory address we seek + * @return the old memory address of the given bone + */ + public Long getBoneOMA(Bone bone) { + Long result = bonesOMAs.get(bone); + if (result == null) { + result = Long.valueOf(0); + } + return result; + } - /** - * This method returns a map where the key is the object's group index that is used by a bone and the key is the - * bone index in the armature. - * @param poseStructure - * a bPose structure of the object - * @return bone group-to-index map - * @throws BlenderFileException - * this exception is thrown when the blender file is somehow corrupted - */ - public Map getGroupToBoneIndexMap(Structure defBaseStructure, DataRepository dataRepository) throws BlenderFileException { - Map result = null; - if(bonesMap != null && bonesMap.size() != 0) { - result = new HashMap(); - List deformGroups = defBaseStructure.evaluateListBase(dataRepository);//bDeformGroup - int groupIndex = 0;//TODO: consider many armatures attached to one object in the future !!! - for(Structure deformGroup : deformGroups) { - String deformGroupName = deformGroup.getFieldValue("name").toString(); - Integer boneIndex = bonesMap.get(deformGroupName); - if(boneIndex != null) { - result.put(Integer.valueOf(groupIndex), boneIndex); - } - ++groupIndex; - } - } - return result; - } + /** + * This method reads the bones and returns an empty skeleton. Bones should be assigned later. + * @param structure + * armature structure + * @param dataRepository + * the data repository + * @return an empty skeleton, bones are stored within the helper object + * @throws BlenderFileException + * this exception is thrown when the blender file is somehow corrupted + */ + public Skeleton toArmature(Structure structure, DataRepository dataRepository) throws BlenderFileException { + LOGGER.log(Level.INFO, "Converting structure to Armature!"); + Structure bonebase = (Structure) structure.getFieldValue("bonebase"); + List bonesStructures = bonebase.evaluateListBase(dataRepository); + for (Structure boneStructure : bonesStructures) { + BoneTransformationData rootBoneTransformationData = this.readBoneAndItsChildren(boneStructure, null, dataRepository); + boneDataRoots.add(rootBoneTransformationData); + } + return new Skeleton();//bones are assigned later + } - /** - * This method reads the tracks of the armature object. - * @param actionStructure - * @param dataRepository - * the data repository - * @param objectName - * the name of animation owner - * @param animationName - * the name of the animation - * @return a list of tracks for the armature - * @throws BlenderFileException - * this exception is thrown when the blender file is somehow corrupted - */ - public BoneTrack[] getTracks(Structure actionStructure, DataRepository dataRepository, String objectName, String animationName) throws BlenderFileException { - LOGGER.log(Level.INFO, "Getting tracks!"); - IpoHelper ipoHelper = dataRepository.getHelper(IpoHelper.class); - int fps = dataRepository.getBlenderKey().getFps(); - int[] animationFrames = dataRepository.getBlenderKey().getAnimationFrames(objectName, animationName); - Structure chanbase = (Structure)actionStructure.getFieldValue("chanbase"); - List actionChannels = chanbase.evaluateListBase(dataRepository);//bActionChannel - if(actionChannels != null && actionChannels.size() > 0 && (bonesMap == null || bonesMap.size() == 0)) { - throw new IllegalStateException("No bones found! Cannot proceed to calculating tracks!"); - } - List tracks = new ArrayList(); - for(Structure bActionChannel : actionChannels) { - String name = bActionChannel.getFieldValue("name").toString(); - Integer boneIndex = bonesMap.get(name); - if(boneIndex != null) { - Pointer p = (Pointer)bActionChannel.getFieldValue("ipo"); - if(!p.isNull()) { - Structure ipoStructure = p.fetchData(dataRepository.getInputStream()).get(0); - Ipo ipo = ipoHelper.createIpo(ipoStructure, dataRepository); - tracks.add(ipo.calculateTrack(boneIndex.intValue(), animationFrames[0], animationFrames[1], fps)); - } - } - } - return tracks.toArray(new BoneTrack[tracks.size()]); - } + /** + * This method returns a map where the key is the object's group index that is used by a bone and the key is the + * bone index in the armature. + * @param poseStructure + * a bPose structure of the object + * @return bone group-to-index map + * @throws BlenderFileException + * this exception is thrown when the blender file is somehow corrupted + */ + public Map getGroupToBoneIndexMap(Structure defBaseStructure, DataRepository dataRepository) throws BlenderFileException { + Map result = null; + if (bonesMap != null && bonesMap.size() != 0) { + result = new HashMap(); + List deformGroups = defBaseStructure.evaluateListBase(dataRepository);//bDeformGroup + int groupIndex = 0;//TODO: consider many armatures attached to one object in the future !!! + for (Structure deformGroup : deformGroups) { + String deformGroupName = deformGroup.getFieldValue("name").toString(); + Integer boneIndex = bonesMap.get(deformGroupName); + if (boneIndex != null) { + result.put(Integer.valueOf(groupIndex), boneIndex); + } + ++groupIndex; + } + } + return result; + } - /** - * This bone returns transformation matrix of the bone that is relative to - * its armature object. - * @param boneStructure the bone's structure - * @return bone's transformation matrix in armature space - */ - @SuppressWarnings("unchecked") - protected Matrix4f getArmatureMatrix(Structure boneStructure) { - DynamicArray boneMat = (DynamicArray)boneStructure.getFieldValue("arm_mat"); - Matrix4f m = new Matrix4f(); - for(int i = 0; i < 4; ++i) { - for(int j = 0; j < 4; ++j) { - m.set(i, j, boneMat.get(j, i).floatValue()); - } - } - return m; - } + /** + * This method reads the tracks of the armature object. + * @param actionStructure + * @param dataRepository + * the data repository + * @param objectName + * the name of animation owner + * @param animationName + * the name of the animation + * @return a list of tracks for the armature + * @throws BlenderFileException + * this exception is thrown when the blender file is somehow corrupted + */ + public BoneTrack[] getTracks(Structure actionStructure, DataRepository dataRepository, String objectName, String animationName) throws BlenderFileException { + LOGGER.log(Level.INFO, "Getting tracks!"); + IpoHelper ipoHelper = dataRepository.getHelper(IpoHelper.class); + int fps = dataRepository.getBlenderKey().getFps(); + int[] animationFrames = dataRepository.getBlenderKey().getAnimationFrames(objectName, animationName); + Structure chanbase = (Structure) actionStructure.getFieldValue("chanbase"); + List actionChannels = chanbase.evaluateListBase(dataRepository);//bActionChannel + if (actionChannels != null && actionChannels.size() > 0 && (bonesMap == null || bonesMap.size() == 0)) { + throw new IllegalStateException("No bones found! Cannot proceed to calculating tracks!"); + } + List tracks = new ArrayList(); + for (Structure bActionChannel : actionChannels) { + String name = bActionChannel.getFieldValue("name").toString(); + Integer boneIndex = bonesMap.get(name); + if (boneIndex != null) { + Pointer p = (Pointer) bActionChannel.getFieldValue("ipo"); + if (!p.isNull()) { + Structure ipoStructure = p.fetchData(dataRepository.getInputStream()).get(0); + Ipo ipo = ipoHelper.createIpo(ipoStructure, dataRepository); + tracks.add(ipo.calculateTrack(boneIndex.intValue(), animationFrames[0], animationFrames[1], fps)); + } + } + } + return tracks.toArray(new BoneTrack[tracks.size()]); + } - /** - * This method reads the bone with its children. - * @param boneStructure - * a structure containing the bone data - * @param parent - * the bone parent; if null then we read the root bone - * @param dataRepository - * the data repository - * @return the bone transformation data; contains bone chierarchy and the bone's matrices - * @throws BlenderFileException - * this exception is thrown when the blender file is somehow corrupted - */ - @SuppressWarnings("unchecked") - protected BoneTransformationData readBoneAndItsChildren(Structure boneStructure, BoneTransformationData parent, DataRepository dataRepository) throws BlenderFileException { - String name = boneStructure.getFieldValue("name").toString(); - Bone bone = new Bone(name); - int bonesAmount = bonesOMAs.size(); - bonesOMAs.put(bone, boneStructure.getOldMemoryAddress()); - if(bonesAmount == bonesOMAs.size()) { - throw new IllegalStateException("Two bones has the same hash value and thereforw a bone was overriden in the bones<->OMA map! Improve the hash algorithm!"); - } - Matrix4f boneArmatureMatrix = this.getArmatureMatrix(boneStructure); - DynamicArray sizeArray = (DynamicArray) boneStructure.getFieldValue("size"); - Vector3f size = new Vector3f(sizeArray.get(0), sizeArray.get(1), sizeArray.get(2)); - BoneTransformationData boneTransformationData = new BoneTransformationData(boneArmatureMatrix, size, bone, parent); - dataRepository.addLoadedFeatures(boneStructure.getOldMemoryAddress(), name, boneStructure, bone); + /** + * This bone returns transformation matrix of the bone that is relative to + * its armature object. + * @param boneStructure the bone's structure + * @return bone's transformation matrix in armature space + */ + @SuppressWarnings("unchecked") + protected Matrix4f getArmatureMatrix(Structure boneStructure) { + DynamicArray boneMat = (DynamicArray) boneStructure.getFieldValue("arm_mat"); + Matrix4f m = new Matrix4f(); + for (int i = 0; i < 4; ++i) { + for (int j = 0; j < 4; ++j) { + m.set(i, j, boneMat.get(j, i).floatValue()); + } + } + return m; + } - Structure childbase = (Structure)boneStructure.getFieldValue("childbase"); - List children = childbase.evaluateListBase(dataRepository);//Bone - for(Structure boneChild : children) { - this.readBoneAndItsChildren(boneChild, boneTransformationData, dataRepository); - } - return boneTransformationData; - } + /** + * This method reads the bone with its children. + * @param boneStructure + * a structure containing the bone data + * @param parent + * the bone parent; if null then we read the root bone + * @param dataRepository + * the data repository + * @return the bone transformation data; contains bone chierarchy and the bone's matrices + * @throws BlenderFileException + * this exception is thrown when the blender file is somehow corrupted + */ + @SuppressWarnings("unchecked") + protected BoneTransformationData readBoneAndItsChildren(Structure boneStructure, BoneTransformationData parent, DataRepository dataRepository) throws BlenderFileException { + String name = boneStructure.getFieldValue("name").toString(); + Bone bone = new Bone(name); + int bonesAmount = bonesOMAs.size(); + bonesOMAs.put(bone, boneStructure.getOldMemoryAddress()); + if (bonesAmount == bonesOMAs.size()) { + throw new IllegalStateException("Two bones has the same hash value and thereforw a bone was overriden in the bones<->OMA map! Improve the hash algorithm!"); + } + Matrix4f boneArmatureMatrix = this.getArmatureMatrix(boneStructure); + DynamicArray sizeArray = (DynamicArray) boneStructure.getFieldValue("size"); + Vector3f size = new Vector3f(sizeArray.get(0), sizeArray.get(1), sizeArray.get(2)); + BoneTransformationData boneTransformationData = new BoneTransformationData(boneArmatureMatrix, size, bone, parent); + dataRepository.addLoadedFeatures(boneStructure.getOldMemoryAddress(), name, boneStructure, bone); - /** - * This method assigns transformations to the bone. - * @param btd - * the bone data containing the bone we assign transformation to - * @param additionalRootBoneTransformation - * additional bone transformation which indicates it's mesh parent and armature object transformations - * @param boneList - * a list of all read bones - */ - protected void assignBonesMatrices(BoneTransformationData btd, Matrix4f additionalRootBoneTransformation, List boneList) { - LOGGER.info("[" + btd.bone.getName() + "] additionalRootBoneTransformation =\n" + additionalRootBoneTransformation); - Matrix4f totalInverseParentMatrix = btd.parent != null ? btd.parent.totalInverseBoneParentMatrix : Matrix4f.IDENTITY; - LOGGER.info("[" + btd.bone.getName() + "] totalInverseParentMatrix =\n" + totalInverseParentMatrix); - Matrix4f restMatrix = additionalRootBoneTransformation.mult(btd.boneArmatureMatrix); - LOGGER.info("[" + btd.bone.getName() + "] restMatrix =\n" + restMatrix); - btd.totalInverseBoneParentMatrix = restMatrix.clone().invert(); - restMatrix = totalInverseParentMatrix.mult(restMatrix); - LOGGER.info("[" + btd.bone.getName() + "] resultMatrix =\n" + restMatrix); - btd.bone.setBindTransforms(restMatrix.toTranslationVector(), restMatrix.toRotationQuat(), btd.size); - boneList.add(btd.bone); - bonesMap.put(btd.bone.getName(), Integer.valueOf(boneList.size() - 1)); - if(btd.children != null && btd.children.size() > 0) { - for(BoneTransformationData child : btd.children) { - this.assignBonesMatrices(child, additionalRootBoneTransformation, boneList); - btd.bone.addChild(child.bone); - } - } - } + Structure childbase = (Structure) boneStructure.getFieldValue("childbase"); + List children = childbase.evaluateListBase(dataRepository);//Bone + for (Structure boneChild : children) { + this.readBoneAndItsChildren(boneChild, boneTransformationData, dataRepository); + } + return boneTransformationData; + } - /** - * This method returns bone transformation data for the bone of a given name. - * @param boneName - * the name of the bone - * @return bone's transformation data - */ - public BoneTransformationData getBoneTransformationDataRoot(int index) { - return boneDataRoots.get(index); - } + /** + * This method assigns transformations to the bone. + * @param btd + * the bone data containing the bone we assign transformation to + * @param additionalRootBoneTransformation + * additional bone transformation which indicates it's mesh parent and armature object transformations + * @param boneList + * a list of all read bones + */ + protected void assignBonesMatrices(BoneTransformationData btd, Matrix4f additionalRootBoneTransformation, List boneList) { + LOGGER.info("[" + btd.bone.getName() + "] additionalRootBoneTransformation =\n" + additionalRootBoneTransformation); + Matrix4f totalInverseParentMatrix = btd.parent != null ? btd.parent.totalInverseBoneParentMatrix : Matrix4f.IDENTITY; + LOGGER.info("[" + btd.bone.getName() + "] totalInverseParentMatrix =\n" + totalInverseParentMatrix); + Matrix4f restMatrix = additionalRootBoneTransformation.mult(btd.boneArmatureMatrix); + LOGGER.info("[" + btd.bone.getName() + "] restMatrix =\n" + restMatrix); + btd.totalInverseBoneParentMatrix = restMatrix.clone().invert(); + restMatrix = totalInverseParentMatrix.mult(restMatrix); + LOGGER.info("[" + btd.bone.getName() + "] resultMatrix =\n" + restMatrix); + btd.bone.setBindTransforms(restMatrix.toTranslationVector(), restMatrix.toRotationQuat(), btd.size); + boneList.add(btd.bone); + bonesMap.put(btd.bone.getName(), Integer.valueOf(boneList.size() - 1)); + if (btd.children != null && btd.children.size() > 0) { + for (BoneTransformationData child : btd.children) { + this.assignBonesMatrices(child, additionalRootBoneTransformation, boneList); + btd.bone.addChild(child.bone); + } + } + } - /** - * This method returns the amount of bones transformations roots. - * @return the amount of bones transformations roots - */ - public int getBoneTransformationDataRootsSize() { - return boneDataRoots.size(); - } + /** + * This method returns bone transformation data for the bone of a given name. + * @param boneName + * the name of the bone + * @return bone's transformation data + */ + public BoneTransformationData getBoneTransformationDataRoot(int index) { + return boneDataRoots.get(index); + } - /** - * This class holds the data needed later for bone transformation calculation and to bind parent with children. - * @author Marcin Roguski - */ - private static class BoneTransformationData { - /** Inverse matrix of bone's parent bone. */ - private Matrix4f totalInverseBoneParentMatrix; - /** Bone's matrix in armature's space. */ - private Matrix4f boneArmatureMatrix; - /** Bone's size (apparently it is held outside the transformation matrix. */ - private Vector3f size; - /** The bone the data applies to. */ - private Bone bone; - /** The parent of the above mentioned bone (not assigned yet). */ - private BoneTransformationData parent; - /** The children of the current bone. */ - private List children; + /** + * This method returns the amount of bones transformations roots. + * @return the amount of bones transformations roots + */ + public int getBoneTransformationDataRootsSize() { + return boneDataRoots.size(); + } - /** - * Private constructor creates the object and assigns the given data. - * @param boneArmatureMatrix - * the matrix of the current bone - * @param size - * the bone's size - * @param bone - * the current bone - * @param parent - * the parent structure of the bone - */ - private BoneTransformationData(Matrix4f boneArmatureMatrix, Vector3f size, Bone bone, BoneTransformationData parent) { - this.boneArmatureMatrix = boneArmatureMatrix; - this.size = size; - this.bone = bone; - this.parent = parent; - this.children = new ArrayList(); - if(this.parent != null) { - this.parent.children.add(this); - } - } - } + /** + * This class holds the data needed later for bone transformation calculation and to bind parent with children. + * @author Marcin Roguski + */ + private static class BoneTransformationData { - /** - * This method creates the whole bones structure. Assignes transformations to bones and combines children with - * parents. - * @param armatureOMA - * old memory address of bones' armature object - * @param additionalRootBoneTransformation - * additional bone transformation which indicates it's mesh parent and armature object transformations - * @return - */ - public Bone[] buildBonesStructure(Long armatureOMA, Matrix4f additionalRootBoneTransformation) {//TODO: uwzględnić wiele szkieletów - List bones = new ArrayList(boneDataRoots.size() + 1); - bones.add(new Bone(null)); - for(BoneTransformationData btd : boneDataRoots) { - this.assignBonesMatrices(btd, additionalRootBoneTransformation, bones); - } - return bones.toArray(new Bone[bones.size()]); - } - - /** - * This method assigns an immovable bone to vertices that have no bone assigned. They have the bone index with the - * value -1. - * @param immovableBoneIndex - * the ondex of immovable bone - * @param meshes - * a list of meshes whose vertices will be assigned to immovable bone - */ - public void assignBoneToOrphanedVertices(byte immovableBoneIndex, Mesh[] meshes) { - //bone indices are common for all the object's meshes (vertex indices specify which are to be used) - VertexBuffer boneIndices = meshes[0].getBuffer(Type.BoneIndex);//common buffer to all the meshes - ByteBuffer data = (ByteBuffer)boneIndices.getData(); - for(int i = 0; i < boneIndices.getNumElements(); ++i) { - if(data.get(i) == -1) { - data.put(i, immovableBoneIndex); - } - } - } + /** Inverse matrix of bone's parent bone. */ + private Matrix4f totalInverseBoneParentMatrix; + /** Bone's matrix in armature's space. */ + private Matrix4f boneArmatureMatrix; + /** Bone's size (apparently it is held outside the transformation matrix. */ + private Vector3f size; + /** The bone the data applies to. */ + private Bone bone; + /** The parent of the above mentioned bone (not assigned yet). */ + private BoneTransformationData parent; + /** The children of the current bone. */ + private List children; - @Override - public void clearState() { - bonesMap.clear(); - boneDataRoots.clear(); - } + /** + * Private constructor creates the object and assigns the given data. + * @param boneArmatureMatrix + * the matrix of the current bone + * @param size + * the bone's size + * @param bone + * the current bone + * @param parent + * the parent structure of the bone + */ + private BoneTransformationData(Matrix4f boneArmatureMatrix, Vector3f size, Bone bone, BoneTransformationData parent) { + this.boneArmatureMatrix = boneArmatureMatrix; + this.size = size; + this.bone = bone; + this.parent = parent; + this.children = new ArrayList(); + if (this.parent != null) { + this.parent.children.add(this); + } + } + } + + /** + * This method creates the whole bones structure. Assignes transformations to bones and combines children with + * parents. + * @param armatureOMA + * old memory address of bones' armature object + * @param additionalRootBoneTransformation + * additional bone transformation which indicates it's mesh parent and armature object transformations + * @return + */ + public Bone[] buildBonesStructure(Long armatureOMA, Matrix4f additionalRootBoneTransformation) {//TODO: uwzględnić wiele szkieletów + List bones = new ArrayList(boneDataRoots.size() + 1); + bones.add(new Bone(null)); + for (BoneTransformationData btd : boneDataRoots) { + this.assignBonesMatrices(btd, additionalRootBoneTransformation, bones); + } + return bones.toArray(new Bone[bones.size()]); + } + + /** + * This method assigns an immovable bone to vertices that have no bone assigned. They have the bone index with the + * value -1. + * @param immovableBoneIndex + * the ondex of immovable bone + * @param meshes + * a list of meshes whose vertices will be assigned to immovable bone + */ + public void assignBoneToOrphanedVertices(byte immovableBoneIndex, Mesh[] meshes) { + //bone indices are common for all the object's meshes (vertex indices specify which are to be used) + VertexBuffer boneIndices = meshes[0].getBuffer(Type.BoneIndex);//common buffer to all the meshes + ByteBuffer data = (ByteBuffer) boneIndices.getData(); + for (int i = 0; i < boneIndices.getNumElements(); ++i) { + if (data.get(i) == -1) { + data.put(i, immovableBoneIndex); + } + } + } + + @Override + public void clearState() { + bonesMap.clear(); + boneDataRoots.clear(); + } } diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/helpers/v249/CameraHelper.java b/engine/src/blender/com/jme3/scene/plugins/blender/helpers/v249/CameraHelper.java index 04b00d1f2..25ceb3fd7 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/helpers/v249/CameraHelper.java +++ b/engine/src/blender/com/jme3/scene/plugins/blender/helpers/v249/CameraHelper.java @@ -13,45 +13,46 @@ import com.jme3.scene.plugins.blender.utils.AbstractBlenderHelper; * @author Marcin Roguski */ public class CameraHelper extends AbstractBlenderHelper { - private static final Logger LOGGER = Logger.getLogger(CameraHelper.class.getName()); - - protected static final int DEFAULT_CAM_WIDTH = 100; - protected static final int DEFAULT_CAM_HEIGHT = 100; - /** - * This constructor parses the given blender version and stores the result. Some functionalities may differ in - * different blender versions. - * @param blenderVersion - * the version read from the blend file - */ - public CameraHelper(String blenderVersion) { - super(blenderVersion); - } - /** - * This method reads the camera object. - * @param structure the structure containing the camera data - * @return the camera object - * @throws BlenderFileException - */ - public Camera toCamera(Structure structure) throws BlenderFileException { - Camera result = new Camera(DEFAULT_CAM_WIDTH, DEFAULT_CAM_HEIGHT); - int type = ((Number)structure.getFieldValue("type")).intValue(); - if(type != 0 && type != 1) { - LOGGER.log(Level.WARNING, "Unknown camera type: " + type + ". Perspective camera is being used!"); - type = 0; - } - //type==0 - perspective; type==1 - orthographic; perspective is used as default - result.setParallelProjection(type == 1); - float angle = ((Number)structure.getFieldValue("angle")).floatValue(); - float aspect = 0; - float clipsta = ((Number)structure.getFieldValue("clipsta")).floatValue(); - float clipend = ((Number)structure.getFieldValue("clipend")).floatValue(); - if(type == 0) { - aspect = ((Number)structure.getFieldValue("lens")).floatValue(); - } else { - aspect = ((Number)structure.getFieldValue("ortho_scale")).floatValue(); - } - result.setFrustumPerspective(angle, aspect, clipsta, clipend); - return result; - } + private static final Logger LOGGER = Logger.getLogger(CameraHelper.class.getName()); + protected static final int DEFAULT_CAM_WIDTH = 100; + protected static final int DEFAULT_CAM_HEIGHT = 100; + + /** + * This constructor parses the given blender version and stores the result. Some functionalities may differ in + * different blender versions. + * @param blenderVersion + * the version read from the blend file + */ + public CameraHelper(String blenderVersion) { + super(blenderVersion); + } + + /** + * This method reads the camera object. + * @param structure the structure containing the camera data + * @return the camera object + * @throws BlenderFileException + */ + public Camera toCamera(Structure structure) throws BlenderFileException { + Camera result = new Camera(DEFAULT_CAM_WIDTH, DEFAULT_CAM_HEIGHT); + int type = ((Number) structure.getFieldValue("type")).intValue(); + if (type != 0 && type != 1) { + LOGGER.log(Level.WARNING, "Unknown camera type: {0}. Perspective camera is being used!", type); + type = 0; + } + //type==0 - perspective; type==1 - orthographic; perspective is used as default + result.setParallelProjection(type == 1); + float angle = ((Number) structure.getFieldValue("angle")).floatValue(); + float aspect = 0; + float clipsta = ((Number) structure.getFieldValue("clipsta")).floatValue(); + float clipend = ((Number) structure.getFieldValue("clipend")).floatValue(); + if (type == 0) { + aspect = ((Number) structure.getFieldValue("lens")).floatValue(); + } else { + aspect = ((Number) structure.getFieldValue("ortho_scale")).floatValue(); + } + result.setFrustumPerspective(angle, aspect, clipsta, clipend); + return result; + } } diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/helpers/v249/ConstraintHelper.java b/engine/src/blender/com/jme3/scene/plugins/blender/helpers/v249/ConstraintHelper.java index 2792b0fe4..6e4a54757 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/helpers/v249/ConstraintHelper.java +++ b/engine/src/blender/com/jme3/scene/plugins/blender/helpers/v249/ConstraintHelper.java @@ -24,711 +24,732 @@ import com.jme3.scene.plugins.blender.structures.Ipo; import com.jme3.scene.plugins.blender.utils.AbstractBlenderHelper; import com.jme3.scene.plugins.blender.utils.DataRepository; import com.jme3.scene.plugins.blender.utils.Pointer; +import java.util.logging.Level; /** * This class should be used for constraint calculations. * @author Marcin Roguski */ public class ConstraintHelper extends AbstractBlenderHelper { - /** - * A table containing implementations of influence functions for constraints. It should contain functions for - * blender at least 249 and higher. - */ - protected static AbstractInfluenceFunction[] influenceFunctions; - - /** - * Constraints stored for object with the given old memory address. - */ - protected Map constraints = new HashMap(); - - /** - * Helper constructor. It's main task is to generate the affection functions. These functions are common to all - * ConstraintHelper instances. Unfortunately this constructor might grow large. If it becomes too large - I shall - * consider refactoring. The constructor parses the given blender version and stores the result. Some - * functionalities may differ in different blender versions. - * @param blenderVersion - * the version read from the blend file - */ - public ConstraintHelper(String blenderVersion, DataRepository dataRepository) { - super(blenderVersion); - if(influenceFunctions == null) { - //TODO: synchronization - influenceFunctions = new AbstractInfluenceFunction[ConstraintType.getLastDefinedTypeValue() + 1]; - //ACTION constraint (TODO: to implement) - influenceFunctions[ConstraintType.CONSTRAINT_TYPE_ACTION.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_ACTION, dataRepository) {}; - - //CHILDOF constraint (TODO: to implement) - influenceFunctions[ConstraintType.CONSTRAINT_TYPE_CHILDOF.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_CHILDOF, dataRepository) {}; - - //CLAMPTO constraint - influenceFunctions[ConstraintType.CONSTRAINT_TYPE_CLAMPTO.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_CLAMPTO, dataRepository) { - @Override - public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation, Constraint constraint) { - this.validateConstraintType(constraint.getData()); - LOGGER.info(constraint.getName() + " not active! Curves not yet implemented!");//TODO: implement when curves are implemented - } - }; - - //DISTLIMIT constraint - influenceFunctions[ConstraintType.CONSTRAINT_TYPE_DISTLIMIT.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_DISTLIMIT, dataRepository) { - @Override - public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation, Constraint constraint) { - Structure constraintStructure = constraint.getData(); - this.validateConstraintType(constraintStructure); - Vector3f targetLocation = this.getTargetLocation(constraint); - BoneTrack boneTrack = this.getBoneTrack(skeleton, boneAnimation, constraint); - if(boneTrack != null) { - //TODO: target vertex group !!! - float dist = ((Number)constraintStructure.getFieldValue("dist")).floatValue(); - int mode = ((Number)constraintStructure.getFieldValue("mode")).intValue(); - - int maxFrames = boneTrack.getTimes().length; - Vector3f[] translations = boneTrack.getTranslations(); - for(int frame = 0; frame < maxFrames; ++frame) { - Vector3f v = translations[frame].subtract(targetLocation); - float currentDistance = v.length(); - float influence = constraint.getIpo().calculateValue(frame); - float modifier = 0.0f; - switch(mode) { - case LIMITDIST_INSIDE: - if(currentDistance >= dist) { - modifier = (dist - currentDistance) / currentDistance; - } - break; - case LIMITDIST_ONSURFACE: - modifier = (dist - currentDistance) / currentDistance; - break; - case LIMITDIST_OUTSIDE: - if(currentDistance <= dist) { - modifier = (dist - currentDistance) / currentDistance; - } - break; - default: - throw new IllegalStateException("Unknown distance limit constraint mode: " + mode); - } - translations[frame].addLocal(v.multLocal(modifier * influence)); - } - boneTrack.setKeyframes(boneTrack.getTimes(), translations, boneTrack.getRotations(), boneTrack.getScales()); - } - } - }; - - //FOLLOWPATH constraint - influenceFunctions[ConstraintType.CONSTRAINT_TYPE_FOLLOWPATH.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_FOLLOWPATH, dataRepository) { - @Override - public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation, Constraint constraint) { - this.validateConstraintType(constraint.getData()); - LOGGER.info(constraint.getName() + " not active! Curves not yet implemented!");//TODO: implement when curves are implemented - } - }; - - //KINEMATIC constraint (TODO: to implement) - influenceFunctions[ConstraintType.CONSTRAINT_TYPE_KINEMATIC.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_KINEMATIC, dataRepository) { - @Override - public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation, Constraint constraint) { - Structure constraintStructure = constraint.getData(); - this.validateConstraintType(constraintStructure); - /*Long boneOMA = constraint.getBoneOMA(); - //IK solver is only attached to bones - Bone ownerBone = (Bone)dataRepository.getLoadedFeature(boneOMA, LoadedFeatureDataType.LOADED_FEATURE); - - //get the target point - Object targetObject = this.getTarget(constraint, LoadedFeatureDataType.LOADED_FEATURE); - Vector3f pt = null;//Point Target - if(targetObject instanceof Bone) { - pt = ((Bone)targetObject).getModelSpacePosition(); - } else if(targetObject instanceof Node) { - pt = ((Node)targetObject).getWorldTranslation(); - } else if(targetObject instanceof Skeleton) { - Structure armatureNodeStructure = (Structure)this.getTarget(constraint, LoadedFeatureDataType.LOADED_STRUCTURE); - ObjectHelper objectHelper = dataRepository.getHelper(ObjectHelper.class); - Transform transform = objectHelper.getTransformation(armatureNodeStructure); - pt = transform.getTranslation(); - } else { - throw new IllegalStateException("Unknown target object type! Should be Node, Bone or Skeleton and there is: " + targetObject.getClass().getName()); - } - //preparing data - int maxIterations = ((Number)constraintStructure.getFieldValue("iterations")).intValue(); - CalculationBone[] bones = this.getBonesToCalculate(ownerBone, skeleton, boneAnimation); - for(int i=0;i= IK_SOLVER_ERROR && iteration <= maxIterations) { - //rotating the bones - for(int i = 0; i < bones.length - 1; ++i) { - Vector3f pe = bones[i].getEndPoint(); - Vector3f pc = bones[i + 1].getWorldTranslation().clone(); - - Vector3f peSUBpc = pe.subtract(pc).normalizeLocal(); - Vector3f ptSUBpc = pt.subtract(pc).normalizeLocal(); - - float theta = FastMath.acos(peSUBpc.dot(ptSUBpc)); - Vector3f direction = peSUBpc.cross(ptSUBpc).normalizeLocal(); - bones[i].rotate(rotation.fromAngleAxis(theta, direction), frame); - } - error = pt.subtract(bones[0].getEndPoint()).length(); - ++iteration; - } - System.out.println("error = " + error + " iterations = " + iteration); - } - - for(CalculationBone bone : bones) { - bone.applyCalculatedTracks(); - } - - System.out.println("&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&"); - for(int i=0;i bonesList = new ArrayList(); - Bone currentBone = bone; - do { - int boneIndex = skeleton.getBoneIndex(currentBone); - for(int i = 0; i < boneAnimation.getTracks().length; ++i) { - if(boneAnimation.getTracks()[i].getTargetBoneIndex() == boneIndex) { - bonesList.add(new CalculationBone(currentBone, boneAnimation.getTracks()[i])); - break; - } - } - currentBone = currentBone.getParent(); - } while(currentBone != null); - //attaching children - CalculationBone[] result = bonesList.toArray(new CalculationBone[bonesList.size()]); - for(int i = result.length - 1; i > 0; --i) { - result[i].attachChild(result[i - 1]); - } - return result; - } - }; - - //LOCKTRACK constraint (TODO: to implement) - influenceFunctions[ConstraintType.CONSTRAINT_TYPE_LOCKTRACK.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_LOCKTRACK, dataRepository) {}; - - //LOCLIKE constraint - influenceFunctions[ConstraintType.CONSTRAINT_TYPE_LOCLIKE.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_LOCLIKE, dataRepository) { - @Override - public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation, Constraint constraint) { - Structure constraintData = constraint.getData(); - this.validateConstraintType(constraintData); - BoneTrack boneTrack = this.getBoneTrack(skeleton, boneAnimation, constraint); - if(boneTrack != null) { - Vector3f targetLocation = this.getTargetLocation(constraint); - int flag = ((Number)constraintData.getFieldValue("flag")).intValue(); - Vector3f[] translations = boneTrack.getTranslations(); - int maxFrames = translations.length; - for(int frame = 0; frame < maxFrames; ++frame) { - Vector3f offset = Vector3f.ZERO; - if((flag & LOCLIKE_OFFSET) != 0) {//we add the original location to the copied location - offset = translations[frame].clone(); - } - - if((flag & LOCLIKE_X) != 0) { - translations[frame].x = targetLocation.x; - if((flag & LOCLIKE_X_INVERT) != 0) { - translations[frame].x = -translations[frame].x; - } - } else if((flag & LOCLIKE_Y) != 0) { - translations[frame].y = targetLocation.y; - if((flag & LOCLIKE_Y_INVERT) != 0) { - translations[frame].y = -translations[frame].y; - } - } else if((flag & LOCLIKE_Z) != 0) { - translations[frame].z = targetLocation.z; - if((flag & LOCLIKE_Z_INVERT) != 0) { - translations[frame].z = -translations[frame].z; - } - } - translations[frame].addLocal(offset);//TODO: ipo influence - } - boneTrack.setKeyframes(boneTrack.getTimes(), translations, boneTrack.getRotations(), boneTrack.getScales()); - } - } - }; - - //LOCLIMIT constraint - influenceFunctions[ConstraintType.CONSTRAINT_TYPE_LOCLIMIT.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_LOCLIMIT, dataRepository) { - @Override - public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation, Constraint constraint) { - Structure constraintStructure = constraint.getData(); - this.validateConstraintType(constraintStructure); - BoneTrack boneTrack = this.getBoneTrack(skeleton, boneAnimation, constraint); - if(boneTrack != null) { - int flag = ((Number)constraintStructure.getFieldValue("flag")).intValue(); - Vector3f[] translations = boneTrack.getTranslations(); - int maxFrames = translations.length; - for(int frame = 0; frame < maxFrames; ++frame) { - float influence = constraint.getIpo().calculateValue(frame); - if((flag & LIMIT_XMIN) != 0) { - float xmin = ((Number)constraintStructure.getFieldValue("xmin")).floatValue(); - if(translations[frame].x < xmin) { - translations[frame].x -= (translations[frame].x - xmin) * influence; - } - } - if((flag & LIMIT_XMAX) != 0) { - float xmax = ((Number)constraintStructure.getFieldValue("xmax")).floatValue(); - if(translations[frame].x > xmax) { - translations[frame].x -= (translations[frame].x - xmax) * influence; - } - } - if((flag & LIMIT_YMIN) != 0) { - float ymin = ((Number)constraintStructure.getFieldValue("ymin")).floatValue(); - if(translations[frame].y < ymin) { - translations[frame].y -= (translations[frame].y - ymin) * influence; - } - } - if((flag & LIMIT_YMAX) != 0) { - float ymax = ((Number)constraintStructure.getFieldValue("ymax")).floatValue(); - if(translations[frame].y > ymax) { - translations[frame].y -= (translations[frame].y - ymax) * influence; - } - } - if((flag & LIMIT_ZMIN) != 0) { - float zmin = ((Number)constraintStructure.getFieldValue("zmin")).floatValue(); - if(translations[frame].z < zmin) { - translations[frame].z -= (translations[frame].z - zmin) * influence; - } - } - if((flag & LIMIT_ZMAX) != 0) { - float zmax = ((Number)constraintStructure.getFieldValue("zmax")).floatValue(); - if(translations[frame].z > zmax) { - translations[frame].z -= (translations[frame].z - zmax) * influence; - } - }//TODO: consider constraint space !!! - } - boneTrack.setKeyframes(boneTrack.getTimes(), translations, boneTrack.getRotations(), boneTrack.getScales()); - } - } - }; - - //MINMAX constraint (TODO: to implement) - influenceFunctions[ConstraintType.CONSTRAINT_TYPE_MINMAX.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_MINMAX, dataRepository) {}; - - //NULL constraint - does nothing - influenceFunctions[ConstraintType.CONSTRAINT_TYPE_NULL.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_NULL, dataRepository) {}; - - //PYTHON constraint (TODO: to implement) - influenceFunctions[ConstraintType.CONSTRAINT_TYPE_PYTHON.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_PYTHON, dataRepository) {}; - - //RIGIDBODYJOINT constraint (TODO: to implement) - influenceFunctions[ConstraintType.CONSTRAINT_TYPE_RIGIDBODYJOINT.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_RIGIDBODYJOINT, dataRepository) {}; - - //ROTLIKE constraint - influenceFunctions[ConstraintType.CONSTRAINT_TYPE_ROTLIKE.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_ROTLIKE, dataRepository) { - @Override - public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation, Constraint constraint) { - Structure constraintData = constraint.getData(); - this.validateConstraintType(constraintData); - BoneTrack boneTrack = this.getBoneTrack(skeleton, boneAnimation, constraint); - if(boneTrack != null) { - Quaternion targetRotation = this.getTargetRotation(constraint); - int flag = ((Number)constraintData.getFieldValue("flag")).intValue(); - float[] targetAngles = targetRotation.toAngles(null); - Quaternion[] rotations = boneTrack.getRotations(); - int maxFrames = rotations.length; - for(int frame = 0; frame < maxFrames; ++frame) { - float[] angles = rotations[frame].toAngles(null); - - Quaternion offset = Quaternion.IDENTITY; - if((flag & ROTLIKE_OFFSET) != 0) {//we add the original rotation to the copied rotation - offset = rotations[frame].clone(); - } - - if((flag & ROTLIKE_X) != 0) { - angles[0] = targetAngles[0]; - if((flag & ROTLIKE_X_INVERT) != 0) { - angles[0] = -angles[0]; - } - } else if((flag & ROTLIKE_Y) != 0) { - angles[1] = targetAngles[1]; - if((flag & ROTLIKE_Y_INVERT) != 0) { - angles[1] = -angles[1]; - } - } else if((flag & ROTLIKE_Z) != 0) { - angles[2] = targetAngles[2]; - if((flag & ROTLIKE_Z_INVERT) != 0) { - angles[2] = -angles[2]; - } - } - rotations[frame].fromAngles(angles).multLocal(offset);//TODO: ipo influence - } - boneTrack.setKeyframes(boneTrack.getTimes(), boneTrack.getTranslations(), rotations, boneTrack.getScales()); - } - } - }; - - //ROTLIMIT constraint - influenceFunctions[ConstraintType.CONSTRAINT_TYPE_ROTLIMIT.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_ROTLIMIT, dataRepository) { - @Override - public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation, Constraint constraint) { - Structure constraintStructure = constraint.getData(); - this.validateConstraintType(constraintStructure); - BoneTrack boneTrack = this.getBoneTrack(skeleton, boneAnimation, constraint); - if(boneTrack != null) { - int flag = ((Number)constraintStructure.getFieldValue("flag")).intValue(); - Quaternion[] rotations = boneTrack.getRotations(); - int maxFrames = rotations.length; - for(int frame = 0; frame < maxFrames; ++frame) { - float[] angles = rotations[frame].toAngles(null); - float influence = constraint.getIpo().calculateValue(frame); - if((flag & LIMIT_XROT) != 0) { - float xmin = ((Number)constraintStructure.getFieldValue("xmin")).floatValue() * FastMath.DEG_TO_RAD; - float xmax = ((Number)constraintStructure.getFieldValue("xmax")).floatValue() * FastMath.DEG_TO_RAD; - float difference = 0.0f; - if(angles[0] < xmin) { - difference = (angles[0] - xmin) * influence; - } else if(angles[0] > xmax) { - difference = (angles[0] - xmax) * influence; - } - angles[0] -= difference; - } - if((flag & LIMIT_YROT) != 0) { - float ymin = ((Number)constraintStructure.getFieldValue("ymin")).floatValue() * FastMath.DEG_TO_RAD; - float ymax = ((Number)constraintStructure.getFieldValue("ymax")).floatValue() * FastMath.DEG_TO_RAD; - float difference = 0.0f; - if(angles[1] < ymin) { - difference = (angles[1] - ymin) * influence; - } else if(angles[1] > ymax) { - difference = (angles[1] - ymax) * influence; - } - angles[1] -= difference; - } - if((flag & LIMIT_ZROT) != 0) { - float zmin = ((Number)constraintStructure.getFieldValue("zmin")).floatValue() * FastMath.DEG_TO_RAD; - float zmax = ((Number)constraintStructure.getFieldValue("zmax")).floatValue() * FastMath.DEG_TO_RAD; - float difference = 0.0f; - if(angles[2] < zmin) { - difference = (angles[2] - zmin) * influence; - } else if(angles[2] > zmax) { - difference = (angles[2] - zmax) * influence; - } - angles[2] -= difference; - } - rotations[frame].fromAngles(angles);//TODO: consider constraint space !!! - } - boneTrack.setKeyframes(boneTrack.getTimes(), boneTrack.getTranslations(), rotations, boneTrack.getScales()); - } - } - }; - - //SHRINKWRAP constraint (TODO: to implement) - influenceFunctions[ConstraintType.CONSTRAINT_TYPE_SHRINKWRAP.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_SHRINKWRAP, dataRepository) {}; - - //SIZELIKE constraint - influenceFunctions[ConstraintType.CONSTRAINT_TYPE_SIZELIKE.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_SIZELIKE, dataRepository) { - @Override - public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation, Constraint constraint) { - Structure constraintData = constraint.getData(); - this.validateConstraintType(constraintData); - Vector3f targetScale = this.getTargetLocation(constraint); - BoneTrack boneTrack = this.getBoneTrack(skeleton, boneAnimation, constraint); - if(boneTrack != null) { - int flag = ((Number)constraintData.getFieldValue("flag")).intValue(); - Vector3f[] scales = boneTrack.getScales(); - int maxFrames = scales.length; - for(int frame = 0; frame < maxFrames; ++frame) { - Vector3f offset = Vector3f.ZERO; - if((flag & LOCLIKE_OFFSET) != 0) {//we add the original scale to the copied scale - offset = scales[frame].clone(); - } - - if((flag & SIZELIKE_X) != 0) { - scales[frame].x = targetScale.x; - } else if((flag & SIZELIKE_Y) != 0) { - scales[frame].y = targetScale.y; - } else if((flag & SIZELIKE_Z) != 0) { - scales[frame].z = targetScale.z; - } - scales[frame].addLocal(offset);//TODO: ipo influence - //TODO: add or multiply??? - } - boneTrack.setKeyframes(boneTrack.getTimes(), boneTrack.getTranslations(), boneTrack.getRotations(), scales); - } - } - }; - - //SIZELIMIT constraint - influenceFunctions[ConstraintType.CONSTRAINT_TYPE_SIZELIMIT.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_SIZELIMIT, dataRepository) { - @Override - public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation, Constraint constraint) { - Structure constraintStructure = constraint.getData(); - this.validateConstraintType(constraintStructure); - BoneTrack boneTrack = this.getBoneTrack(skeleton, boneAnimation, constraint); - if(boneTrack != null) { - int flag = ((Number)constraintStructure.getFieldValue("flag")).intValue(); - Vector3f[] scales = boneTrack.getScales(); - int maxFrames = scales.length; - for(int frame = 0; frame < maxFrames; ++frame) { - float influence = constraint.getIpo().calculateValue(frame); - if((flag & LIMIT_XMIN) != 0) { - float xmin = ((Number)constraintStructure.getFieldValue("xmin")).floatValue(); - if(scales[frame].x < xmin) { - scales[frame].x -= (scales[frame].x - xmin) * influence; - } - } - if((flag & LIMIT_XMAX) != 0) { - float xmax = ((Number)constraintStructure.getFieldValue("xmax")).floatValue(); - if(scales[frame].x > xmax) { - scales[frame].x -= (scales[frame].x - xmax) * influence; - } - } - if((flag & LIMIT_YMIN) != 0) { - float ymin = ((Number)constraintStructure.getFieldValue("ymin")).floatValue(); - if(scales[frame].y < ymin) { - scales[frame].y -= (scales[frame].y - ymin) * influence; - } - } - if((flag & LIMIT_YMAX) != 0) { - float ymax = ((Number)constraintStructure.getFieldValue("ymax")).floatValue(); - if(scales[frame].y > ymax) { - scales[frame].y -= (scales[frame].y - ymax) * influence; - } - } - if((flag & LIMIT_ZMIN) != 0) { - float zmin = ((Number)constraintStructure.getFieldValue("zmin")).floatValue(); - if(scales[frame].z < zmin) { - scales[frame].z -= (scales[frame].z - zmin) * influence; - } - } - if((flag & LIMIT_ZMAX) != 0) { - float zmax = ((Number)constraintStructure.getFieldValue("zmax")).floatValue(); - if(scales[frame].z > zmax) { - scales[frame].z -= (scales[frame].z - zmax) * influence; - } - }//TODO: consider constraint space !!! - } - boneTrack.setKeyframes(boneTrack.getTimes(), boneTrack.getTranslations(), boneTrack.getRotations(), scales); - } - } - }; - - //STRETCHTO constraint (TODO: to implement) - influenceFunctions[ConstraintType.CONSTRAINT_TYPE_STRETCHTO.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_STRETCHTO, dataRepository) {}; - - //TRANSFORM constraint (TODO: to implement) - influenceFunctions[ConstraintType.CONSTRAINT_TYPE_TRANSFORM.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_TRANSFORM, dataRepository) {}; - } - } - - /** - * This method reads constraints for for the given structure. The constraints are loaded only once for object/bone. - * @param ownerOMA - * the owner's old memory address - * @param objectStructure - * the structure we read constraint's for - * @param dataRepository - * the data repository - * @throws BlenderFileException - */ - public void loadConstraints(Structure objectStructure, DataRepository dataRepository) throws BlenderFileException { - // reading influence ipos for the constraints - IpoHelper ipoHelper = dataRepository.getHelper(IpoHelper.class); - Map> constraintsIpos = new HashMap>(); - Pointer pActions = (Pointer)objectStructure.getFieldValue("action"); - if(!pActions.isNull()) { - List actions = pActions.fetchData(dataRepository.getInputStream()); - for(Structure action : actions) { - Structure chanbase = (Structure)action.getFieldValue("chanbase"); - List actionChannels = chanbase.evaluateListBase(dataRepository); - for(Structure actionChannel : actionChannels) { - Map ipos = new HashMap(); - Structure constChannels = (Structure)actionChannel.getFieldValue("constraintChannels"); - List constraintChannels = constChannels.evaluateListBase(dataRepository); - for(Structure constraintChannel : constraintChannels) { - Pointer pIpo = (Pointer)constraintChannel.getFieldValue("ipo"); - if(!pIpo.isNull()) { - String constraintName = constraintChannel.getFieldValue("name").toString(); - Ipo ipo = ipoHelper.createIpo(pIpo.fetchData(dataRepository.getInputStream()).get(0), dataRepository); - ipos.put(constraintName, ipo); - } - } - String actionName = actionChannel.getFieldValue("name").toString(); - constraintsIpos.put(actionName, ipos); - } - } - } - - //loading constraints connected with the object's bones - List constraintsList = new ArrayList(); - Pointer pPose = (Pointer)objectStructure.getFieldValue("pose");//TODO: what if the object has two armatures ???? - if(!pPose.isNull()) { - //getting pose channels - List poseChannels = ((Structure)pPose.fetchData(dataRepository.getInputStream()).get(0).getFieldValue("chanbase")).evaluateListBase(dataRepository); - for(Structure poseChannel : poseChannels) { - Long boneOMA = Long.valueOf(((Pointer)poseChannel.getFieldValue("bone")).getOldMemoryAddress()); - //the name is read directly from structure because bone might not yet be loaded - String name = dataRepository.getFileBlock(boneOMA).getStructure(dataRepository).getFieldValue("name").toString(); - List constraints = ((Structure)poseChannel.getFieldValue("constraints")).evaluateListBase(dataRepository); - for(Structure constraint : constraints) { - int type = ((Number)constraint.getFieldValue("type")).intValue(); - String constraintName = constraint.getFieldValue("name").toString(); - Ipo ipo = constraintsIpos.get(name).get(constraintName); - if(ipo == null) { - float enforce = ((Number)constraint.getFieldValue("enforce")).floatValue(); - ipo = ipoHelper.createIpo(enforce); - } - Space ownerSpace = Space.valueOf(((Number)constraint.getFieldValue("ownspace")).byteValue()); - Space targetSpace = Space.valueOf(((Number)constraint.getFieldValue("tarspace")).byteValue()); - Constraint c = new Constraint(constraint, influenceFunctions[type], boneOMA, ownerSpace, targetSpace, ipo, dataRepository); - constraintsList.add(c); - } - } - } - /* TODO: reading constraints for objects (implement when object's animation will be available) - List constraintChannels = ((Structure)objectStructure.getFieldValue("constraintChannels")).evaluateListBase(dataRepository); - for(Structure constraintChannel : constraintChannels) { - System.out.println(constraintChannel); - } - - //loading constraints connected with the object itself (TODO: test this) - if(!this.constraints.containsKey(objectStructure.getOldMemoryAddress())) { - List constraints = ((Structure)objectStructure.getFieldValue("constraints")).evaluateListBase(dataRepository); - Constraint[] result = new Constraint[constraints.size()]; - int i = 0; - for(Structure constraint : constraints) { - int type = ((Number)constraint.getFieldValue("type")).intValue(); - String name = constraint.getFieldValue("name").toString(); - result[i++] = new Constraint(constraint, influenceFunctions[type], null, dataRepository);//TODO: influence ipos for object animation - } - this.constraints.put(objectStructure.getOldMemoryAddress(), result); - } - */ - if(constraintsList.size() > 0) { - this.constraints.put(objectStructure.getOldMemoryAddress(), constraintsList.toArray(new Constraint[constraintsList.size()])); - } - } - - /** - * This method returns a list of constraints of the feature's constraints. The order of constraints is important. - * @param ownerOMA - * the owner's old memory address - * @return a table of constraints for the feature specified by old memory address - */ - public Constraint[] getConstraints(Long ownerOMA) { - return constraints.get(ownerOMA); - } - - @Override - public void clearState() { - constraints.clear(); - } - - /** - * The purpose of this class is to imitate bone's movement when calculating inverse kinematics. - * @author Marcin Roguski - */ - private static class CalculationBone extends Node { - /** The name of the bone. Only to be used in toString method. */ - private String boneName; - /** The bone's tracks. Will be altered at the end of calculation process. */ - private BoneTrack track; - /** The starting position of the bone. */ - private Vector3f startTranslation; - /** The starting rotation of the bone. */ - private Quaternion startRotation; - /** The starting scale of the bone. */ - private Vector3f startScale; - - private Vector3f[] translations; - private Quaternion[] rotations; - private Vector3f[] scales; - - /** - * Constructor. Stores the track, starting transformation and sets the transformation to the starting positions. - * @param bone - * the bone this class will imitate - * @param track - * the bone's tracks - */ - public CalculationBone(Bone bone, BoneTrack track) { - this.boneName = bone.getName(); - this.track = track; - this.startRotation = bone.getModelSpaceRotation().clone(); - this.startTranslation = bone.getModelSpacePosition().clone(); - this.startScale = bone.getModelSpaceScale().clone(); - this.translations = track.getTranslations(); - this.rotations = track.getRotations(); - this.scales = track.getScales(); - this.reset(); - } - - /** - * This method returns the end point of the bone. If the bone has parent it is calculated from the start point - * of parent to the start point of this bone. If the bone doesn't have a parent the end location is considered - * to be 1 point up along Y axis (scale is applied if set to != 1.0); - * @return the end point of this bone - */ - //TODO: set to Z axis if user defined it this way - public Vector3f getEndPoint() { - if(this.getParent() == null) { - return new Vector3f(0, this.getLocalScale().y, 0); - } else { - Node parent = this.getParent(); - return parent.getWorldTranslation().subtract(this.getWorldTranslation()).multLocal(this.getWorldScale()); - } - } - - /** - * This method resets the calculation bone to the starting position. - */ - public void reset() { - this.setLocalTranslation(startTranslation); - this.setLocalRotation(startRotation); - this.setLocalScale(startScale); - } - - @Override - public int attachChild(Spatial child) { - if(this.getChildren() != null && this.getChildren().size() > 1) { - throw new IllegalStateException(this.getClass().getName() + " class instance can only have one child!"); - } - return super.attachChild(child); - } - - public Spatial rotate(Quaternion rot, int frame) { - Spatial spatial = super.rotate(rot); - this.updateWorldTransforms(); - if(this.getChildren()!=null && this.getChildren().size()>0) { - CalculationBone child = (CalculationBone)this.getChild(0); - child.updateWorldTransforms(); - } - rotations[frame].set(this.getLocalRotation()); - translations[frame].set(this.getLocalTranslation()); - if(scales!=null) { - scales[frame].set(this.getLocalScale()); - } - return spatial; - } - - public void applyCalculatedTracks() { - track.setKeyframes(track.getTimes(), translations, rotations);//TODO:scales - } - - @Override - public String toString() { - return boneName+ ": " + this.getLocalRotation() + " " + this.getLocalTranslation(); - } - } + + /** + * A table containing implementations of influence functions for constraints. It should contain functions for + * blender at least 249 and higher. + */ + protected static AbstractInfluenceFunction[] influenceFunctions; + /** + * Constraints stored for object with the given old memory address. + */ + protected Map constraints = new HashMap(); + + /** + * Helper constructor. It's main task is to generate the affection functions. These functions are common to all + * ConstraintHelper instances. Unfortunately this constructor might grow large. If it becomes too large - I shall + * consider refactoring. The constructor parses the given blender version and stores the result. Some + * functionalities may differ in different blender versions. + * @param blenderVersion + * the version read from the blend file + */ + public ConstraintHelper(String blenderVersion, DataRepository dataRepository) { + super(blenderVersion); + if (influenceFunctions == null) { + //TODO: synchronization + influenceFunctions = new AbstractInfluenceFunction[ConstraintType.getLastDefinedTypeValue() + 1]; + //ACTION constraint (TODO: to implement) + influenceFunctions[ConstraintType.CONSTRAINT_TYPE_ACTION.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_ACTION, dataRepository) { + }; + + //CHILDOF constraint (TODO: to implement) + influenceFunctions[ConstraintType.CONSTRAINT_TYPE_CHILDOF.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_CHILDOF, dataRepository) { + }; + + //CLAMPTO constraint + influenceFunctions[ConstraintType.CONSTRAINT_TYPE_CLAMPTO.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_CLAMPTO, dataRepository) { + + @Override + public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation, Constraint constraint) { + this.validateConstraintType(constraint.getData()); + LOGGER.log(Level.INFO, "{0} not active! Curves not yet implemented!", constraint.getName());//TODO: implement when curves are implemented + } + }; + + //DISTLIMIT constraint + influenceFunctions[ConstraintType.CONSTRAINT_TYPE_DISTLIMIT.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_DISTLIMIT, dataRepository) { + + @Override + public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation, Constraint constraint) { + Structure constraintStructure = constraint.getData(); + this.validateConstraintType(constraintStructure); + Vector3f targetLocation = this.getTargetLocation(constraint); + BoneTrack boneTrack = this.getBoneTrack(skeleton, boneAnimation, constraint); + if (boneTrack != null) { + //TODO: target vertex group !!! + float dist = ((Number) constraintStructure.getFieldValue("dist")).floatValue(); + int mode = ((Number) constraintStructure.getFieldValue("mode")).intValue(); + + int maxFrames = boneTrack.getTimes().length; + Vector3f[] translations = boneTrack.getTranslations(); + for (int frame = 0; frame < maxFrames; ++frame) { + Vector3f v = translations[frame].subtract(targetLocation); + float currentDistance = v.length(); + float influence = constraint.getIpo().calculateValue(frame); + float modifier = 0.0f; + switch (mode) { + case LIMITDIST_INSIDE: + if (currentDistance >= dist) { + modifier = (dist - currentDistance) / currentDistance; + } + break; + case LIMITDIST_ONSURFACE: + modifier = (dist - currentDistance) / currentDistance; + break; + case LIMITDIST_OUTSIDE: + if (currentDistance <= dist) { + modifier = (dist - currentDistance) / currentDistance; + } + break; + default: + throw new IllegalStateException("Unknown distance limit constraint mode: " + mode); + } + translations[frame].addLocal(v.multLocal(modifier * influence)); + } + boneTrack.setKeyframes(boneTrack.getTimes(), translations, boneTrack.getRotations(), boneTrack.getScales()); + } + } + }; + + //FOLLOWPATH constraint + influenceFunctions[ConstraintType.CONSTRAINT_TYPE_FOLLOWPATH.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_FOLLOWPATH, dataRepository) { + + @Override + public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation, Constraint constraint) { + this.validateConstraintType(constraint.getData()); + LOGGER.log(Level.INFO, "{0} not active! Curves not yet implemented!", constraint.getName());//TODO: implement when curves are implemented + } + }; + + //KINEMATIC constraint (TODO: to implement) + influenceFunctions[ConstraintType.CONSTRAINT_TYPE_KINEMATIC.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_KINEMATIC, dataRepository) { + + @Override + public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation, Constraint constraint) { + Structure constraintStructure = constraint.getData(); + this.validateConstraintType(constraintStructure); + /*Long boneOMA = constraint.getBoneOMA(); + //IK solver is only attached to bones + Bone ownerBone = (Bone)dataRepository.getLoadedFeature(boneOMA, LoadedFeatureDataType.LOADED_FEATURE); + + //get the target point + Object targetObject = this.getTarget(constraint, LoadedFeatureDataType.LOADED_FEATURE); + Vector3f pt = null;//Point Target + if(targetObject instanceof Bone) { + pt = ((Bone)targetObject).getModelSpacePosition(); + } else if(targetObject instanceof Node) { + pt = ((Node)targetObject).getWorldTranslation(); + } else if(targetObject instanceof Skeleton) { + Structure armatureNodeStructure = (Structure)this.getTarget(constraint, LoadedFeatureDataType.LOADED_STRUCTURE); + ObjectHelper objectHelper = dataRepository.getHelper(ObjectHelper.class); + Transform transform = objectHelper.getTransformation(armatureNodeStructure); + pt = transform.getTranslation(); + } else { + throw new IllegalStateException("Unknown target object type! Should be Node, Bone or Skeleton and there is: " + targetObject.getClass().getName()); + } + //preparing data + int maxIterations = ((Number)constraintStructure.getFieldValue("iterations")).intValue(); + CalculationBone[] bones = this.getBonesToCalculate(ownerBone, skeleton, boneAnimation); + for(int i=0;i= IK_SOLVER_ERROR && iteration <= maxIterations) { + //rotating the bones + for(int i = 0; i < bones.length - 1; ++i) { + Vector3f pe = bones[i].getEndPoint(); + Vector3f pc = bones[i + 1].getWorldTranslation().clone(); + + Vector3f peSUBpc = pe.subtract(pc).normalizeLocal(); + Vector3f ptSUBpc = pt.subtract(pc).normalizeLocal(); + + float theta = FastMath.acos(peSUBpc.dot(ptSUBpc)); + Vector3f direction = peSUBpc.cross(ptSUBpc).normalizeLocal(); + bones[i].rotate(rotation.fromAngleAxis(theta, direction), frame); + } + error = pt.subtract(bones[0].getEndPoint()).length(); + ++iteration; + } + System.out.println("error = " + error + " iterations = " + iteration); + } + + for(CalculationBone bone : bones) { + bone.applyCalculatedTracks(); + } + + System.out.println("&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&"); + for(int i=0;i bonesList = new ArrayList(); + Bone currentBone = bone; + do { + int boneIndex = skeleton.getBoneIndex(currentBone); + for (int i = 0; i < boneAnimation.getTracks().length; ++i) { + if (boneAnimation.getTracks()[i].getTargetBoneIndex() == boneIndex) { + bonesList.add(new CalculationBone(currentBone, boneAnimation.getTracks()[i])); + break; + } + } + currentBone = currentBone.getParent(); + } while (currentBone != null); + //attaching children + CalculationBone[] result = bonesList.toArray(new CalculationBone[bonesList.size()]); + for (int i = result.length - 1; i > 0; --i) { + result[i].attachChild(result[i - 1]); + } + return result; + } + }; + + //LOCKTRACK constraint (TODO: to implement) + influenceFunctions[ConstraintType.CONSTRAINT_TYPE_LOCKTRACK.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_LOCKTRACK, dataRepository) { + }; + + //LOCLIKE constraint + influenceFunctions[ConstraintType.CONSTRAINT_TYPE_LOCLIKE.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_LOCLIKE, dataRepository) { + + @Override + public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation, Constraint constraint) { + Structure constraintData = constraint.getData(); + this.validateConstraintType(constraintData); + BoneTrack boneTrack = this.getBoneTrack(skeleton, boneAnimation, constraint); + if (boneTrack != null) { + Vector3f targetLocation = this.getTargetLocation(constraint); + int flag = ((Number) constraintData.getFieldValue("flag")).intValue(); + Vector3f[] translations = boneTrack.getTranslations(); + int maxFrames = translations.length; + for (int frame = 0; frame < maxFrames; ++frame) { + Vector3f offset = Vector3f.ZERO; + if ((flag & LOCLIKE_OFFSET) != 0) {//we add the original location to the copied location + offset = translations[frame].clone(); + } + + if ((flag & LOCLIKE_X) != 0) { + translations[frame].x = targetLocation.x; + if ((flag & LOCLIKE_X_INVERT) != 0) { + translations[frame].x = -translations[frame].x; + } + } else if ((flag & LOCLIKE_Y) != 0) { + translations[frame].y = targetLocation.y; + if ((flag & LOCLIKE_Y_INVERT) != 0) { + translations[frame].y = -translations[frame].y; + } + } else if ((flag & LOCLIKE_Z) != 0) { + translations[frame].z = targetLocation.z; + if ((flag & LOCLIKE_Z_INVERT) != 0) { + translations[frame].z = -translations[frame].z; + } + } + translations[frame].addLocal(offset);//TODO: ipo influence + } + boneTrack.setKeyframes(boneTrack.getTimes(), translations, boneTrack.getRotations(), boneTrack.getScales()); + } + } + }; + + //LOCLIMIT constraint + influenceFunctions[ConstraintType.CONSTRAINT_TYPE_LOCLIMIT.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_LOCLIMIT, dataRepository) { + + @Override + public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation, Constraint constraint) { + Structure constraintStructure = constraint.getData(); + this.validateConstraintType(constraintStructure); + BoneTrack boneTrack = this.getBoneTrack(skeleton, boneAnimation, constraint); + if (boneTrack != null) { + int flag = ((Number) constraintStructure.getFieldValue("flag")).intValue(); + Vector3f[] translations = boneTrack.getTranslations(); + int maxFrames = translations.length; + for (int frame = 0; frame < maxFrames; ++frame) { + float influence = constraint.getIpo().calculateValue(frame); + if ((flag & LIMIT_XMIN) != 0) { + float xmin = ((Number) constraintStructure.getFieldValue("xmin")).floatValue(); + if (translations[frame].x < xmin) { + translations[frame].x -= (translations[frame].x - xmin) * influence; + } + } + if ((flag & LIMIT_XMAX) != 0) { + float xmax = ((Number) constraintStructure.getFieldValue("xmax")).floatValue(); + if (translations[frame].x > xmax) { + translations[frame].x -= (translations[frame].x - xmax) * influence; + } + } + if ((flag & LIMIT_YMIN) != 0) { + float ymin = ((Number) constraintStructure.getFieldValue("ymin")).floatValue(); + if (translations[frame].y < ymin) { + translations[frame].y -= (translations[frame].y - ymin) * influence; + } + } + if ((flag & LIMIT_YMAX) != 0) { + float ymax = ((Number) constraintStructure.getFieldValue("ymax")).floatValue(); + if (translations[frame].y > ymax) { + translations[frame].y -= (translations[frame].y - ymax) * influence; + } + } + if ((flag & LIMIT_ZMIN) != 0) { + float zmin = ((Number) constraintStructure.getFieldValue("zmin")).floatValue(); + if (translations[frame].z < zmin) { + translations[frame].z -= (translations[frame].z - zmin) * influence; + } + } + if ((flag & LIMIT_ZMAX) != 0) { + float zmax = ((Number) constraintStructure.getFieldValue("zmax")).floatValue(); + if (translations[frame].z > zmax) { + translations[frame].z -= (translations[frame].z - zmax) * influence; + } + }//TODO: consider constraint space !!! + } + boneTrack.setKeyframes(boneTrack.getTimes(), translations, boneTrack.getRotations(), boneTrack.getScales()); + } + } + }; + + //MINMAX constraint (TODO: to implement) + influenceFunctions[ConstraintType.CONSTRAINT_TYPE_MINMAX.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_MINMAX, dataRepository) { + }; + + //NULL constraint - does nothing + influenceFunctions[ConstraintType.CONSTRAINT_TYPE_NULL.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_NULL, dataRepository) { + }; + + //PYTHON constraint (TODO: to implement) + influenceFunctions[ConstraintType.CONSTRAINT_TYPE_PYTHON.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_PYTHON, dataRepository) { + }; + + //RIGIDBODYJOINT constraint (TODO: to implement) + influenceFunctions[ConstraintType.CONSTRAINT_TYPE_RIGIDBODYJOINT.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_RIGIDBODYJOINT, dataRepository) { + }; + + //ROTLIKE constraint + influenceFunctions[ConstraintType.CONSTRAINT_TYPE_ROTLIKE.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_ROTLIKE, dataRepository) { + + @Override + public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation, Constraint constraint) { + Structure constraintData = constraint.getData(); + this.validateConstraintType(constraintData); + BoneTrack boneTrack = this.getBoneTrack(skeleton, boneAnimation, constraint); + if (boneTrack != null) { + Quaternion targetRotation = this.getTargetRotation(constraint); + int flag = ((Number) constraintData.getFieldValue("flag")).intValue(); + float[] targetAngles = targetRotation.toAngles(null); + Quaternion[] rotations = boneTrack.getRotations(); + int maxFrames = rotations.length; + for (int frame = 0; frame < maxFrames; ++frame) { + float[] angles = rotations[frame].toAngles(null); + + Quaternion offset = Quaternion.IDENTITY; + if ((flag & ROTLIKE_OFFSET) != 0) {//we add the original rotation to the copied rotation + offset = rotations[frame].clone(); + } + + if ((flag & ROTLIKE_X) != 0) { + angles[0] = targetAngles[0]; + if ((flag & ROTLIKE_X_INVERT) != 0) { + angles[0] = -angles[0]; + } + } else if ((flag & ROTLIKE_Y) != 0) { + angles[1] = targetAngles[1]; + if ((flag & ROTLIKE_Y_INVERT) != 0) { + angles[1] = -angles[1]; + } + } else if ((flag & ROTLIKE_Z) != 0) { + angles[2] = targetAngles[2]; + if ((flag & ROTLIKE_Z_INVERT) != 0) { + angles[2] = -angles[2]; + } + } + rotations[frame].fromAngles(angles).multLocal(offset);//TODO: ipo influence + } + boneTrack.setKeyframes(boneTrack.getTimes(), boneTrack.getTranslations(), rotations, boneTrack.getScales()); + } + } + }; + + //ROTLIMIT constraint + influenceFunctions[ConstraintType.CONSTRAINT_TYPE_ROTLIMIT.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_ROTLIMIT, dataRepository) { + + @Override + public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation, Constraint constraint) { + Structure constraintStructure = constraint.getData(); + this.validateConstraintType(constraintStructure); + BoneTrack boneTrack = this.getBoneTrack(skeleton, boneAnimation, constraint); + if (boneTrack != null) { + int flag = ((Number) constraintStructure.getFieldValue("flag")).intValue(); + Quaternion[] rotations = boneTrack.getRotations(); + int maxFrames = rotations.length; + for (int frame = 0; frame < maxFrames; ++frame) { + float[] angles = rotations[frame].toAngles(null); + float influence = constraint.getIpo().calculateValue(frame); + if ((flag & LIMIT_XROT) != 0) { + float xmin = ((Number) constraintStructure.getFieldValue("xmin")).floatValue() * FastMath.DEG_TO_RAD; + float xmax = ((Number) constraintStructure.getFieldValue("xmax")).floatValue() * FastMath.DEG_TO_RAD; + float difference = 0.0f; + if (angles[0] < xmin) { + difference = (angles[0] - xmin) * influence; + } else if (angles[0] > xmax) { + difference = (angles[0] - xmax) * influence; + } + angles[0] -= difference; + } + if ((flag & LIMIT_YROT) != 0) { + float ymin = ((Number) constraintStructure.getFieldValue("ymin")).floatValue() * FastMath.DEG_TO_RAD; + float ymax = ((Number) constraintStructure.getFieldValue("ymax")).floatValue() * FastMath.DEG_TO_RAD; + float difference = 0.0f; + if (angles[1] < ymin) { + difference = (angles[1] - ymin) * influence; + } else if (angles[1] > ymax) { + difference = (angles[1] - ymax) * influence; + } + angles[1] -= difference; + } + if ((flag & LIMIT_ZROT) != 0) { + float zmin = ((Number) constraintStructure.getFieldValue("zmin")).floatValue() * FastMath.DEG_TO_RAD; + float zmax = ((Number) constraintStructure.getFieldValue("zmax")).floatValue() * FastMath.DEG_TO_RAD; + float difference = 0.0f; + if (angles[2] < zmin) { + difference = (angles[2] - zmin) * influence; + } else if (angles[2] > zmax) { + difference = (angles[2] - zmax) * influence; + } + angles[2] -= difference; + } + rotations[frame].fromAngles(angles);//TODO: consider constraint space !!! + } + boneTrack.setKeyframes(boneTrack.getTimes(), boneTrack.getTranslations(), rotations, boneTrack.getScales()); + } + } + }; + + //SHRINKWRAP constraint (TODO: to implement) + influenceFunctions[ConstraintType.CONSTRAINT_TYPE_SHRINKWRAP.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_SHRINKWRAP, dataRepository) { + }; + + //SIZELIKE constraint + influenceFunctions[ConstraintType.CONSTRAINT_TYPE_SIZELIKE.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_SIZELIKE, dataRepository) { + + @Override + public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation, Constraint constraint) { + Structure constraintData = constraint.getData(); + this.validateConstraintType(constraintData); + Vector3f targetScale = this.getTargetLocation(constraint); + BoneTrack boneTrack = this.getBoneTrack(skeleton, boneAnimation, constraint); + if (boneTrack != null) { + int flag = ((Number) constraintData.getFieldValue("flag")).intValue(); + Vector3f[] scales = boneTrack.getScales(); + int maxFrames = scales.length; + for (int frame = 0; frame < maxFrames; ++frame) { + Vector3f offset = Vector3f.ZERO; + if ((flag & LOCLIKE_OFFSET) != 0) {//we add the original scale to the copied scale + offset = scales[frame].clone(); + } + + if ((flag & SIZELIKE_X) != 0) { + scales[frame].x = targetScale.x; + } else if ((flag & SIZELIKE_Y) != 0) { + scales[frame].y = targetScale.y; + } else if ((flag & SIZELIKE_Z) != 0) { + scales[frame].z = targetScale.z; + } + scales[frame].addLocal(offset);//TODO: ipo influence + //TODO: add or multiply??? + } + boneTrack.setKeyframes(boneTrack.getTimes(), boneTrack.getTranslations(), boneTrack.getRotations(), scales); + } + } + }; + + //SIZELIMIT constraint + influenceFunctions[ConstraintType.CONSTRAINT_TYPE_SIZELIMIT.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_SIZELIMIT, dataRepository) { + + @Override + public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation, Constraint constraint) { + Structure constraintStructure = constraint.getData(); + this.validateConstraintType(constraintStructure); + BoneTrack boneTrack = this.getBoneTrack(skeleton, boneAnimation, constraint); + if (boneTrack != null) { + int flag = ((Number) constraintStructure.getFieldValue("flag")).intValue(); + Vector3f[] scales = boneTrack.getScales(); + int maxFrames = scales.length; + for (int frame = 0; frame < maxFrames; ++frame) { + float influence = constraint.getIpo().calculateValue(frame); + if ((flag & LIMIT_XMIN) != 0) { + float xmin = ((Number) constraintStructure.getFieldValue("xmin")).floatValue(); + if (scales[frame].x < xmin) { + scales[frame].x -= (scales[frame].x - xmin) * influence; + } + } + if ((flag & LIMIT_XMAX) != 0) { + float xmax = ((Number) constraintStructure.getFieldValue("xmax")).floatValue(); + if (scales[frame].x > xmax) { + scales[frame].x -= (scales[frame].x - xmax) * influence; + } + } + if ((flag & LIMIT_YMIN) != 0) { + float ymin = ((Number) constraintStructure.getFieldValue("ymin")).floatValue(); + if (scales[frame].y < ymin) { + scales[frame].y -= (scales[frame].y - ymin) * influence; + } + } + if ((flag & LIMIT_YMAX) != 0) { + float ymax = ((Number) constraintStructure.getFieldValue("ymax")).floatValue(); + if (scales[frame].y > ymax) { + scales[frame].y -= (scales[frame].y - ymax) * influence; + } + } + if ((flag & LIMIT_ZMIN) != 0) { + float zmin = ((Number) constraintStructure.getFieldValue("zmin")).floatValue(); + if (scales[frame].z < zmin) { + scales[frame].z -= (scales[frame].z - zmin) * influence; + } + } + if ((flag & LIMIT_ZMAX) != 0) { + float zmax = ((Number) constraintStructure.getFieldValue("zmax")).floatValue(); + if (scales[frame].z > zmax) { + scales[frame].z -= (scales[frame].z - zmax) * influence; + } + }//TODO: consider constraint space !!! + } + boneTrack.setKeyframes(boneTrack.getTimes(), boneTrack.getTranslations(), boneTrack.getRotations(), scales); + } + } + }; + + //STRETCHTO constraint (TODO: to implement) + influenceFunctions[ConstraintType.CONSTRAINT_TYPE_STRETCHTO.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_STRETCHTO, dataRepository) { + }; + + //TRANSFORM constraint (TODO: to implement) + influenceFunctions[ConstraintType.CONSTRAINT_TYPE_TRANSFORM.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_TRANSFORM, dataRepository) { + }; + } + } + + /** + * This method reads constraints for for the given structure. The constraints are loaded only once for object/bone. + * @param ownerOMA + * the owner's old memory address + * @param objectStructure + * the structure we read constraint's for + * @param dataRepository + * the data repository + * @throws BlenderFileException + */ + public void loadConstraints(Structure objectStructure, DataRepository dataRepository) throws BlenderFileException { + // reading influence ipos for the constraints + IpoHelper ipoHelper = dataRepository.getHelper(IpoHelper.class); + Map> constraintsIpos = new HashMap>(); + Pointer pActions = (Pointer) objectStructure.getFieldValue("action"); + if (!pActions.isNull()) { + List actions = pActions.fetchData(dataRepository.getInputStream()); + for (Structure action : actions) { + Structure chanbase = (Structure) action.getFieldValue("chanbase"); + List actionChannels = chanbase.evaluateListBase(dataRepository); + for (Structure actionChannel : actionChannels) { + Map ipos = new HashMap(); + Structure constChannels = (Structure) actionChannel.getFieldValue("constraintChannels"); + List constraintChannels = constChannels.evaluateListBase(dataRepository); + for (Structure constraintChannel : constraintChannels) { + Pointer pIpo = (Pointer) constraintChannel.getFieldValue("ipo"); + if (!pIpo.isNull()) { + String constraintName = constraintChannel.getFieldValue("name").toString(); + Ipo ipo = ipoHelper.createIpo(pIpo.fetchData(dataRepository.getInputStream()).get(0), dataRepository); + ipos.put(constraintName, ipo); + } + } + String actionName = actionChannel.getFieldValue("name").toString(); + constraintsIpos.put(actionName, ipos); + } + } + } + + //loading constraints connected with the object's bones + List constraintsList = new ArrayList(); + Pointer pPose = (Pointer) objectStructure.getFieldValue("pose");//TODO: what if the object has two armatures ???? + if (!pPose.isNull()) { + //getting pose channels + List poseChannels = ((Structure) pPose.fetchData(dataRepository.getInputStream()).get(0).getFieldValue("chanbase")).evaluateListBase(dataRepository); + for (Structure poseChannel : poseChannels) { + Long boneOMA = Long.valueOf(((Pointer) poseChannel.getFieldValue("bone")).getOldMemoryAddress()); + //the name is read directly from structure because bone might not yet be loaded + String name = dataRepository.getFileBlock(boneOMA).getStructure(dataRepository).getFieldValue("name").toString(); + List constraints = ((Structure) poseChannel.getFieldValue("constraints")).evaluateListBase(dataRepository); + for (Structure constraint : constraints) { + int type = ((Number) constraint.getFieldValue("type")).intValue(); + String constraintName = constraint.getFieldValue("name").toString(); + Ipo ipo = constraintsIpos.get(name).get(constraintName); + if (ipo == null) { + float enforce = ((Number) constraint.getFieldValue("enforce")).floatValue(); + ipo = ipoHelper.createIpo(enforce); + } + Space ownerSpace = Space.valueOf(((Number) constraint.getFieldValue("ownspace")).byteValue()); + Space targetSpace = Space.valueOf(((Number) constraint.getFieldValue("tarspace")).byteValue()); + Constraint c = new Constraint(constraint, influenceFunctions[type], boneOMA, ownerSpace, targetSpace, ipo, dataRepository); + constraintsList.add(c); + } + } + } + /* TODO: reading constraints for objects (implement when object's animation will be available) + List constraintChannels = ((Structure)objectStructure.getFieldValue("constraintChannels")).evaluateListBase(dataRepository); + for(Structure constraintChannel : constraintChannels) { + System.out.println(constraintChannel); + } + + //loading constraints connected with the object itself (TODO: test this) + if(!this.constraints.containsKey(objectStructure.getOldMemoryAddress())) { + List constraints = ((Structure)objectStructure.getFieldValue("constraints")).evaluateListBase(dataRepository); + Constraint[] result = new Constraint[constraints.size()]; + int i = 0; + for(Structure constraint : constraints) { + int type = ((Number)constraint.getFieldValue("type")).intValue(); + String name = constraint.getFieldValue("name").toString(); + result[i++] = new Constraint(constraint, influenceFunctions[type], null, dataRepository);//TODO: influence ipos for object animation + } + this.constraints.put(objectStructure.getOldMemoryAddress(), result); + } + */ + if (constraintsList.size() > 0) { + this.constraints.put(objectStructure.getOldMemoryAddress(), constraintsList.toArray(new Constraint[constraintsList.size()])); + } + } + + /** + * This method returns a list of constraints of the feature's constraints. The order of constraints is important. + * @param ownerOMA + * the owner's old memory address + * @return a table of constraints for the feature specified by old memory address + */ + public Constraint[] getConstraints(Long ownerOMA) { + return constraints.get(ownerOMA); + } + + @Override + public void clearState() { + constraints.clear(); + } + + /** + * The purpose of this class is to imitate bone's movement when calculating inverse kinematics. + * @author Marcin Roguski + */ + private static class CalculationBone extends Node { + + /** The name of the bone. Only to be used in toString method. */ + private String boneName; + /** The bone's tracks. Will be altered at the end of calculation process. */ + private BoneTrack track; + /** The starting position of the bone. */ + private Vector3f startTranslation; + /** The starting rotation of the bone. */ + private Quaternion startRotation; + /** The starting scale of the bone. */ + private Vector3f startScale; + private Vector3f[] translations; + private Quaternion[] rotations; + private Vector3f[] scales; + + /** + * Constructor. Stores the track, starting transformation and sets the transformation to the starting positions. + * @param bone + * the bone this class will imitate + * @param track + * the bone's tracks + */ + public CalculationBone(Bone bone, BoneTrack track) { + this.boneName = bone.getName(); + this.track = track; + this.startRotation = bone.getModelSpaceRotation().clone(); + this.startTranslation = bone.getModelSpacePosition().clone(); + this.startScale = bone.getModelSpaceScale().clone(); + this.translations = track.getTranslations(); + this.rotations = track.getRotations(); + this.scales = track.getScales(); + this.reset(); + } + + /** + * This method returns the end point of the bone. If the bone has parent it is calculated from the start point + * of parent to the start point of this bone. If the bone doesn't have a parent the end location is considered + * to be 1 point up along Y axis (scale is applied if set to != 1.0); + * @return the end point of this bone + */ + //TODO: set to Z axis if user defined it this way + public Vector3f getEndPoint() { + if (this.getParent() == null) { + return new Vector3f(0, this.getLocalScale().y, 0); + } else { + Node parent = this.getParent(); + return parent.getWorldTranslation().subtract(this.getWorldTranslation()).multLocal(this.getWorldScale()); + } + } + + /** + * This method resets the calculation bone to the starting position. + */ + public void reset() { + this.setLocalTranslation(startTranslation); + this.setLocalRotation(startRotation); + this.setLocalScale(startScale); + } + + @Override + public int attachChild(Spatial child) { + if (this.getChildren() != null && this.getChildren().size() > 1) { + throw new IllegalStateException(this.getClass().getName() + " class instance can only have one child!"); + } + return super.attachChild(child); + } + + public Spatial rotate(Quaternion rot, int frame) { + Spatial spatial = super.rotate(rot); + this.updateWorldTransforms(); + if (this.getChildren() != null && this.getChildren().size() > 0) { + CalculationBone child = (CalculationBone) this.getChild(0); + child.updateWorldTransforms(); + } + rotations[frame].set(this.getLocalRotation()); + translations[frame].set(this.getLocalTranslation()); + if (scales != null) { + scales[frame].set(this.getLocalScale()); + } + return spatial; + } + + public void applyCalculatedTracks() { + track.setKeyframes(track.getTimes(), translations, rotations);//TODO:scales + } + + @Override + public String toString() { + return boneName + ": " + this.getLocalRotation() + " " + this.getLocalTranslation(); + } + } } diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/helpers/v249/CurvesHelper.java b/engine/src/blender/com/jme3/scene/plugins/blender/helpers/v249/CurvesHelper.java index f5ca3c982..343fc1bdc 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/helpers/v249/CurvesHelper.java +++ b/engine/src/blender/com/jme3/scene/plugins/blender/helpers/v249/CurvesHelper.java @@ -39,552 +39,552 @@ import com.jme3.util.BufferUtils; * @author Marcin Roguski */ public class CurvesHelper extends AbstractBlenderHelper { - private static final Logger LOGGER = Logger.getLogger(CurvesHelper.class.getName()); - - /** This variable indicates if the Y asxis is the UP axis or not. */ - protected boolean fixUpAxis; - /** Minimum basis U function degree for NURBS curves and surfaces. */ - protected int minimumBasisUFunctionDegree = 4; - /** Minimum basis V function degree for NURBS curves and surfaces. */ - protected int minimumBasisVFunctionDegree = 4; - - /** - * This constructor parses the given blender version and stores the result. Some functionalities may differ in - * different blender versions. - * @param blenderVersion - * the version read from the blend file - */ - public CurvesHelper(String blenderVersion) { - super(blenderVersion); - } - - /** - * This method sets the Y is UP axis. By default the UP axis is Z (just like in blender). - * @param fixUpAxis - * a variable that indicates if the Y asxis is the UP axis or not - */ - public void setyIsUpAxis(boolean fixUpAxis) { - this.fixUpAxis = fixUpAxis; - } - - /** - * This method converts given curve structure into a list of geometries representing the curve. The list is used here because on object - * can have several separate curves. - * @param curveStructure - * the curve structure - * @param dataRepository - * the data repository - * @return a list of geometries repreenting a single curve object - * @throws BlenderFileException - */ - public List toCurve(Structure curveStructure, DataRepository dataRepository) throws BlenderFileException { - String name = curveStructure.getName(); - int flag = ((Number)curveStructure.getFieldValue("flag")).intValue(); - boolean is3D = (flag & 0x01) != 0; - boolean isFront = (flag & 0x02) != 0 && !is3D; - boolean isBack = (flag & 0x04) != 0 && !is3D; - if(isFront) { - LOGGER.warning("No front face in curve implemented yet!");//TODO: implement front face - } - if(isBack) { - LOGGER.warning("No back face in curve implemented yet!");//TODO: implement back face - } - - //reading nurbs (and sorting them by material) - List nurbStructures = ((Structure)curveStructure.getFieldValue("nurb")).evaluateListBase(dataRepository); - Map> nurbs = new HashMap>(); - for(Structure nurb : nurbStructures) { - Number matNumber = (Number) nurb.getFieldValue("mat_nr"); - List nurbList = nurbs.get(matNumber); - if(nurbList==null) { - nurbList = new ArrayList(); - nurbs.put(matNumber, nurbList); - } - nurbList.add(nurb); - } - - //getting materials - MaterialHelper materialHelper = dataRepository.getHelper(MaterialHelper.class); - Material[] materials = materialHelper.getMaterials(curveStructure, dataRepository); - if(materials==null) { - materials = new Material[] { dataRepository.getDefaultMaterial().clone() }; - } - for(Material material : materials) { - material.getAdditionalRenderState().setFaceCullMode(FaceCullMode.Off); - } - - //getting or creating bevel object - List bevelObject = null; - Pointer pBevelObject = (Pointer) curveStructure.getFieldValue("bevobj"); - if(!pBevelObject.isNull()) { - Pointer pBevelStructure = (Pointer) pBevelObject.fetchData(dataRepository.getInputStream()).get(0).getFieldValue("data"); - Structure bevelStructure = pBevelStructure.fetchData(dataRepository.getInputStream()).get(0); - bevelObject = this.toCurve(bevelStructure, dataRepository); - } else { - int bevResol = ((Number)curveStructure.getFieldValue("bevresol")).intValue(); - float extrude = ((Number)curveStructure.getFieldValue("ext1")).floatValue(); - float bevelDepth = ((Number)curveStructure.getFieldValue("ext2")).floatValue(); - if(bevelDepth>0.0f) { - float handlerLength = bevelDepth/2.0f; - - List conrtolPoints = new ArrayList(extrude>0.0f ? 19 : 13); - conrtolPoints.add(new Vector3f(-bevelDepth,extrude,0)); - conrtolPoints.add(new Vector3f(-bevelDepth,handlerLength+extrude,0)); - - conrtolPoints.add(new Vector3f(-handlerLength,bevelDepth+extrude,0)); - conrtolPoints.add(new Vector3f(0,bevelDepth+extrude,0)); - conrtolPoints.add(new Vector3f(handlerLength,bevelDepth+extrude,0)); - - conrtolPoints.add(new Vector3f(bevelDepth,extrude + handlerLength,0)); - conrtolPoints.add(new Vector3f(bevelDepth,extrude,0)); - conrtolPoints.add(new Vector3f(bevelDepth,extrude - handlerLength,0)); - - if(extrude>0.0f) { - conrtolPoints.add(new Vector3f(bevelDepth,-extrude + handlerLength,0)); - conrtolPoints.add(new Vector3f(bevelDepth,-extrude,0)); - conrtolPoints.add(new Vector3f(bevelDepth,-extrude-handlerLength,0)); - } - - conrtolPoints.add(new Vector3f(handlerLength,-bevelDepth-extrude,0)); - conrtolPoints.add(new Vector3f(0,-bevelDepth-extrude,0)); - conrtolPoints.add(new Vector3f(-handlerLength,-bevelDepth-extrude,0)); - - conrtolPoints.add(new Vector3f(-bevelDepth,-handlerLength - extrude,0)); - conrtolPoints.add(new Vector3f(-bevelDepth,-extrude,0)); - - if(extrude>0.0f) { - conrtolPoints.add(new Vector3f(-bevelDepth,handlerLength - extrude,0)); - - conrtolPoints.add(new Vector3f(-bevelDepth,-handlerLength + extrude,0)); - conrtolPoints.add(new Vector3f(-bevelDepth,extrude,0)); - } - - Spline bevelSpline = new Spline(SplineType.Bezier, conrtolPoints, 0, false); - Curve bevelCurve = new Curve(bevelSpline, bevResol); - bevelObject = new ArrayList(1); - bevelObject.add(new Geometry("", bevelCurve)); - } else if(extrude>0.0f) { - Spline bevelSpline = new Spline(SplineType.Linear, new Vector3f[] { - new Vector3f(0, extrude, 0), new Vector3f(0, -extrude, 0) - }, 1, false); - Curve bevelCurve = new Curve(bevelSpline, bevResol); - bevelObject = new ArrayList(1); - bevelObject.add(new Geometry("", bevelCurve)); - } - } - - //getting taper object - Curve taperObject = null; - Pointer pTaperObject = (Pointer) curveStructure.getFieldValue("taperobj"); - if(bevelObject!=null && !pTaperObject.isNull()) { - Pointer pTaperStructure = (Pointer) pTaperObject.fetchData(dataRepository.getInputStream()).get(0).getFieldValue("data"); - Structure taperStructure = pTaperStructure.fetchData(dataRepository.getInputStream()).get(0); - taperObject = this.loadTaperObject(taperStructure, dataRepository); - } - - Vector3f loc = this.getLoc(curveStructure); - //creating the result curves - List result = new ArrayList(nurbs.size()); - for(Entry> nurbEntry : nurbs.entrySet()) { - for(Structure nurb : nurbEntry.getValue()) { - int type = ((Number)nurb.getFieldValue("type")).intValue(); - List nurbGeoms = null; - if((type & 0x01)!=0) {//Bezier curve - nurbGeoms = this.loadBezierCurve(loc, nurb, bevelObject, taperObject, dataRepository); - } else if((type & 0x04)!=0) {//NURBS - nurbGeoms = this.loadNurb(loc, nurb, bevelObject, taperObject, dataRepository); - } - if(nurbGeoms!=null) {//setting the name and assigning materials - for(Geometry nurbGeom : nurbGeoms) { - nurbGeom.setMaterial(materials[nurbEntry.getKey().intValue()]); - nurbGeom.setName(name); - result.add(nurbGeom); - } - } - } - } - return result; - } - - /** - * This method loads the bezier curve. - * @param loc - * the translation of the curve - * @param nurb - * the nurb structure - * @param bevelObject - * the bevel object - * @param taperObject - * the taper object - * @param dataRepository - * the data repository - * @return a list of geometries representing the curves - * @throws BlenderFileException - * an exception is thrown when there are problems with the blender file - */ - protected List loadBezierCurve(Vector3f loc, Structure nurb, List bevelObject, Curve taperObject, - DataRepository dataRepository) throws BlenderFileException { - Pointer pBezierTriple = (Pointer) nurb.getFieldValue("bezt"); - List result = new ArrayList(); - if(!pBezierTriple.isNull()) { - boolean smooth = (((Number)nurb.getFlatFieldValue("flag")).intValue() & 0x01) !=0; - int resolution = ((Number)nurb.getFieldValue("resolu")).intValue(); - boolean cyclic = (((Number)nurb.getFieldValue("flagu")).intValue() & 0x01) != 0; - - //creating the curve object - BezierCurve bezierCurve = new BezierCurve(0, pBezierTriple.fetchData(dataRepository.getInputStream()), 3); - List controlPoints = bezierCurve.getControlPoints(); - if(cyclic) { - //copy the first three points at the end - for(int i=0;i<3;++i) { - controlPoints.add(controlPoints.get(i)); - } - } - //removing the first and last handles - controlPoints.remove(0); - controlPoints.remove(controlPoints.size()-1); - - //creating curve - Spline spline = new Spline(SplineType.Bezier, controlPoints, 0, false); - Curve curve = new Curve(spline, resolution); - if(bevelObject==null) {//creating a normal curve - Geometry curveGeometry = new Geometry(null, curve); - result.add(curveGeometry); - //TODO: use front and back flags; surface excluding algorithm for bezier circles should be added - } else {//creating curve with bevel and taper shape - result = this.applyBevelAndTaper(curve, bevelObject, taperObject, smooth, dataRepository); - } - } - return result; - } - - /** - * This method loads the NURBS curve or surface. - * @param loc - * object's location - * @param nurb - * the NURBS data structure - * @param bevelObject - * the bevel object to be applied - * @param taperObject - * the taper object to be applied - * @param dataRepository - * the data repository - * @return a list of geometries that represents the loaded NURBS curve or surface - * @throws BlenderFileException - * an exception is throw when problems with blender loaded data occurs - */ - @SuppressWarnings("unchecked") - protected List loadNurb(Vector3f loc, Structure nurb, List bevelObject, Curve taperObject, - DataRepository dataRepository) throws BlenderFileException { - //loading the knots - List[] knots = new List[2]; - Pointer[] pKnots = new Pointer[] { (Pointer) nurb.getFieldValue("knotsu"), (Pointer) nurb.getFieldValue("knotsv") }; - for(int i=0;i(knotsAmount); - for(int j=0;j bPoints = ((Pointer)nurb.getFieldValue("bp")).fetchData(dataRepository.getInputStream()); - List> controlPoints = new ArrayList>(pntsV); - for(int i=0;i uControlPoints = new ArrayList(pntsU); - for(int j=0;j vec = (DynamicArray)bPoints.get(j + i*pntsU).getFieldValue("vec"); - if(fixUpAxis) { - uControlPoints.add(new Vector4f(vec.get(0).floatValue(), vec.get(2).floatValue(), -vec.get(1).floatValue(), vec.get(3).floatValue())); - } else { - uControlPoints.add(new Vector4f(vec.get(0).floatValue(), vec.get(1).floatValue(), vec.get(2).floatValue(), vec.get(3).floatValue())); - } - } - if((flagU & 0x01) != 0) { - for(int k=0;k result; - if(knots[1]==null) {//creating the curve - Spline nurbSpline = new Spline(controlPoints.get(0), knots[0]); - Curve nurbCurve = new Curve(nurbSpline, resolu); - if(bevelObject!=null) { - result = this.applyBevelAndTaper(nurbCurve, bevelObject, taperObject, true, dataRepository);//TODO: smooth - } else { - result = new ArrayList(1); - Geometry nurbGeometry = new Geometry("", nurbCurve); - result.add(nurbGeometry); - } - } else {//creating the nurb surface - int resolv = ((Number)nurb.getFieldValue("resolv")).intValue() + 1; - Surface nurbSurface = Surface.createNurbsSurface(controlPoints, knots, resolu, resolv, orderU, orderV); - Geometry nurbGeometry = new Geometry("", nurbSurface); - result = new ArrayList(1); - result.add(nurbGeometry); - } - return result; - } - - /** - * This method returns the taper scale that should be applied to the object. - * @param taperPoints - * the taper points - * @param taperLength - * the taper curve length - * @param percent - * the percent of way along the whole taper curve - * @param store - * the vector where the result will be stored - */ - protected float getTaperScale(float[] taperPoints, float taperLength, float percent) { - float length = taperLength * percent; - float currentLength = 0; - Vector3f p = new Vector3f(); - int i; - for(i=0;i applyBevelAndTaper(Curve curve, List bevelObject, Curve taperObject, - boolean smooth, DataRepository dataRepository) { - float[] curvePoints = BufferUtils.getFloatArray(curve.getFloatBuffer(Type.Position)); - MeshHelper meshHelper = dataRepository.getHelper(MeshHelper.class); - float curveLength = curve.getLength(); - //TODO: use the smooth var - - //taper data - float[] taperPoints = null; - float taperLength = 0; - if(taperObject!=null) { - taperPoints = BufferUtils.getFloatArray(taperObject.getFloatBuffer(Type.Position)); - taperLength = taperObject.getLength(); - } - - //several objects can be allocated only once - Vector3f p = new Vector3f(); - Vector3f z = new Vector3f(0,0,1); - Vector3f negativeY = new Vector3f(0, -1, 0); - Matrix4f m = new Matrix4f(); - float lengthAlongCurve = 0, taperScale = 1.0f; - Quaternion planeRotation = new Quaternion(); - Quaternion zRotation = new Quaternion(); - float[] temp = new float[] {0,0,0,1}; - Map normalMap = new HashMap();//normalMap merges normals of faces that will be rendered smooth - - FloatBuffer[] vertexBuffers = new FloatBuffer[bevelObject.size()]; - FloatBuffer[] normalBuffers = new FloatBuffer[bevelObject.size()]; - IntBuffer[] indexBuffers = new IntBuffer[bevelObject.size()]; - for(int geomIndex = 0;geomIndex=curvePoints.length) { - v = new Vector3f(p.x - curvePoints[i-3], p.y - curvePoints[i-2], p.z - curvePoints[i-1]); - lengthAlongCurve += v.length(); - } else { - v = new Vector3f(curvePoints[i+3] - curvePoints[i-3], - curvePoints[i+4] - curvePoints[i-2], - curvePoints[i+5] - curvePoints[i-1]); - lengthAlongCurve += new Vector3f(curvePoints[i+3] - p.x, curvePoints[i+4] - p.y, curvePoints[i+5] - p.z).length(); - } - v.normalizeLocal(); - - float angle = FastMath.acos(v.dot(z)); - v.crossLocal(z).normalizeLocal();//v is the rotation axis now - planeRotation.fromAngleAxis(angle, v); - - Vector3f zAxisRotationVector = negativeY.cross(v).normalizeLocal(); - float zAxisRotationAngle = FastMath.acos(negativeY.dot(v)); - zRotation.fromAngleAxis(zAxisRotationAngle, zAxisRotationVector); - - //point transformation matrix - if(taperPoints!=null) { - taperScale = this.getTaperScale(taperPoints, taperLength, lengthAlongCurve / curveLength); - } - m.set(Matrix4f.IDENTITY); - m.setRotationQuaternion(planeRotation.multLocal(zRotation)); - m.setTranslation(p); - - //these vertices need to be thrown on XY plane - //and moved to the origin of [p1.x, p1.y] on the plane - Vector3f[] verts = new Vector3f[vertices.length/3]; - for(int j=0;j result = new ArrayList(vertexBuffers.length); - for(int i=0;i nurbStructures = ((Structure)taperStructure.getFieldValue("nurb")).evaluateListBase(dataRepository); - for(Structure nurb : nurbStructures) { - Pointer pBezierTriple = (Pointer) nurb.getFieldValue("bezt"); - if(!pBezierTriple.isNull()) { - //creating the curve object - BezierCurve bezierCurve = new BezierCurve(0, pBezierTriple.fetchData(dataRepository.getInputStream()), 3); - List controlPoints = bezierCurve.getControlPoints(); - //removing the first and last handles - controlPoints.remove(0); - controlPoints.remove(controlPoints.size()-1); - - //return the first taper curve that has more than 3 control points - if(controlPoints.size()>3) { - Spline spline = new Spline(SplineType.Bezier, controlPoints, 0, false); - int resolution = ((Number)taperStructure.getFieldValue("resolu")).intValue(); - return new Curve(spline, resolution); - } - } - } - return null; - } - - /** - * This method returns the translation of the curve. The UP axis is taken into account here. - * @param curveStructure - * the curve structure - * @return curve translation - */ - @SuppressWarnings("unchecked") - protected Vector3f getLoc(Structure curveStructure) { - DynamicArray locArray = (DynamicArray)curveStructure.getFieldValue("loc"); - if(fixUpAxis) { - return new Vector3f(locArray.get(0).floatValue(), locArray.get(1).floatValue(), -locArray.get(2).floatValue()); - } else { - return new Vector3f(locArray.get(0).floatValue(), locArray.get(2).floatValue(), locArray.get(1).floatValue()); - } - } + + private static final Logger LOGGER = Logger.getLogger(CurvesHelper.class.getName()); + /** This variable indicates if the Y asxis is the UP axis or not. */ + protected boolean fixUpAxis; + /** Minimum basis U function degree for NURBS curves and surfaces. */ + protected int minimumBasisUFunctionDegree = 4; + /** Minimum basis V function degree for NURBS curves and surfaces. */ + protected int minimumBasisVFunctionDegree = 4; + + /** + * This constructor parses the given blender version and stores the result. Some functionalities may differ in + * different blender versions. + * @param blenderVersion + * the version read from the blend file + */ + public CurvesHelper(String blenderVersion) { + super(blenderVersion); + } + + /** + * This method sets the Y is UP axis. By default the UP axis is Z (just like in blender). + * @param fixUpAxis + * a variable that indicates if the Y asxis is the UP axis or not + */ + public void setyIsUpAxis(boolean fixUpAxis) { + this.fixUpAxis = fixUpAxis; + } + + /** + * This method converts given curve structure into a list of geometries representing the curve. The list is used here because on object + * can have several separate curves. + * @param curveStructure + * the curve structure + * @param dataRepository + * the data repository + * @return a list of geometries repreenting a single curve object + * @throws BlenderFileException + */ + public List toCurve(Structure curveStructure, DataRepository dataRepository) throws BlenderFileException { + String name = curveStructure.getName(); + int flag = ((Number) curveStructure.getFieldValue("flag")).intValue(); + boolean is3D = (flag & 0x01) != 0; + boolean isFront = (flag & 0x02) != 0 && !is3D; + boolean isBack = (flag & 0x04) != 0 && !is3D; + if (isFront) { + LOGGER.warning("No front face in curve implemented yet!");//TODO: implement front face + } + if (isBack) { + LOGGER.warning("No back face in curve implemented yet!");//TODO: implement back face + } + + //reading nurbs (and sorting them by material) + List nurbStructures = ((Structure) curveStructure.getFieldValue("nurb")).evaluateListBase(dataRepository); + Map> nurbs = new HashMap>(); + for (Structure nurb : nurbStructures) { + Number matNumber = (Number) nurb.getFieldValue("mat_nr"); + List nurbList = nurbs.get(matNumber); + if (nurbList == null) { + nurbList = new ArrayList(); + nurbs.put(matNumber, nurbList); + } + nurbList.add(nurb); + } + + //getting materials + MaterialHelper materialHelper = dataRepository.getHelper(MaterialHelper.class); + Material[] materials = materialHelper.getMaterials(curveStructure, dataRepository); + if (materials == null) { + materials = new Material[]{dataRepository.getDefaultMaterial().clone()}; + } + for (Material material : materials) { + material.getAdditionalRenderState().setFaceCullMode(FaceCullMode.Off); + } + + //getting or creating bevel object + List bevelObject = null; + Pointer pBevelObject = (Pointer) curveStructure.getFieldValue("bevobj"); + if (!pBevelObject.isNull()) { + Pointer pBevelStructure = (Pointer) pBevelObject.fetchData(dataRepository.getInputStream()).get(0).getFieldValue("data"); + Structure bevelStructure = pBevelStructure.fetchData(dataRepository.getInputStream()).get(0); + bevelObject = this.toCurve(bevelStructure, dataRepository); + } else { + int bevResol = ((Number) curveStructure.getFieldValue("bevresol")).intValue(); + float extrude = ((Number) curveStructure.getFieldValue("ext1")).floatValue(); + float bevelDepth = ((Number) curveStructure.getFieldValue("ext2")).floatValue(); + if (bevelDepth > 0.0f) { + float handlerLength = bevelDepth / 2.0f; + + List conrtolPoints = new ArrayList(extrude > 0.0f ? 19 : 13); + conrtolPoints.add(new Vector3f(-bevelDepth, extrude, 0)); + conrtolPoints.add(new Vector3f(-bevelDepth, handlerLength + extrude, 0)); + + conrtolPoints.add(new Vector3f(-handlerLength, bevelDepth + extrude, 0)); + conrtolPoints.add(new Vector3f(0, bevelDepth + extrude, 0)); + conrtolPoints.add(new Vector3f(handlerLength, bevelDepth + extrude, 0)); + + conrtolPoints.add(new Vector3f(bevelDepth, extrude + handlerLength, 0)); + conrtolPoints.add(new Vector3f(bevelDepth, extrude, 0)); + conrtolPoints.add(new Vector3f(bevelDepth, extrude - handlerLength, 0)); + + if (extrude > 0.0f) { + conrtolPoints.add(new Vector3f(bevelDepth, -extrude + handlerLength, 0)); + conrtolPoints.add(new Vector3f(bevelDepth, -extrude, 0)); + conrtolPoints.add(new Vector3f(bevelDepth, -extrude - handlerLength, 0)); + } + + conrtolPoints.add(new Vector3f(handlerLength, -bevelDepth - extrude, 0)); + conrtolPoints.add(new Vector3f(0, -bevelDepth - extrude, 0)); + conrtolPoints.add(new Vector3f(-handlerLength, -bevelDepth - extrude, 0)); + + conrtolPoints.add(new Vector3f(-bevelDepth, -handlerLength - extrude, 0)); + conrtolPoints.add(new Vector3f(-bevelDepth, -extrude, 0)); + + if (extrude > 0.0f) { + conrtolPoints.add(new Vector3f(-bevelDepth, handlerLength - extrude, 0)); + + conrtolPoints.add(new Vector3f(-bevelDepth, -handlerLength + extrude, 0)); + conrtolPoints.add(new Vector3f(-bevelDepth, extrude, 0)); + } + + Spline bevelSpline = new Spline(SplineType.Bezier, conrtolPoints, 0, false); + Curve bevelCurve = new Curve(bevelSpline, bevResol); + bevelObject = new ArrayList(1); + bevelObject.add(new Geometry("", bevelCurve)); + } else if (extrude > 0.0f) { + Spline bevelSpline = new Spline(SplineType.Linear, new Vector3f[]{ + new Vector3f(0, extrude, 0), new Vector3f(0, -extrude, 0) + }, 1, false); + Curve bevelCurve = new Curve(bevelSpline, bevResol); + bevelObject = new ArrayList(1); + bevelObject.add(new Geometry("", bevelCurve)); + } + } + + //getting taper object + Curve taperObject = null; + Pointer pTaperObject = (Pointer) curveStructure.getFieldValue("taperobj"); + if (bevelObject != null && !pTaperObject.isNull()) { + Pointer pTaperStructure = (Pointer) pTaperObject.fetchData(dataRepository.getInputStream()).get(0).getFieldValue("data"); + Structure taperStructure = pTaperStructure.fetchData(dataRepository.getInputStream()).get(0); + taperObject = this.loadTaperObject(taperStructure, dataRepository); + } + + Vector3f loc = this.getLoc(curveStructure); + //creating the result curves + List result = new ArrayList(nurbs.size()); + for (Entry> nurbEntry : nurbs.entrySet()) { + for (Structure nurb : nurbEntry.getValue()) { + int type = ((Number) nurb.getFieldValue("type")).intValue(); + List nurbGeoms = null; + if ((type & 0x01) != 0) {//Bezier curve + nurbGeoms = this.loadBezierCurve(loc, nurb, bevelObject, taperObject, dataRepository); + } else if ((type & 0x04) != 0) {//NURBS + nurbGeoms = this.loadNurb(loc, nurb, bevelObject, taperObject, dataRepository); + } + if (nurbGeoms != null) {//setting the name and assigning materials + for (Geometry nurbGeom : nurbGeoms) { + nurbGeom.setMaterial(materials[nurbEntry.getKey().intValue()]); + nurbGeom.setName(name); + result.add(nurbGeom); + } + } + } + } + return result; + } + + /** + * This method loads the bezier curve. + * @param loc + * the translation of the curve + * @param nurb + * the nurb structure + * @param bevelObject + * the bevel object + * @param taperObject + * the taper object + * @param dataRepository + * the data repository + * @return a list of geometries representing the curves + * @throws BlenderFileException + * an exception is thrown when there are problems with the blender file + */ + protected List loadBezierCurve(Vector3f loc, Structure nurb, List bevelObject, Curve taperObject, + DataRepository dataRepository) throws BlenderFileException { + Pointer pBezierTriple = (Pointer) nurb.getFieldValue("bezt"); + List result = new ArrayList(); + if (!pBezierTriple.isNull()) { + boolean smooth = (((Number) nurb.getFlatFieldValue("flag")).intValue() & 0x01) != 0; + int resolution = ((Number) nurb.getFieldValue("resolu")).intValue(); + boolean cyclic = (((Number) nurb.getFieldValue("flagu")).intValue() & 0x01) != 0; + + //creating the curve object + BezierCurve bezierCurve = new BezierCurve(0, pBezierTriple.fetchData(dataRepository.getInputStream()), 3); + List controlPoints = bezierCurve.getControlPoints(); + if (cyclic) { + //copy the first three points at the end + for (int i = 0; i < 3; ++i) { + controlPoints.add(controlPoints.get(i)); + } + } + //removing the first and last handles + controlPoints.remove(0); + controlPoints.remove(controlPoints.size() - 1); + + //creating curve + Spline spline = new Spline(SplineType.Bezier, controlPoints, 0, false); + Curve curve = new Curve(spline, resolution); + if (bevelObject == null) {//creating a normal curve + Geometry curveGeometry = new Geometry(null, curve); + result.add(curveGeometry); + //TODO: use front and back flags; surface excluding algorithm for bezier circles should be added + } else {//creating curve with bevel and taper shape + result = this.applyBevelAndTaper(curve, bevelObject, taperObject, smooth, dataRepository); + } + } + return result; + } + + /** + * This method loads the NURBS curve or surface. + * @param loc + * object's location + * @param nurb + * the NURBS data structure + * @param bevelObject + * the bevel object to be applied + * @param taperObject + * the taper object to be applied + * @param dataRepository + * the data repository + * @return a list of geometries that represents the loaded NURBS curve or surface + * @throws BlenderFileException + * an exception is throw when problems with blender loaded data occurs + */ + @SuppressWarnings("unchecked") + protected List loadNurb(Vector3f loc, Structure nurb, List bevelObject, Curve taperObject, + DataRepository dataRepository) throws BlenderFileException { + //loading the knots + List[] knots = new List[2]; + Pointer[] pKnots = new Pointer[]{(Pointer) nurb.getFieldValue("knotsu"), (Pointer) nurb.getFieldValue("knotsv")}; + for (int i = 0; i < knots.length; ++i) { + if (!pKnots[i].isNull()) { + FileBlockHeader fileBlockHeader = dataRepository.getFileBlock(pKnots[i].getOldMemoryAddress()); + BlenderInputStream blenderInputStream = dataRepository.getInputStream(); + blenderInputStream.setPosition(fileBlockHeader.getBlockPosition()); + int knotsAmount = fileBlockHeader.getCount() * fileBlockHeader.getSize() / 4; + knots[i] = new ArrayList(knotsAmount); + for (int j = 0; j < knotsAmount; ++j) { + knots[i].add(Float.valueOf(blenderInputStream.readFloat())); + } + } + } + + //loading the flags and orders (basis functions degrees) + int flagU = ((Number) nurb.getFieldValue("flagu")).intValue(); + int flagV = ((Number) nurb.getFieldValue("flagv")).intValue(); + int orderU = ((Number) nurb.getFieldValue("orderu")).intValue(); + int orderV = ((Number) nurb.getFieldValue("orderv")).intValue(); + + //loading control points and their weights + int pntsU = ((Number) nurb.getFieldValue("pntsu")).intValue(); + int pntsV = ((Number) nurb.getFieldValue("pntsv")).intValue(); + List bPoints = ((Pointer) nurb.getFieldValue("bp")).fetchData(dataRepository.getInputStream()); + List> controlPoints = new ArrayList>(pntsV); + for (int i = 0; i < pntsV; ++i) { + List uControlPoints = new ArrayList(pntsU); + for (int j = 0; j < pntsU; ++j) { + DynamicArray vec = (DynamicArray) bPoints.get(j + i * pntsU).getFieldValue("vec"); + if (fixUpAxis) { + uControlPoints.add(new Vector4f(vec.get(0).floatValue(), vec.get(2).floatValue(), -vec.get(1).floatValue(), vec.get(3).floatValue())); + } else { + uControlPoints.add(new Vector4f(vec.get(0).floatValue(), vec.get(1).floatValue(), vec.get(2).floatValue(), vec.get(3).floatValue())); + } + } + if ((flagU & 0x01) != 0) { + for (int k = 0; k < orderU - 1; ++k) { + uControlPoints.add(uControlPoints.get(k)); + } + } + controlPoints.add(uControlPoints); + } + if ((flagV & 0x01) != 0) { + for (int k = 0; k < orderV - 1; ++k) { + controlPoints.add(controlPoints.get(k)); + } + } + + int resolu = ((Number) nurb.getFieldValue("resolu")).intValue() + 1; + List result; + if (knots[1] == null) {//creating the curve + Spline nurbSpline = new Spline(controlPoints.get(0), knots[0]); + Curve nurbCurve = new Curve(nurbSpline, resolu); + if (bevelObject != null) { + result = this.applyBevelAndTaper(nurbCurve, bevelObject, taperObject, true, dataRepository);//TODO: smooth + } else { + result = new ArrayList(1); + Geometry nurbGeometry = new Geometry("", nurbCurve); + result.add(nurbGeometry); + } + } else {//creating the nurb surface + int resolv = ((Number) nurb.getFieldValue("resolv")).intValue() + 1; + Surface nurbSurface = Surface.createNurbsSurface(controlPoints, knots, resolu, resolv, orderU, orderV); + Geometry nurbGeometry = new Geometry("", nurbSurface); + result = new ArrayList(1); + result.add(nurbGeometry); + } + return result; + } + + /** + * This method returns the taper scale that should be applied to the object. + * @param taperPoints + * the taper points + * @param taperLength + * the taper curve length + * @param percent + * the percent of way along the whole taper curve + * @param store + * the vector where the result will be stored + */ + protected float getTaperScale(float[] taperPoints, float taperLength, float percent) { + float length = taperLength * percent; + float currentLength = 0; + Vector3f p = new Vector3f(); + int i; + for (i = 0; i < taperPoints.length - 6 && currentLength < length; i += 3) { + p.set(taperPoints[i], taperPoints[i + 1], taperPoints[i + 2]); + p.subtractLocal(taperPoints[i + 3], taperPoints[i + 4], taperPoints[i + 5]); + currentLength += p.length(); + } + currentLength -= p.length(); + float leftLength = length - currentLength; + float percentOnSegment = p.length() == 0 ? 0 : leftLength / p.length(); + Vector3f store = FastMath.interpolateLinear(percentOnSegment, + new Vector3f(taperPoints[i], taperPoints[i + 1], taperPoints[i + 2]), + new Vector3f(taperPoints[i + 3], taperPoints[i + 4], taperPoints[i + 5])); + return store.y; + } + + /** + * This method applies bevel and taper objects to the curve. + * @param curve + * the curve we apply the objects to + * @param bevelObject + * the bevel object + * @param taperObject + * the taper object + * @param smooth + * the smooth flag + * @param dataRepository + * the data repository + * @return a list of geometries representing the beveled and/or tapered curve + */ + protected List applyBevelAndTaper(Curve curve, List bevelObject, Curve taperObject, + boolean smooth, DataRepository dataRepository) { + float[] curvePoints = BufferUtils.getFloatArray(curve.getFloatBuffer(Type.Position)); + MeshHelper meshHelper = dataRepository.getHelper(MeshHelper.class); + float curveLength = curve.getLength(); + //TODO: use the smooth var + + //taper data + float[] taperPoints = null; + float taperLength = 0; + if (taperObject != null) { + taperPoints = BufferUtils.getFloatArray(taperObject.getFloatBuffer(Type.Position)); + taperLength = taperObject.getLength(); + } + + //several objects can be allocated only once + Vector3f p = new Vector3f(); + Vector3f z = new Vector3f(0, 0, 1); + Vector3f negativeY = new Vector3f(0, -1, 0); + Matrix4f m = new Matrix4f(); + float lengthAlongCurve = 0, taperScale = 1.0f; + Quaternion planeRotation = new Quaternion(); + Quaternion zRotation = new Quaternion(); + float[] temp = new float[]{0, 0, 0, 1}; + Map normalMap = new HashMap();//normalMap merges normals of faces that will be rendered smooth + + FloatBuffer[] vertexBuffers = new FloatBuffer[bevelObject.size()]; + FloatBuffer[] normalBuffers = new FloatBuffer[bevelObject.size()]; + IntBuffer[] indexBuffers = new IntBuffer[bevelObject.size()]; + for (int geomIndex = 0; geomIndex < bevelObject.size(); ++geomIndex) { + Mesh mesh = bevelObject.get(geomIndex).getMesh(); + FloatBuffer positions = mesh.getFloatBuffer(Type.Position); + float[] vertices = BufferUtils.getFloatArray(positions); + + for (int i = 0; i < curvePoints.length; i += 3) { + p.set(curvePoints[i], curvePoints[i + 1], curvePoints[i + 2]); + Vector3f v; + if (i == 0) { + v = new Vector3f(curvePoints[3] - p.x, curvePoints[4] - p.y, curvePoints[5] - p.z); + } else if (i + 3 >= curvePoints.length) { + v = new Vector3f(p.x - curvePoints[i - 3], p.y - curvePoints[i - 2], p.z - curvePoints[i - 1]); + lengthAlongCurve += v.length(); + } else { + v = new Vector3f(curvePoints[i + 3] - curvePoints[i - 3], + curvePoints[i + 4] - curvePoints[i - 2], + curvePoints[i + 5] - curvePoints[i - 1]); + lengthAlongCurve += new Vector3f(curvePoints[i + 3] - p.x, curvePoints[i + 4] - p.y, curvePoints[i + 5] - p.z).length(); + } + v.normalizeLocal(); + + float angle = FastMath.acos(v.dot(z)); + v.crossLocal(z).normalizeLocal();//v is the rotation axis now + planeRotation.fromAngleAxis(angle, v); + + Vector3f zAxisRotationVector = negativeY.cross(v).normalizeLocal(); + float zAxisRotationAngle = FastMath.acos(negativeY.dot(v)); + zRotation.fromAngleAxis(zAxisRotationAngle, zAxisRotationVector); + + //point transformation matrix + if (taperPoints != null) { + taperScale = this.getTaperScale(taperPoints, taperLength, lengthAlongCurve / curveLength); + } + m.set(Matrix4f.IDENTITY); + m.setRotationQuaternion(planeRotation.multLocal(zRotation)); + m.setTranslation(p); + + //these vertices need to be thrown on XY plane + //and moved to the origin of [p1.x, p1.y] on the plane + Vector3f[] verts = new Vector3f[vertices.length / 3]; + for (int j = 0; j < verts.length; ++j) { + temp[0] = vertices[j * 3] * taperScale; + temp[1] = vertices[j * 3 + 1] * taperScale; + temp[2] = 0; + m.mult(temp);//the result is stored in the array + if (fixUpAxis) { + verts[j] = new Vector3f(temp[0], temp[1], temp[2]); + } else { + verts[j] = new Vector3f(temp[0], temp[2], -temp[1]); + } + } + if (vertexBuffers[geomIndex] == null) { + vertexBuffers[geomIndex] = BufferUtils.createFloatBuffer(verts.length * curvePoints.length); + } + FloatBuffer buffer = BufferUtils.createFloatBuffer(verts); + vertexBuffers[geomIndex].put(buffer); + + //adding indexes + IntBuffer indexBuffer = indexBuffers[geomIndex]; + if (indexBuffer == null) { + //the amount of faces in the final mesh is the amount of edges in the bevel curve + //(which is less by 1 than its number of vertices) + //multiplied by 2 (because each edge has two faces assigned on both sides) + //and multiplied by the amount of bevel curve repeats which is equal to the amount of vertices on the target curve + //finally we need to subtract the bevel edges amount 2 times because the border edges have only one face attached + //and at last multiply everything by 3 because each face needs 3 indexes to be described + int bevelCurveEdgesAmount = verts.length - 1; + indexBuffer = BufferUtils.createIntBuffer(((bevelCurveEdgesAmount << 1) * curvePoints.length - bevelCurveEdgesAmount << 1) * 3); + indexBuffers[geomIndex] = indexBuffer; + } + int pointOffset = i / 3 * verts.length; + if (i + 3 < curvePoints.length) { + for (int index = 0; index < verts.length - 1; ++index) { + indexBuffer.put(index + pointOffset); + indexBuffer.put(index + pointOffset + 1); + indexBuffer.put(verts.length + index + pointOffset); + indexBuffer.put(verts.length + index + pointOffset); + indexBuffer.put(index + pointOffset + 1); + indexBuffer.put(verts.length + index + pointOffset + 1); + } + } + } + } + + //calculating the normals + for (int geomIndex = 0; geomIndex < bevelObject.size(); ++geomIndex) { + Vector3f[] allVerts = BufferUtils.getVector3Array(vertexBuffers[geomIndex]); + int[] allIndices = BufferUtils.getIntArray(indexBuffers[geomIndex]); + for (int i = 0; i < allIndices.length - 3; i += 3) { + Vector3f n = FastMath.computeNormal(allVerts[allIndices[i]], allVerts[allIndices[i + 1]], allVerts[allIndices[i + 2]]); + meshHelper.addNormal(n, normalMap, smooth, allVerts[allIndices[i]], allVerts[allIndices[i + 1]], allVerts[allIndices[i + 2]]); + } + if (normalBuffers[geomIndex] == null) { + normalBuffers[geomIndex] = BufferUtils.createFloatBuffer(allVerts.length * 3); + } + for (Vector3f v : allVerts) { + Vector3f n = normalMap.get(v); + normalBuffers[geomIndex].put(n.x); + normalBuffers[geomIndex].put(n.y); + normalBuffers[geomIndex].put(n.z); + } + } + + List result = new ArrayList(vertexBuffers.length); + for (int i = 0; i < vertexBuffers.length; ++i) { + Mesh mesh = new Mesh(); + mesh.setBuffer(Type.Position, 3, vertexBuffers[i]); + mesh.setBuffer(Type.Index, 3, indexBuffers[i]); + mesh.setBuffer(Type.Normal, 3, normalBuffers[i]); + Geometry g = new Geometry("g" + i, mesh); + g.updateModelBound(); + result.add(g); + } + + return result; + } + + /** + * This method loads the taper object. + * @param taperStructure + * the taper structure + * @param dataRepository + * the data repository + * @return the taper object + * @throws BlenderFileException + */ + protected Curve loadTaperObject(Structure taperStructure, DataRepository dataRepository) throws BlenderFileException { + //reading nurbs + List nurbStructures = ((Structure) taperStructure.getFieldValue("nurb")).evaluateListBase(dataRepository); + for (Structure nurb : nurbStructures) { + Pointer pBezierTriple = (Pointer) nurb.getFieldValue("bezt"); + if (!pBezierTriple.isNull()) { + //creating the curve object + BezierCurve bezierCurve = new BezierCurve(0, pBezierTriple.fetchData(dataRepository.getInputStream()), 3); + List controlPoints = bezierCurve.getControlPoints(); + //removing the first and last handles + controlPoints.remove(0); + controlPoints.remove(controlPoints.size() - 1); + + //return the first taper curve that has more than 3 control points + if (controlPoints.size() > 3) { + Spline spline = new Spline(SplineType.Bezier, controlPoints, 0, false); + int resolution = ((Number) taperStructure.getFieldValue("resolu")).intValue(); + return new Curve(spline, resolution); + } + } + } + return null; + } + + /** + * This method returns the translation of the curve. The UP axis is taken into account here. + * @param curveStructure + * the curve structure + * @return curve translation + */ + @SuppressWarnings("unchecked") + protected Vector3f getLoc(Structure curveStructure) { + DynamicArray locArray = (DynamicArray) curveStructure.getFieldValue("loc"); + if (fixUpAxis) { + return new Vector3f(locArray.get(0).floatValue(), locArray.get(1).floatValue(), -locArray.get(2).floatValue()); + } else { + return new Vector3f(locArray.get(0).floatValue(), locArray.get(2).floatValue(), locArray.get(1).floatValue()); + } + } } diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/helpers/v249/IpoHelper.java b/engine/src/blender/com/jme3/scene/plugins/blender/helpers/v249/IpoHelper.java index 1a6eacdc7..280299e25 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/helpers/v249/IpoHelper.java +++ b/engine/src/blender/com/jme3/scene/plugins/blender/helpers/v249/IpoHelper.java @@ -17,98 +17,98 @@ import com.jme3.scene.plugins.blender.utils.Pointer; * @author Marcin Roguski */ public class IpoHelper extends AbstractBlenderHelper { - /** - * This constructor parses the given blender version and stores the result. Some functionalities may differ in - * different blender versions. - * @param blenderVersion - * the version read from the blend file - */ - public IpoHelper(String blenderVersion) { - super(blenderVersion); - } - /** - * This method creates an ipo object used for interpolation calculations. - * @param ipoStructure - * the structure with ipo definition - * @param dataRepository - * the data repository - * @return the ipo object - * @throws BlenderFileException - * this exception is thrown when the blender file is somehow corrupted - */ - public Ipo createIpo(Structure ipoStructure, DataRepository dataRepository) throws BlenderFileException { - Structure curvebase = (Structure)ipoStructure.getFieldValue("curve"); + /** + * This constructor parses the given blender version and stores the result. Some functionalities may differ in + * different blender versions. + * @param blenderVersion + * the version read from the blend file + */ + public IpoHelper(String blenderVersion) { + super(blenderVersion); + } - //preparing bezier curves - Ipo result = null; - List curves = curvebase.evaluateListBase(dataRepository);//IpoCurve - if(curves.size() > 0) { - BezierCurve[] bezierCurves = new BezierCurve[curves.size()]; - int frame = 0; - for(Structure curve : curves) { - Pointer pBezTriple = (Pointer)curve.getFieldValue("bezt"); - List bezTriples = pBezTriple.fetchData(dataRepository.getInputStream()); - int type = ((Number)curve.getFieldValue("adrcode")).intValue(); - bezierCurves[frame++] = new BezierCurve(type, bezTriples, 2); - } - curves.clear(); - result = new Ipo(bezierCurves); - dataRepository.addLoadedFeatures(ipoStructure.getOldMemoryAddress(), ipoStructure.getName(), ipoStructure, result); - } - return result; - } + /** + * This method creates an ipo object used for interpolation calculations. + * @param ipoStructure + * the structure with ipo definition + * @param dataRepository + * the data repository + * @return the ipo object + * @throws BlenderFileException + * this exception is thrown when the blender file is somehow corrupted + */ + public Ipo createIpo(Structure ipoStructure, DataRepository dataRepository) throws BlenderFileException { + Structure curvebase = (Structure) ipoStructure.getFieldValue("curve"); - /** - * This method creates an ipo with only a single value. No track type is specified so do not use it for calculating - * tracks. - * @param constValue - * the value of this ipo - * @return constant ipo - */ - public Ipo createIpo(float constValue) { - return new ConstIpo(constValue); - } + //preparing bezier curves + Ipo result = null; + List curves = curvebase.evaluateListBase(dataRepository);//IpoCurve + if (curves.size() > 0) { + BezierCurve[] bezierCurves = new BezierCurve[curves.size()]; + int frame = 0; + for (Structure curve : curves) { + Pointer pBezTriple = (Pointer) curve.getFieldValue("bezt"); + List bezTriples = pBezTriple.fetchData(dataRepository.getInputStream()); + int type = ((Number) curve.getFieldValue("adrcode")).intValue(); + bezierCurves[frame++] = new BezierCurve(type, bezTriples, 2); + } + curves.clear(); + result = new Ipo(bezierCurves); + dataRepository.addLoadedFeatures(ipoStructure.getOldMemoryAddress(), ipoStructure.getName(), ipoStructure, result); + } + return result; + } - + /** + * This method creates an ipo with only a single value. No track type is specified so do not use it for calculating + * tracks. + * @param constValue + * the value of this ipo + * @return constant ipo + */ + public Ipo createIpo(float constValue) { + return new ConstIpo(constValue); + } - /** - * Ipo constant curve. This is a curve with only one value and no specified type. This type of ipo cannot be used to - * calculate tracks. It should only be used to calculate single value for a given frame. - * @author Marcin Roguski - */ - private class ConstIpo extends Ipo { - /** The constant value of this ipo. */ - private float constValue; + /** + * Ipo constant curve. This is a curve with only one value and no specified type. This type of ipo cannot be used to + * calculate tracks. It should only be used to calculate single value for a given frame. + * @author Marcin Roguski + */ + private class ConstIpo extends Ipo { - /** - * Constructor. Stores the constant value of this ipo. - * @param constValue - * the constant value of this ipo - */ - public ConstIpo(float constValue) { - super(null); - this.constValue = constValue; - } + /** The constant value of this ipo. */ + private float constValue; - @Override - public float calculateValue(int frame) { - return constValue; - } + /** + * Constructor. Stores the constant value of this ipo. + * @param constValue + * the constant value of this ipo + */ + public ConstIpo(float constValue) { + super(null); + this.constValue = constValue; + } - @Override - public float calculateValue(int frame, int curveIndex) { - return constValue; - } + @Override + public float calculateValue(int frame) { + return constValue; + } - @Override - public int getCurvesAmount() { - return 0; - } - - @Override - public BoneTrack calculateTrack(int boneIndex, int startFrame, int stopFrame, int fps) { - throw new IllegalStateException("Constatnt ipo object cannot be used for calculating bone tracks!"); - } - } + @Override + public float calculateValue(int frame, int curveIndex) { + return constValue; + } + + @Override + public int getCurvesAmount() { + return 0; + } + + @Override + public BoneTrack calculateTrack(int boneIndex, int startFrame, int stopFrame, int fps) { + throw new IllegalStateException("Constatnt ipo object cannot be used for calculating bone tracks!"); + } + } } diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/helpers/v249/LightHelper.java b/engine/src/blender/com/jme3/scene/plugins/blender/helpers/v249/LightHelper.java index ce9da9f47..95878907a 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/helpers/v249/LightHelper.java +++ b/engine/src/blender/com/jme3/scene/plugins/blender/helpers/v249/LightHelper.java @@ -49,51 +49,52 @@ import com.jme3.scene.plugins.blender.utils.DataRepository.LoadedFeatureDataType * @author Marcin Roguski */ public class LightHelper extends AbstractBlenderHelper { - private static final Logger LOGGER = Logger.getLogger(LightHelper.class.getName()); - - /** - * This constructor parses the given blender version and stores the result. Some functionalities may differ in - * different blender versions. - * @param blenderVersion - * the version read from the blend file - */ - public LightHelper(String blenderVersion) { - super(blenderVersion); - } - public Light toLight(Structure structure, DataRepository dataRepository) throws BlenderFileException { - Light result = (Light)dataRepository.getLoadedFeature(structure.getOldMemoryAddress(), LoadedFeatureDataType.LOADED_FEATURE); - if(result != null) { - return result; - } - int type = ((Number)structure.getFieldValue("type")).intValue(); - switch(type) { - case 0://Lamp - result = new PointLight(); - float distance = ((Number)structure.getFieldValue("dist")).floatValue(); - ((PointLight)result).setRadius(distance); - break; - case 1://Sun - LOGGER.log(Level.WARNING, "'Sun' lamp is not supported in jMonkeyEngine."); - break; - case 2://Spot - LOGGER.log(Level.WARNING, "'Spot' lamp is not supported in jMonkeyEngine."); - break; - case 3://Hemi - LOGGER.log(Level.WARNING, "'Hemi' lamp is not supported in jMonkeyEngine."); - break; - case 4://Area - result = new DirectionalLight(); - break; - default: - throw new BlenderFileException("Unknown light source type: " + type); - } - if(result != null) { - float r = ((Number)structure.getFieldValue("r")).floatValue(); - float g = ((Number)structure.getFieldValue("g")).floatValue(); - float b = ((Number)structure.getFieldValue("b")).floatValue(); - result.setColor(new ColorRGBA(r, g, b, 0.0f));//TODO: 0 czy 1 ??? - } - return result; - } + private static final Logger LOGGER = Logger.getLogger(LightHelper.class.getName()); + + /** + * This constructor parses the given blender version and stores the result. Some functionalities may differ in + * different blender versions. + * @param blenderVersion + * the version read from the blend file + */ + public LightHelper(String blenderVersion) { + super(blenderVersion); + } + + public Light toLight(Structure structure, DataRepository dataRepository) throws BlenderFileException { + Light result = (Light) dataRepository.getLoadedFeature(structure.getOldMemoryAddress(), LoadedFeatureDataType.LOADED_FEATURE); + if (result != null) { + return result; + } + int type = ((Number) structure.getFieldValue("type")).intValue(); + switch (type) { + case 0://Lamp + result = new PointLight(); + float distance = ((Number) structure.getFieldValue("dist")).floatValue(); + ((PointLight) result).setRadius(distance); + break; + case 1://Sun + LOGGER.log(Level.WARNING, "'Sun' lamp is not supported in jMonkeyEngine."); + break; + case 2://Spot + LOGGER.log(Level.WARNING, "'Spot' lamp is not supported in jMonkeyEngine."); + break; + case 3://Hemi + LOGGER.log(Level.WARNING, "'Hemi' lamp is not supported in jMonkeyEngine."); + break; + case 4://Area + result = new DirectionalLight(); + break; + default: + throw new BlenderFileException("Unknown light source type: " + type); + } + if (result != null) { + float r = ((Number) structure.getFieldValue("r")).floatValue(); + float g = ((Number) structure.getFieldValue("g")).floatValue(); + float b = ((Number) structure.getFieldValue("b")).floatValue(); + result.setColor(new ColorRGBA(r, g, b, 0.0f));//TODO: 0 czy 1 ??? + } + return result; + } } diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/helpers/v249/MaterialHelper.java b/engine/src/blender/com/jme3/scene/plugins/blender/helpers/v249/MaterialHelper.java index c2882b954..bbe107aa7 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/helpers/v249/MaterialHelper.java +++ b/engine/src/blender/com/jme3/scene/plugins/blender/helpers/v249/MaterialHelper.java @@ -62,640 +62,646 @@ import com.jme3.texture.Texture.WrapMode; import com.jme3.util.BufferUtils; public class MaterialHelper extends AbstractBlenderHelper { - private static final Logger LOGGER = Logger.getLogger(MaterialHelper.class.getName()); - protected static final float DEFAULT_SHININESS = 20.0f; - - public static final String TEXTURE_TYPE_COLOR = "ColorMap"; - public static final String TEXTURE_TYPE_DIFFUSE = "DiffuseMap"; - public static final String TEXTURE_TYPE_NORMAL = "NormalMap"; - public static final String TEXTURE_TYPE_SPECULAR = "SpecularMap"; - public static final String TEXTURE_TYPE_GLOW = "GlowMap"; - public static final String TEXTURE_TYPE_ALPHA = "AlphaMap"; - - public static final Integer ALPHA_MASK_NONE = Integer.valueOf(0); - public static final Integer ALPHA_MASK_CIRCLE = Integer.valueOf(1); - public static final Integer ALPHA_MASK_CONE = Integer.valueOf(2); - public static final Integer ALPHA_MASK_HYPERBOLE = Integer.valueOf(3); - protected final Map alphaMasks = new HashMap(); - - /** - * The type of the material's diffuse shader. - */ - public static enum DiffuseShader { - LAMBERT, ORENNAYAR, TOON, MINNAERT, FRESNEL - } - - /** - * The type of the material's specular shader. - */ - public static enum SpecularShader { - COOKTORRENCE, PHONG, BLINN, TOON, WARDISO - } - - /** Face cull mode. Should be excplicitly set before this helper is used. */ - protected FaceCullMode faceCullMode; - - /** - * This constructor parses the given blender version and stores the result. Some functionalities may differ in different blender - * versions. - * - * @param blenderVersion - * the version read from the blend file - */ - public MaterialHelper(String blenderVersion) { - super(blenderVersion); - // setting alpha masks - alphaMasks.put(ALPHA_MASK_NONE, new IAlphaMask() { - @Override - public void setImageSize(int width, int height) {} - - @Override - public byte getAlpha(float x, float y) { - return (byte) 255; - } - }); - alphaMasks.put(ALPHA_MASK_CIRCLE, new IAlphaMask() { - private float r; - private float[] center; - - @Override - public void setImageSize(int width, int height) { - r = Math.min(width, height) * 0.5f; - center = new float[] { width * 0.5f, height * 0.5f }; - } - - @Override - public byte getAlpha(float x, float y) { - float d = FastMath.abs(FastMath.sqrt((x - center[0]) * (x - center[0]) + (y - center[1]) * (y - center[1]))); - return (byte) (d >= r ? 0 : 255); - } - }); - alphaMasks.put(ALPHA_MASK_CONE, new IAlphaMask() { - private float r; - private float[] center; - - @Override - public void setImageSize(int width, int height) { - r = Math.min(width, height) * 0.5f; - center = new float[] { width * 0.5f, height * 0.5f }; - } - - @Override - public byte getAlpha(float x, float y) { - float d = FastMath.abs(FastMath.sqrt((x - center[0]) * (x - center[0]) + (y - center[1]) * (y - center[1]))); - return (byte) (d >= r ? 0 : -255.0f * d / r + 255.0f); - } - }); - alphaMasks.put(ALPHA_MASK_HYPERBOLE, new IAlphaMask() { - private float r; - private float[] center; - - @Override - public void setImageSize(int width, int height) { - r = Math.min(width, height) * 0.5f; - center = new float[] { width * 0.5f, height * 0.5f }; - } - - @Override - public byte getAlpha(float x, float y) { - float d = FastMath.abs(FastMath.sqrt((x - center[0]) * (x - center[0]) + (y - center[1]) * (y - center[1]))) / r; - return d >= 1.0f ? 0 : (byte) ((-FastMath.sqrt((2.0f - d) * d) + 1.0f) * 255.0f); - } - }); - } - - /** - * This method sets the face cull mode to be used with every loaded material. - * - * @param faceCullMode - * the face cull mode - */ - public void setFaceCullMode(FaceCullMode faceCullMode) { - this.faceCullMode = faceCullMode; - } - - @SuppressWarnings("unchecked") - public Material toMaterial(Structure structure, DataRepository dataRepository) throws BlenderFileException { - LOGGER.log(Level.INFO, "Loading material."); - if (structure == null) { - return dataRepository.getDefaultMaterial(); - } - Material result = (Material) dataRepository.getLoadedFeature(structure.getOldMemoryAddress(), LoadedFeatureDataType.LOADED_FEATURE); - if (result != null) { - return result; - } - - int mode = ((Number) structure.getFieldValue("mode")).intValue(); - boolean shadeless = (mode & 0x4) != 0; - boolean vertexColor = (mode & 0x16) != 0; - boolean transparent = (mode & 0x64) != 0; - - if (shadeless) { - result = new Material(dataRepository.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md"); - } else { - result = new Material(dataRepository.getAssetManager(), "Common/MatDefs/Light/Lighting.j3md"); - } - - result.getAdditionalRenderState().setFaceCullMode(faceCullMode); - - if (transparent) { - result.getAdditionalRenderState().setBlendMode(BlendMode.Alpha); - } - - String name = structure.getName(); - LOGGER.log(Level.INFO, "Material's name: {0}", name); - if (vertexColor) { - if (shadeless) { - result.setBoolean("VertexColor", true); - } else { - result.setBoolean("UseVertexColor", true); - } - } - - MaterialHelper materialHelper = dataRepository.getHelper(MaterialHelper.class); - ColorRGBA diffuseColor = null; - if (shadeless) { - // color of shadeless? doesn't seem to work in blender .. - } else { - result.setBoolean("UseMaterialColors", true); - - // setting the colors - DiffuseShader diffuseShader = materialHelper.getDiffuseShader(structure); - result.setBoolean("Minnaert", diffuseShader == DiffuseShader.MINNAERT); - diffuseColor = materialHelper.getDiffuseColor(structure, diffuseShader); - result.setColor("Diffuse", diffuseColor); - - SpecularShader specularShader = materialHelper.getSpecularShader(structure); - result.setBoolean("WardIso", specularShader == SpecularShader.WARDISO); - result.setColor("Specular", materialHelper.getSpecularColor(structure, specularShader)); - - result.setColor("Ambient", materialHelper.getAmbientColor(structure)); - result.setFloat("Shininess", materialHelper.getShininess(structure)); - } - - // texture - if ((dataRepository.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.TEXTURES) != 0) { - TextureHelper textureHelper = dataRepository.getHelper(TextureHelper.class); - DynamicArray mtexs = (DynamicArray) structure.getFieldValue("mtex"); - for (int i = 0; i < mtexs.getTotalSize(); ++i) { - Pointer p = mtexs.get(i); - if (!p.isNull()) { - List mtex = p.fetchData(dataRepository.getInputStream()); - if (mtex.size() == 1) { - Structure textureLink = mtex.get(0); - int texflag = ((Number) textureLink.getFieldValue("texflag")).intValue(); - // int texco = ((Number) textureLink.getFieldValue("texco")).intValue(); - boolean negateTexture = (texflag & 0x04) == 0; - - // if(texco == 0x10) {//TEXCO_UV (this is only supported now) - int mapto = ((Number) textureLink.getFieldValue("mapto")).intValue(); - if (mapto != 0) { - Pointer pTex = (Pointer) textureLink.getFieldValue("tex"); - Structure tex = pTex.fetchData(dataRepository.getInputStream()).get(0); - Texture texture = textureHelper.getTexture(tex, dataRepository); - if (texture != null) { - if ((mapto & 0x01) != 0) {// Col - result.setBoolean("UseMaterialColors", Boolean.FALSE); - // blending the texture with material color and texture's defined color - int blendType = ((Number) textureLink.getFieldValue("blendtype")).intValue(); - float[] color = new float[] { ((Number) textureLink.getFieldValue("r")).floatValue(), ((Number) textureLink.getFieldValue("g")).floatValue(), ((Number) textureLink.getFieldValue("b")).floatValue() }; - float colfac = ((Number) textureLink.getFieldValue("colfac")).floatValue(); - texture = textureHelper.blendTexture(diffuseColor.getColorArray(), texture, color, colfac, blendType, negateTexture, dataRepository); - texture.setWrap(WrapMode.Repeat); - if (shadeless) { - result.setTexture(TEXTURE_TYPE_COLOR, texture); - } else { - result.setTexture(TEXTURE_TYPE_DIFFUSE, texture); - } - } - if ((mapto & 0x02) != 0) {// Nor - result.setTexture(TEXTURE_TYPE_NORMAL, texture); - } - if ((mapto & 0x20) != 0) {// Spec - result.setTexture(TEXTURE_TYPE_SPECULAR, texture); - } - if ((mapto & 0x40) != 0) {// Emit - result.setTexture(TEXTURE_TYPE_GLOW, texture); - } - if ((mapto & 0x80) != 0) {// Alpha - result.setTexture(TEXTURE_TYPE_ALPHA, texture); - } - } else { - LOGGER.log(Level.WARNING, "Texture not found!"); - } - } - // } else { - // Pointer pTex = (Pointer)textureLink.getFieldValue("tex"); - // List texs = pTex.fetchData(dataRepository.getInputStream()); - // Structure tex = texs.get(0); - // LOGGER.log(Level.WARNING, "Unsupported texture type: " + texco); - // } - } else { - LOGGER.log(Level.WARNING, "Many textures. Not solved yet!");// TODO - } - } - } - } - dataRepository.addLoadedFeatures(structure.getOldMemoryAddress(), structure.getName(), structure, result); - return result; - } - - /** - * This method returns a material similar to the one given but without textures. If the material has no textures it is not cloned but - * returned itself. - * - * @param material - * a material to be cloned without textures - * @param imageType - * type of image defined by blender; the constants are defined in TextureHelper - * @return material without textures of a specified type - */ - public Material getNonTexturedMaterial(Material material, int imageType) { - String[] textureParamNames = new String[] { TEXTURE_TYPE_DIFFUSE, TEXTURE_TYPE_NORMAL, TEXTURE_TYPE_GLOW, TEXTURE_TYPE_SPECULAR, TEXTURE_TYPE_ALPHA }; - Map textures = new HashMap(textureParamNames.length); - for (String textureParamName : textureParamNames) { - MatParamTexture matParamTexture = material.getTextureParam(textureParamName); - if (matParamTexture != null) { - textures.put(textureParamName, matParamTexture.getTextureValue()); - } - } - if (textures.isEmpty()) { - return material; - } else { - // clear all textures first so that wo de not waste resources cloning them - for (Entry textureParamName : textures.entrySet()) { - String name = textureParamName.getValue().getName(); - try { - int type = Integer.parseInt(name); - if (type == imageType) { - material.clearParam(textureParamName.getKey()); - } - } catch (NumberFormatException e) { - LOGGER.log(Level.WARNING, "The name of the texture does not contain the texture type value! {0} will not be removed!", name); - } - } - Material result = material.clone(); - // put the textures back in place - for (Entry textureEntry : textures.entrySet()) { - material.setTexture(textureEntry.getKey(), textureEntry.getValue()); - } - return result; - } - } - - /** - * This method converts the given material into particles-usable material. - * The texture and glow color are being copied. - * The method assumes it receives the Lighting type of material. - * @param material - * the source material - * @param dataRepository - * the data repository - * @return material converted into particles-usable material - */ - public Material getParticlesMaterial(Material material, Integer alphaMaskIndex, DataRepository dataRepository) { - Material result = new Material(dataRepository.getAssetManager(), "Common/MatDefs/Misc/Particle.j3md"); - - // copying texture - MatParam diffuseMap = material.getParam("DiffuseMap"); - if (diffuseMap != null) { - Texture texture = ((Texture) diffuseMap.getValue()).clone(); - - // applying alpha mask to the texture - Image image = texture.getImage(); - ByteBuffer sourceBB = image.getData(0); - sourceBB.rewind(); - int w = image.getWidth(); - int h = image.getHeight(); - ByteBuffer bb = BufferUtils.createByteBuffer(w * h * 4); - IAlphaMask iAlphaMask = alphaMasks.get(alphaMaskIndex); - iAlphaMask.setImageSize(w, h); - - for (int x = 0; x < w; ++x) { - for (int y = 0; y < h; ++y) { - bb.put(sourceBB.get()); - bb.put(sourceBB.get()); - bb.put(sourceBB.get()); - bb.put(iAlphaMask.getAlpha(x, y)); - } - } - - image = new Image(Format.RGBA8, w, h, bb); - texture.setImage(image); - - result.setTextureParam("Texture", VarType.Texture2D, texture); - } - - // copying glow color - MatParam glowColor = material.getParam("GlowColor"); - if (glowColor != null) { - ColorRGBA color = (ColorRGBA) glowColor.getValue(); - result.setParam("GlowColor", VarType.Vector3, color); - } - return result; - } - - /** - * This method indicates if the material has a texture of a specified type. - * - * @param material - * the material - * @param textureType - * the type of the texture - * @return true if the texture exists in the material and false otherwise - */ - public boolean hasTexture(Material material, String textureType) { - if (material != null) { - return material.getTextureParam(textureType) != null; - } - return false; - } - - /** - * This method returns the diffuse color - * - * @param materialStructure - * @param diffuseShader - * @return - */ - public ColorRGBA getDiffuseColor(Structure materialStructure, DiffuseShader diffuseShader) { - // bitwise 'or' of all textures mappings - int commonMapto = ((Number) materialStructure.getFieldValue("mapto")).intValue(); - - // diffuse color - float r = ((Number) materialStructure.getFieldValue("r")).floatValue(); - float g = ((Number) materialStructure.getFieldValue("g")).floatValue(); - float b = ((Number) materialStructure.getFieldValue("b")).floatValue(); - float alpha = ((Number) materialStructure.getFieldValue("alpha")).floatValue(); - if ((commonMapto & 0x01) == 0x01) {// Col - return new ColorRGBA(r, g, b, alpha); - } else { - switch (diffuseShader) { - case FRESNEL: - case ORENNAYAR: - case TOON: - break;// TODO: find what is the proper modification - case MINNAERT: - case LAMBERT:// TODO: check if that is correct - float ref = ((Number) materialStructure.getFieldValue("ref")).floatValue(); - r *= ref; - g *= ref; - b *= ref; - break; - default: - throw new IllegalStateException("Unknown diffuse shader type: " + diffuseShader.toString()); - } - return new ColorRGBA(r, g, b, alpha); - } - } - - /** - * This method returns an enum describing the type of a diffuse shader used by this material. - * - * @param materialStructure - * the material structure filled with data - * @return an enum describing the type of a diffuse shader used by this material - */ - public DiffuseShader getDiffuseShader(Structure materialStructure) { - int diff_shader = ((Number) materialStructure.getFieldValue("diff_shader")).intValue(); - return DiffuseShader.values()[diff_shader]; - } - - /** - * This method returns an ambient color used by the material. - * - * @param materialStructure - * the material structure filled with data - * @return an ambient color used by the material - */ - public ColorRGBA getAmbientColor(Structure materialStructure) { - float r = ((Number) materialStructure.getFieldValue("ambr")).floatValue(); - float g = ((Number) materialStructure.getFieldValue("ambg")).floatValue(); - float b = ((Number) materialStructure.getFieldValue("ambb")).floatValue(); - float alpha = ((Number) materialStructure.getFieldValue("alpha")).floatValue(); - return new ColorRGBA(r, g, b, alpha); - } - - /** - * This method returns an enum describing the type of a specular shader used by this material. - * - * @param materialStructure - * the material structure filled with data - * @return an enum describing the type of a specular shader used by this material - */ - public SpecularShader getSpecularShader(Structure materialStructure) { - int spec_shader = ((Number) materialStructure.getFieldValue("spec_shader")).intValue(); - return SpecularShader.values()[spec_shader]; - } - - /** - * This method returns a specular color used by the material. - * - * @param materialStructure - * the material structure filled with data - * @return a specular color used by the material - */ - public ColorRGBA getSpecularColor(Structure materialStructure, SpecularShader specularShader) { - float r = ((Number) materialStructure.getFieldValue("specr")).floatValue(); - float g = ((Number) materialStructure.getFieldValue("specg")).floatValue(); - float b = ((Number) materialStructure.getFieldValue("specb")).floatValue(); - float alpha = ((Number) materialStructure.getFieldValue("alpha")).floatValue(); - switch (specularShader) { - case BLINN: - case COOKTORRENCE: - case TOON: - case WARDISO:// TODO: find what is the proper modification - break; - case PHONG:// TODO: check if that is correct - float spec = ((Number) materialStructure.getFieldValue("spec")).floatValue(); - r *= spec * 0.5f; - g *= spec * 0.5f; - b *= spec * 0.5f; - break; - default: - throw new IllegalStateException("Unknown specular shader type: " + specularShader.toString()); - } - return new ColorRGBA(r, g, b, alpha); - } - - /** - * This method returns the sihiness of this material or DEFAULT_SHININESS value if not present. - * - * @param materialStructure - * the material structure filled with data - * @return the sihiness of this material or DEFAULT_SHININESS value if not present - */ - public float getShininess(Structure materialStructure) { - float shininess = ((Number) materialStructure.getFieldValue("emit")).floatValue(); - return shininess > 0.0f ? shininess : DEFAULT_SHININESS; - } - - /** - * This method returns the table of materials connected to the specified structure. The given structure can be of any type (ie. mesh or - * curve) but needs to have 'mat' field/ - * - * @param structureWithMaterials - * the structure containing the mesh data - * @param dataRepository - * the data repository - * @return a list of vertices colors, each color belongs to a single vertex - * @throws BlenderFileException - * this exception is thrown when the blend file structure is somehow invalid or corrupted - */ - public Material[] getMaterials(Structure structureWithMaterials, DataRepository dataRepository) throws BlenderFileException { - Pointer ppMaterials = (Pointer) structureWithMaterials.getFieldValue("mat"); - Material[] materials = null; - if (!ppMaterials.isNull()) { - List materialStructures = ppMaterials.fetchData(dataRepository.getInputStream()); - if (materialStructures != null && materialStructures.size() > 0) { - MaterialHelper materialHelper = dataRepository.getHelper(MaterialHelper.class); - materials = new Material[materialStructures.size()]; - int i = 0; - for (Structure s : materialStructures) { - Material material = (Material) dataRepository.getLoadedFeature(s.getOldMemoryAddress(), LoadedFeatureDataType.LOADED_FEATURE); - if (material == null) { - material = materialHelper.toMaterial(s, dataRepository); - } - materials[i++] = material; - } - } - } - return materials; - } - - /** - * This method converts rgb values to hsv values. - * - * @param rgb - * rgb values of the color - * @param hsv - * hsv values of a color (this table contains the result of the transformation) - */ - public void rgbToHsv(float r, float g, float b, float[] hsv) { - float cmax = r; - float cmin = r; - cmax = g > cmax ? g : cmax; - cmin = g < cmin ? g : cmin; - cmax = b > cmax ? b : cmax; - cmin = b < cmin ? b : cmin; - - hsv[2] = cmax; /* value */ - if (cmax != 0.0) { - hsv[1] = (cmax - cmin) / cmax; - } else { - hsv[1] = 0.0f; - hsv[0] = 0.0f; - } - if (hsv[1] == 0.0) { - hsv[0] = -1.0f; - } else { - float cdelta = cmax - cmin; - float rc = (cmax - r) / cdelta; - float gc = (cmax - g) / cdelta; - float bc = (cmax - b) / cdelta; - if (r == cmax) { - hsv[0] = bc - gc; - } else if (g == cmax) { - hsv[0] = 2.0f + rc - bc; - } else { - hsv[0] = 4.0f + gc - rc; - } - hsv[0] *= 60.0f; - if (hsv[0] < 0.0f) { - hsv[0] += 360.0f; - } - } - - hsv[0] /= 360.0f; - if (hsv[0] < 0.0f) { - hsv[0] = 0.0f; - } - } - - /** - * This method converts rgb values to hsv values. - * - * @param h - * hue - * @param s - * saturation - * @param v - * value - * @param rgb - * rgb result vector (should have 3 elements) - */ - public void hsvToRgb(float h, float s, float v, float[] rgb) { - h *= 360.0f; - if (s == 0.0) { - rgb[0] = rgb[1] = rgb[2] = v; - } else { - if (h == 360) { - h = 0; - } else { - h /= 60; - } - int i = (int) Math.floor(h); - float f = h - i; - float p = v * (1.0f - s); - float q = v * (1.0f - s * f); - float t = v * (1.0f - s * (1.0f - f)); - switch (i) { - case 0: - rgb[0] = v; - rgb[1] = t; - rgb[2] = p; - break; - case 1: - rgb[0] = q; - rgb[1] = v; - rgb[2] = p; - break; - case 2: - rgb[0] = p; - rgb[1] = v; - rgb[2] = t; - break; - case 3: - rgb[0] = p; - rgb[1] = q; - rgb[2] = v; - break; - case 4: - rgb[0] = t; - rgb[1] = p; - rgb[2] = v; - break; - case 5: - rgb[0] = v; - rgb[1] = p; - rgb[2] = q; - break; - } - } - } - - /** - * An interface used in calculating alpha mask during particles' texture calculations. - * @author Marcin Roguski (Kaelthas) - */ - protected static interface IAlphaMask { - /** - * This method sets the size of the texture's image. - * @param width - * the width of the image - * @param height - * the height of the image - */ - void setImageSize(int width, int height); - - /** - * This method returns the alpha value for the specified texture position. - * @param x - * the X coordinate of the texture position - * @param y - * the Y coordinate of the texture position - * @return the alpha value for the specified texture position - */ - byte getAlpha(float x, float y); - } + + private static final Logger LOGGER = Logger.getLogger(MaterialHelper.class.getName()); + protected static final float DEFAULT_SHININESS = 20.0f; + public static final String TEXTURE_TYPE_COLOR = "ColorMap"; + public static final String TEXTURE_TYPE_DIFFUSE = "DiffuseMap"; + public static final String TEXTURE_TYPE_NORMAL = "NormalMap"; + public static final String TEXTURE_TYPE_SPECULAR = "SpecularMap"; + public static final String TEXTURE_TYPE_GLOW = "GlowMap"; + public static final String TEXTURE_TYPE_ALPHA = "AlphaMap"; + public static final Integer ALPHA_MASK_NONE = Integer.valueOf(0); + public static final Integer ALPHA_MASK_CIRCLE = Integer.valueOf(1); + public static final Integer ALPHA_MASK_CONE = Integer.valueOf(2); + public static final Integer ALPHA_MASK_HYPERBOLE = Integer.valueOf(3); + protected final Map alphaMasks = new HashMap(); + + /** + * The type of the material's diffuse shader. + */ + public static enum DiffuseShader { + + LAMBERT, ORENNAYAR, TOON, MINNAERT, FRESNEL + } + + /** + * The type of the material's specular shader. + */ + public static enum SpecularShader { + + COOKTORRENCE, PHONG, BLINN, TOON, WARDISO + } + /** Face cull mode. Should be excplicitly set before this helper is used. */ + protected FaceCullMode faceCullMode; + + /** + * This constructor parses the given blender version and stores the result. Some functionalities may differ in different blender + * versions. + * + * @param blenderVersion + * the version read from the blend file + */ + public MaterialHelper(String blenderVersion) { + super(blenderVersion); + // setting alpha masks + alphaMasks.put(ALPHA_MASK_NONE, new AlphaMask() { + + @Override + public void setImageSize(int width, int height) { + } + + @Override + public byte getAlpha(float x, float y) { + return (byte) 255; + } + }); + alphaMasks.put(ALPHA_MASK_CIRCLE, new AlphaMask() { + + private float r; + private float[] center; + + @Override + public void setImageSize(int width, int height) { + r = Math.min(width, height) * 0.5f; + center = new float[]{width * 0.5f, height * 0.5f}; + } + + @Override + public byte getAlpha(float x, float y) { + float d = FastMath.abs(FastMath.sqrt((x - center[0]) * (x - center[0]) + (y - center[1]) * (y - center[1]))); + return (byte) (d >= r ? 0 : 255); + } + }); + alphaMasks.put(ALPHA_MASK_CONE, new AlphaMask() { + + private float r; + private float[] center; + + @Override + public void setImageSize(int width, int height) { + r = Math.min(width, height) * 0.5f; + center = new float[]{width * 0.5f, height * 0.5f}; + } + + @Override + public byte getAlpha(float x, float y) { + float d = FastMath.abs(FastMath.sqrt((x - center[0]) * (x - center[0]) + (y - center[1]) * (y - center[1]))); + return (byte) (d >= r ? 0 : -255.0f * d / r + 255.0f); + } + }); + alphaMasks.put(ALPHA_MASK_HYPERBOLE, new AlphaMask() { + + private float r; + private float[] center; + + @Override + public void setImageSize(int width, int height) { + r = Math.min(width, height) * 0.5f; + center = new float[]{width * 0.5f, height * 0.5f}; + } + + @Override + public byte getAlpha(float x, float y) { + float d = FastMath.abs(FastMath.sqrt((x - center[0]) * (x - center[0]) + (y - center[1]) * (y - center[1]))) / r; + return d >= 1.0f ? 0 : (byte) ((-FastMath.sqrt((2.0f - d) * d) + 1.0f) * 255.0f); + } + }); + } + + /** + * This method sets the face cull mode to be used with every loaded material. + * + * @param faceCullMode + * the face cull mode + */ + public void setFaceCullMode(FaceCullMode faceCullMode) { + this.faceCullMode = faceCullMode; + } + + @SuppressWarnings("unchecked") + public Material toMaterial(Structure structure, DataRepository dataRepository) throws BlenderFileException { + LOGGER.log(Level.INFO, "Loading material."); + if (structure == null) { + return dataRepository.getDefaultMaterial(); + } + Material result = (Material) dataRepository.getLoadedFeature(structure.getOldMemoryAddress(), LoadedFeatureDataType.LOADED_FEATURE); + if (result != null) { + return result; + } + + int mode = ((Number) structure.getFieldValue("mode")).intValue(); + boolean shadeless = (mode & 0x4) != 0; + boolean vertexColor = (mode & 0x16) != 0; + boolean transparent = (mode & 0x64) != 0; + + if (shadeless) { + result = new Material(dataRepository.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md"); + } else { + result = new Material(dataRepository.getAssetManager(), "Common/MatDefs/Light/Lighting.j3md"); + } + + result.getAdditionalRenderState().setFaceCullMode(faceCullMode); + + if (transparent) { + result.getAdditionalRenderState().setBlendMode(BlendMode.Alpha); + } + + String name = structure.getName(); + LOGGER.log(Level.INFO, "Material's name: {0}", name); + if (vertexColor) { + if (shadeless) { + result.setBoolean("VertexColor", true); + } else { + result.setBoolean("UseVertexColor", true); + } + } + + MaterialHelper materialHelper = dataRepository.getHelper(MaterialHelper.class); + ColorRGBA diffuseColor = null; + if (shadeless) { + // color of shadeless? doesn't seem to work in blender .. + } else { + result.setBoolean("UseMaterialColors", true); + + // setting the colors + DiffuseShader diffuseShader = materialHelper.getDiffuseShader(structure); + result.setBoolean("Minnaert", diffuseShader == DiffuseShader.MINNAERT); + diffuseColor = materialHelper.getDiffuseColor(structure, diffuseShader); + result.setColor("Diffuse", diffuseColor); + + SpecularShader specularShader = materialHelper.getSpecularShader(structure); + result.setBoolean("WardIso", specularShader == SpecularShader.WARDISO); + result.setColor("Specular", materialHelper.getSpecularColor(structure, specularShader)); + + result.setColor("Ambient", materialHelper.getAmbientColor(structure)); + result.setFloat("Shininess", materialHelper.getShininess(structure)); + } + + // texture + if ((dataRepository.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.TEXTURES) != 0) { + TextureHelper textureHelper = dataRepository.getHelper(TextureHelper.class); + DynamicArray mtexs = (DynamicArray) structure.getFieldValue("mtex"); + for (int i = 0; i < mtexs.getTotalSize(); ++i) { + Pointer p = mtexs.get(i); + if (!p.isNull()) { + List mtex = p.fetchData(dataRepository.getInputStream()); + if (mtex.size() == 1) { + Structure textureLink = mtex.get(0); + int texflag = ((Number) textureLink.getFieldValue("texflag")).intValue(); + // int texco = ((Number) textureLink.getFieldValue("texco")).intValue(); + boolean negateTexture = (texflag & 0x04) == 0; + + // if(texco == 0x10) {//TEXCO_UV (this is only supported now) + int mapto = ((Number) textureLink.getFieldValue("mapto")).intValue(); + if (mapto != 0) { + Pointer pTex = (Pointer) textureLink.getFieldValue("tex"); + Structure tex = pTex.fetchData(dataRepository.getInputStream()).get(0); + Texture texture = textureHelper.getTexture(tex, dataRepository); + if (texture != null) { + if ((mapto & 0x01) != 0) {// Col + result.setBoolean("UseMaterialColors", Boolean.FALSE); + // blending the texture with material color and texture's defined color + int blendType = ((Number) textureLink.getFieldValue("blendtype")).intValue(); + float[] color = new float[]{((Number) textureLink.getFieldValue("r")).floatValue(), ((Number) textureLink.getFieldValue("g")).floatValue(), ((Number) textureLink.getFieldValue("b")).floatValue()}; + float colfac = ((Number) textureLink.getFieldValue("colfac")).floatValue(); + texture = textureHelper.blendTexture(diffuseColor.getColorArray(), texture, color, colfac, blendType, negateTexture, dataRepository); + texture.setWrap(WrapMode.Repeat); + if (shadeless) { + result.setTexture(TEXTURE_TYPE_COLOR, texture); + } else { + result.setTexture(TEXTURE_TYPE_DIFFUSE, texture); + } + } + if ((mapto & 0x02) != 0) {// Nor + result.setTexture(TEXTURE_TYPE_NORMAL, texture); + } + if ((mapto & 0x20) != 0) {// Spec + result.setTexture(TEXTURE_TYPE_SPECULAR, texture); + } + if ((mapto & 0x40) != 0) {// Emit + result.setTexture(TEXTURE_TYPE_GLOW, texture); + } + if ((mapto & 0x80) != 0) {// Alpha + result.setTexture(TEXTURE_TYPE_ALPHA, texture); + } + } else { + LOGGER.log(Level.WARNING, "Texture not found!"); + } + } + // } else { + // Pointer pTex = (Pointer)textureLink.getFieldValue("tex"); + // List texs = pTex.fetchData(dataRepository.getInputStream()); + // Structure tex = texs.get(0); + // LOGGER.log(Level.WARNING, "Unsupported texture type: " + texco); + // } + } else { + LOGGER.log(Level.WARNING, "Many textures. Not solved yet!");// TODO + } + } + } + } + dataRepository.addLoadedFeatures(structure.getOldMemoryAddress(), structure.getName(), structure, result); + return result; + } + + /** + * This method returns a material similar to the one given but without textures. If the material has no textures it is not cloned but + * returned itself. + * + * @param material + * a material to be cloned without textures + * @param imageType + * type of image defined by blender; the constants are defined in TextureHelper + * @return material without textures of a specified type + */ + public Material getNonTexturedMaterial(Material material, int imageType) { + String[] textureParamNames = new String[]{TEXTURE_TYPE_DIFFUSE, TEXTURE_TYPE_NORMAL, TEXTURE_TYPE_GLOW, TEXTURE_TYPE_SPECULAR, TEXTURE_TYPE_ALPHA}; + Map textures = new HashMap(textureParamNames.length); + for (String textureParamName : textureParamNames) { + MatParamTexture matParamTexture = material.getTextureParam(textureParamName); + if (matParamTexture != null) { + textures.put(textureParamName, matParamTexture.getTextureValue()); + } + } + if (textures.isEmpty()) { + return material; + } else { + // clear all textures first so that wo de not waste resources cloning them + for (Entry textureParamName : textures.entrySet()) { + String name = textureParamName.getValue().getName(); + try { + int type = Integer.parseInt(name); + if (type == imageType) { + material.clearParam(textureParamName.getKey()); + } + } catch (NumberFormatException e) { + LOGGER.log(Level.WARNING, "The name of the texture does not contain the texture type value! {0} will not be removed!", name); + } + } + Material result = material.clone(); + // put the textures back in place + for (Entry textureEntry : textures.entrySet()) { + material.setTexture(textureEntry.getKey(), textureEntry.getValue()); + } + return result; + } + } + + /** + * This method converts the given material into particles-usable material. + * The texture and glow color are being copied. + * The method assumes it receives the Lighting type of material. + * @param material + * the source material + * @param dataRepository + * the data repository + * @return material converted into particles-usable material + */ + public Material getParticlesMaterial(Material material, Integer alphaMaskIndex, DataRepository dataRepository) { + Material result = new Material(dataRepository.getAssetManager(), "Common/MatDefs/Misc/Particle.j3md"); + + // copying texture + MatParam diffuseMap = material.getParam("DiffuseMap"); + if (diffuseMap != null) { + Texture texture = ((Texture) diffuseMap.getValue()).clone(); + + // applying alpha mask to the texture + Image image = texture.getImage(); + ByteBuffer sourceBB = image.getData(0); + sourceBB.rewind(); + int w = image.getWidth(); + int h = image.getHeight(); + ByteBuffer bb = BufferUtils.createByteBuffer(w * h * 4); + AlphaMask iAlphaMask = alphaMasks.get(alphaMaskIndex); + iAlphaMask.setImageSize(w, h); + + for (int x = 0; x < w; ++x) { + for (int y = 0; y < h; ++y) { + bb.put(sourceBB.get()); + bb.put(sourceBB.get()); + bb.put(sourceBB.get()); + bb.put(iAlphaMask.getAlpha(x, y)); + } + } + + image = new Image(Format.RGBA8, w, h, bb); + texture.setImage(image); + + result.setTextureParam("Texture", VarType.Texture2D, texture); + } + + // copying glow color + MatParam glowColor = material.getParam("GlowColor"); + if (glowColor != null) { + ColorRGBA color = (ColorRGBA) glowColor.getValue(); + result.setParam("GlowColor", VarType.Vector3, color); + } + return result; + } + + /** + * This method indicates if the material has a texture of a specified type. + * + * @param material + * the material + * @param textureType + * the type of the texture + * @return true if the texture exists in the material and false otherwise + */ + public boolean hasTexture(Material material, String textureType) { + if (material != null) { + return material.getTextureParam(textureType) != null; + } + return false; + } + + /** + * This method returns the diffuse color + * + * @param materialStructure + * @param diffuseShader + * @return + */ + public ColorRGBA getDiffuseColor(Structure materialStructure, DiffuseShader diffuseShader) { + // bitwise 'or' of all textures mappings + int commonMapto = ((Number) materialStructure.getFieldValue("mapto")).intValue(); + + // diffuse color + float r = ((Number) materialStructure.getFieldValue("r")).floatValue(); + float g = ((Number) materialStructure.getFieldValue("g")).floatValue(); + float b = ((Number) materialStructure.getFieldValue("b")).floatValue(); + float alpha = ((Number) materialStructure.getFieldValue("alpha")).floatValue(); + if ((commonMapto & 0x01) == 0x01) {// Col + return new ColorRGBA(r, g, b, alpha); + } else { + switch (diffuseShader) { + case FRESNEL: + case ORENNAYAR: + case TOON: + break;// TODO: find what is the proper modification + case MINNAERT: + case LAMBERT:// TODO: check if that is correct + float ref = ((Number) materialStructure.getFieldValue("ref")).floatValue(); + r *= ref; + g *= ref; + b *= ref; + break; + default: + throw new IllegalStateException("Unknown diffuse shader type: " + diffuseShader.toString()); + } + return new ColorRGBA(r, g, b, alpha); + } + } + + /** + * This method returns an enum describing the type of a diffuse shader used by this material. + * + * @param materialStructure + * the material structure filled with data + * @return an enum describing the type of a diffuse shader used by this material + */ + public DiffuseShader getDiffuseShader(Structure materialStructure) { + int diff_shader = ((Number) materialStructure.getFieldValue("diff_shader")).intValue(); + return DiffuseShader.values()[diff_shader]; + } + + /** + * This method returns an ambient color used by the material. + * + * @param materialStructure + * the material structure filled with data + * @return an ambient color used by the material + */ + public ColorRGBA getAmbientColor(Structure materialStructure) { + float r = ((Number) materialStructure.getFieldValue("ambr")).floatValue(); + float g = ((Number) materialStructure.getFieldValue("ambg")).floatValue(); + float b = ((Number) materialStructure.getFieldValue("ambb")).floatValue(); + float alpha = ((Number) materialStructure.getFieldValue("alpha")).floatValue(); + return new ColorRGBA(r, g, b, alpha); + } + + /** + * This method returns an enum describing the type of a specular shader used by this material. + * + * @param materialStructure + * the material structure filled with data + * @return an enum describing the type of a specular shader used by this material + */ + public SpecularShader getSpecularShader(Structure materialStructure) { + int spec_shader = ((Number) materialStructure.getFieldValue("spec_shader")).intValue(); + return SpecularShader.values()[spec_shader]; + } + + /** + * This method returns a specular color used by the material. + * + * @param materialStructure + * the material structure filled with data + * @return a specular color used by the material + */ + public ColorRGBA getSpecularColor(Structure materialStructure, SpecularShader specularShader) { + float r = ((Number) materialStructure.getFieldValue("specr")).floatValue(); + float g = ((Number) materialStructure.getFieldValue("specg")).floatValue(); + float b = ((Number) materialStructure.getFieldValue("specb")).floatValue(); + float alpha = ((Number) materialStructure.getFieldValue("alpha")).floatValue(); + switch (specularShader) { + case BLINN: + case COOKTORRENCE: + case TOON: + case WARDISO:// TODO: find what is the proper modification + break; + case PHONG:// TODO: check if that is correct + float spec = ((Number) materialStructure.getFieldValue("spec")).floatValue(); + r *= spec * 0.5f; + g *= spec * 0.5f; + b *= spec * 0.5f; + break; + default: + throw new IllegalStateException("Unknown specular shader type: " + specularShader.toString()); + } + return new ColorRGBA(r, g, b, alpha); + } + + /** + * This method returns the sihiness of this material or DEFAULT_SHININESS value if not present. + * + * @param materialStructure + * the material structure filled with data + * @return the sihiness of this material or DEFAULT_SHININESS value if not present + */ + public float getShininess(Structure materialStructure) { + float shininess = ((Number) materialStructure.getFieldValue("emit")).floatValue(); + return shininess > 0.0f ? shininess : DEFAULT_SHININESS; + } + + /** + * This method returns the table of materials connected to the specified structure. The given structure can be of any type (ie. mesh or + * curve) but needs to have 'mat' field/ + * + * @param structureWithMaterials + * the structure containing the mesh data + * @param dataRepository + * the data repository + * @return a list of vertices colors, each color belongs to a single vertex + * @throws BlenderFileException + * this exception is thrown when the blend file structure is somehow invalid or corrupted + */ + public Material[] getMaterials(Structure structureWithMaterials, DataRepository dataRepository) throws BlenderFileException { + Pointer ppMaterials = (Pointer) structureWithMaterials.getFieldValue("mat"); + Material[] materials = null; + if (!ppMaterials.isNull()) { + List materialStructures = ppMaterials.fetchData(dataRepository.getInputStream()); + if (materialStructures != null && materialStructures.size() > 0) { + MaterialHelper materialHelper = dataRepository.getHelper(MaterialHelper.class); + materials = new Material[materialStructures.size()]; + int i = 0; + for (Structure s : materialStructures) { + Material material = (Material) dataRepository.getLoadedFeature(s.getOldMemoryAddress(), LoadedFeatureDataType.LOADED_FEATURE); + if (material == null) { + material = materialHelper.toMaterial(s, dataRepository); + } + materials[i++] = material; + } + } + } + return materials; + } + + /** + * This method converts rgb values to hsv values. + * + * @param rgb + * rgb values of the color + * @param hsv + * hsv values of a color (this table contains the result of the transformation) + */ + public void rgbToHsv(float r, float g, float b, float[] hsv) { + float cmax = r; + float cmin = r; + cmax = g > cmax ? g : cmax; + cmin = g < cmin ? g : cmin; + cmax = b > cmax ? b : cmax; + cmin = b < cmin ? b : cmin; + + hsv[2] = cmax; /* value */ + if (cmax != 0.0) { + hsv[1] = (cmax - cmin) / cmax; + } else { + hsv[1] = 0.0f; + hsv[0] = 0.0f; + } + if (hsv[1] == 0.0) { + hsv[0] = -1.0f; + } else { + float cdelta = cmax - cmin; + float rc = (cmax - r) / cdelta; + float gc = (cmax - g) / cdelta; + float bc = (cmax - b) / cdelta; + if (r == cmax) { + hsv[0] = bc - gc; + } else if (g == cmax) { + hsv[0] = 2.0f + rc - bc; + } else { + hsv[0] = 4.0f + gc - rc; + } + hsv[0] *= 60.0f; + if (hsv[0] < 0.0f) { + hsv[0] += 360.0f; + } + } + + hsv[0] /= 360.0f; + if (hsv[0] < 0.0f) { + hsv[0] = 0.0f; + } + } + + /** + * This method converts rgb values to hsv values. + * + * @param h + * hue + * @param s + * saturation + * @param v + * value + * @param rgb + * rgb result vector (should have 3 elements) + */ + public void hsvToRgb(float h, float s, float v, float[] rgb) { + h *= 360.0f; + if (s == 0.0) { + rgb[0] = rgb[1] = rgb[2] = v; + } else { + if (h == 360) { + h = 0; + } else { + h /= 60; + } + int i = (int) Math.floor(h); + float f = h - i; + float p = v * (1.0f - s); + float q = v * (1.0f - s * f); + float t = v * (1.0f - s * (1.0f - f)); + switch (i) { + case 0: + rgb[0] = v; + rgb[1] = t; + rgb[2] = p; + break; + case 1: + rgb[0] = q; + rgb[1] = v; + rgb[2] = p; + break; + case 2: + rgb[0] = p; + rgb[1] = v; + rgb[2] = t; + break; + case 3: + rgb[0] = p; + rgb[1] = q; + rgb[2] = v; + break; + case 4: + rgb[0] = t; + rgb[1] = p; + rgb[2] = v; + break; + case 5: + rgb[0] = v; + rgb[1] = p; + rgb[2] = q; + break; + } + } + } + + /** + * An interface used in calculating alpha mask during particles' texture calculations. + * @author Marcin Roguski (Kaelthas) + */ + protected static interface AlphaMask { + + /** + * This method sets the size of the texture's image. + * @param width + * the width of the image + * @param height + * the height of the image + */ + void setImageSize(int width, int height); + + /** + * This method returns the alpha value for the specified texture position. + * @param x + * the X coordinate of the texture position + * @param y + * the Y coordinate of the texture position + * @return the alpha value for the specified texture position + */ + byte getAlpha(float x, float y); + } } diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/helpers/v249/MeshHelper.java b/engine/src/blender/com/jme3/scene/plugins/blender/helpers/v249/MeshHelper.java index 9dd35f51d..0644a933a 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/helpers/v249/MeshHelper.java +++ b/engine/src/blender/com/jme3/scene/plugins/blender/helpers/v249/MeshHelper.java @@ -67,533 +67,534 @@ import com.jme3.util.BufferUtils; * @author Marcin Roguski (Kaelthas) */ public class MeshHelper extends AbstractBlenderHelper { - protected static final int MAXIMUM_WEIGHTS_PER_VERTEX = 4; // have no idea why 4, could someone please explain ? - - /** - * This constructor parses the given blender version and stores the result. Some functionalities may differ in different blender - * versions. - * - * @param blenderVersion - * the version read from the blend file - */ - public MeshHelper(String blenderVersion) { - super(blenderVersion); - } - - /** - * This method reads converts the given structure into mesh. The given structure needs to be filled with the appropriate data. - * - * @param structure - * the structure we read the mesh from - * @return the mesh feature - * @throws BlenderFileException - */ - @SuppressWarnings("unchecked") - public List toMesh(Structure structure, DataRepository dataRepository) throws BlenderFileException { - List geometries = (List) dataRepository.getLoadedFeature(structure.getOldMemoryAddress(), - LoadedFeatureDataType.LOADED_FEATURE); - if (geometries != null) { - List copiedGeometries = new ArrayList(geometries.size()); - for (Geometry geometry : geometries) { - copiedGeometries.add(geometry.clone()); - } - return copiedGeometries; - } - - // helpers - TextureHelper textureHelper = dataRepository.getHelper(TextureHelper.class); - - // reading mesh data - String name = structure.getName(); - - // reading vertices - Vector3f[] vertices = this.getVertices(structure, dataRepository); - int verticesAmount = vertices.length; - - // vertices Colors - List verticesColors = this.getVerticesColors(structure, dataRepository); - - // reading faces - // the following map sorts faces by material number (because in jme Mesh can have only one material) - Map> meshesMap = new HashMap>(); - Pointer pMFace = (Pointer) structure.getFieldValue("mface"); - List mFaces = pMFace.fetchData(dataRepository.getInputStream()); - - Pointer pMTFace = (Pointer) structure.getFieldValue("mtface"); - List uvCoordinates = null; - List mtFaces = null; - - if (!pMTFace.isNull()) { - mtFaces = pMTFace.fetchData(dataRepository.getInputStream()); - int facesAmount = ((Number) structure.getFieldValue("totface")).intValue(); - if (mtFaces.size() != facesAmount) { - throw new BlenderFileException("The amount of faces uv coordinates is not equal to faces amount!"); - } - uvCoordinates = new ArrayList();// TODO: calculate the amount of coordinates if possible - } - - // normalMap merges normals of faces that will be rendered smooth - Map normalMap = new HashMap(verticesAmount); - - List normalList = new ArrayList(); - List vertexList = new ArrayList(); - Map materialNumberToTexture = new HashMap();// indicates if the material with the specified - // number should have a texture attached - // this map's key is the vertex index from 'vertices 'table and the value are indices from 'vertexList' - // positions (it simply tells which vertex is referenced where in the result list) - Map> vertexReferenceMap = new HashMap>(verticesAmount); - int vertexColorIndex = 0; - for (int i = 0; i < mFaces.size(); ++i) { - Structure mFace = mFaces.get(i); - boolean smooth = (((Number) mFace.getFieldValue("flag")).byteValue() & 0x01) != 0x00; - DynamicArray uvs = null; - boolean materialWithoutTextures = false; - Pointer pImage = null; - if (mtFaces != null) { - Structure mtFace = mtFaces.get(i); - pImage = (Pointer) mtFace.getFieldValue("tpage"); - materialWithoutTextures = pImage.isNull(); - // uvs always must be added wheater we have texture or not - uvs = (DynamicArray) mtFace.getFieldValue("uv"); - uvCoordinates.add(new Vector2f(uvs.get(0, 0).floatValue(), uvs.get(0, 1).floatValue())); - uvCoordinates.add(new Vector2f(uvs.get(1, 0).floatValue(), uvs.get(1, 1).floatValue())); - uvCoordinates.add(new Vector2f(uvs.get(2, 0).floatValue(), uvs.get(2, 1).floatValue())); - } - int matNr = ((Number) mFace.getFieldValue("mat_nr")).intValue(); - Integer materialNumber = Integer.valueOf(materialWithoutTextures ? -1 * matNr - 1 : matNr); - List indexList = meshesMap.get(materialNumber); - if (indexList == null) { - indexList = new ArrayList(); - meshesMap.put(materialNumber, indexList); - } - if (pImage != null && !pImage.isNull() && !materialNumberToTexture.containsKey(materialNumber)) {// attaching image to texture - // (face can have UV's and - // image whlie its material - // may have no texture - // attached) - Texture texture = textureHelper.getTextureFromImage(pImage.fetchData(dataRepository.getInputStream()).get(0), - dataRepository); - if (texture != null) { - materialNumberToTexture.put(materialNumber, texture); - } - } - - int v1 = ((Number) mFace.getFieldValue("v1")).intValue(); - int v2 = ((Number) mFace.getFieldValue("v2")).intValue(); - int v3 = ((Number) mFace.getFieldValue("v3")).intValue(); - int v4 = ((Number) mFace.getFieldValue("v4")).intValue(); - - Vector3f n = FastMath.computeNormal(vertices[v1], vertices[v2], vertices[v3]); - this.addNormal(n, normalMap, smooth, vertices[v1], vertices[v2], vertices[v3]); - normalList.add(normalMap.get(vertices[v1])); - normalList.add(normalMap.get(vertices[v2])); - normalList.add(normalMap.get(vertices[v3])); - - this.appendVertexReference(v1, vertexList.size(), vertexReferenceMap); - indexList.add(vertexList.size()); - vertexList.add(vertices[v1]); - - this.appendVertexReference(v2, vertexList.size(), vertexReferenceMap); - indexList.add(vertexList.size()); - vertexList.add(vertices[v2]); - - this.appendVertexReference(v3, vertexList.size(), vertexReferenceMap); - indexList.add(vertexList.size()); - vertexList.add(vertices[v3]); - - if (v4 > 0) { - if (uvs != null) { - uvCoordinates.add(new Vector2f(uvs.get(0, 0).floatValue(), uvs.get(0, 1).floatValue())); - uvCoordinates.add(new Vector2f(uvs.get(2, 0).floatValue(), uvs.get(2, 1).floatValue())); - uvCoordinates.add(new Vector2f(uvs.get(3, 0).floatValue(), uvs.get(3, 1).floatValue())); - } - this.appendVertexReference(v1, vertexList.size(), vertexReferenceMap); - indexList.add(vertexList.size()); - vertexList.add(vertices[v1]); - - this.appendVertexReference(v3, vertexList.size(), vertexReferenceMap); - indexList.add(vertexList.size()); - vertexList.add(vertices[v3]); - - this.appendVertexReference(v4, vertexList.size(), vertexReferenceMap); - indexList.add(vertexList.size()); - vertexList.add(vertices[v4]); - - this.addNormal(n, normalMap, smooth, vertices[v4]); - normalList.add(normalMap.get(vertices[v1])); - normalList.add(normalMap.get(vertices[v3])); - normalList.add(normalMap.get(vertices[v4])); - - if (verticesColors != null) { - verticesColors.add(vertexColorIndex + 3, verticesColors.get(vertexColorIndex)); - verticesColors.add(vertexColorIndex + 4, verticesColors.get(vertexColorIndex + 2)); - } - vertexColorIndex += 6; - } else { - if (verticesColors != null) { - verticesColors.remove(vertexColorIndex + 3); - vertexColorIndex += 3; - } - } - } - Vector3f[] normals = normalList.toArray(new Vector3f[normalList.size()]); - - // reading vertices groups (from the parent) - Structure parent = dataRepository.peekParent(); - Structure defbase = (Structure) parent.getFieldValue("defbase"); - List defs = defbase.evaluateListBase(dataRepository); - String[] verticesGroups = new String[defs.size()]; - int defIndex = 0; - for (Structure def : defs) { - verticesGroups[defIndex++] = def.getFieldValue("name").toString(); - } - - // vertices bone weights and indices - ArmatureHelper armatureHelper = dataRepository.getHelper(ArmatureHelper.class); - Structure defBase = (Structure) parent.getFieldValue("defbase"); - Map groupToBoneIndexMap = armatureHelper.getGroupToBoneIndexMap(defBase, dataRepository); - - VertexBuffer verticesWeights = null, verticesWeightsIndices = null; - int[] bonesGroups = new int[] { 0 }; - VertexBuffer[] boneWeightsAndIndex = this.getBoneWeightAndIndexBuffer(structure, vertexList.size(), bonesGroups, - vertexReferenceMap, groupToBoneIndexMap, dataRepository); - verticesWeights = boneWeightsAndIndex[0]; - verticesWeightsIndices = boneWeightsAndIndex[1]; - - // reading materials - MaterialHelper materialHelper = dataRepository.getHelper(MaterialHelper.class); - Material[] materials = null; - Material[] nonTexturedMaterials = null; - if ((dataRepository.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.MATERIALS) != 0) { - materials = materialHelper.getMaterials(structure, dataRepository); - nonTexturedMaterials = materials == null ? null : new Material[materials.length];// fill it when needed - } - - // creating the result meshes - geometries = new ArrayList(meshesMap.size()); - - VertexBuffer verticesBuffer = new VertexBuffer(Type.Position); - verticesBuffer.setupData(Usage.Stream, 3, Format.Float, - BufferUtils.createFloatBuffer(vertexList.toArray(new Vector3f[vertexList.size()]))); - - // initial vertex position (used with animation) - VertexBuffer verticesBind = new VertexBuffer(Type.BindPosePosition); - verticesBind.setupData(Usage.CpuOnly, 3, Format.Float, BufferUtils.clone(verticesBuffer.getData())); - - VertexBuffer normalsBuffer = new VertexBuffer(Type.Normal); - normalsBuffer.setupData(Usage.Stream, 3, Format.Float, BufferUtils.createFloatBuffer(normals)); - - // initial normals position (used with animation) - VertexBuffer normalsBind = new VertexBuffer(Type.BindPoseNormal); - normalsBind.setupData(Usage.CpuOnly, 3, Format.Float, BufferUtils.clone(normalsBuffer.getData())); - - VertexBuffer uvCoordsBuffer = null; - if (uvCoordinates != null) { - uvCoordsBuffer = new VertexBuffer(Type.TexCoord); - uvCoordsBuffer.setupData(Usage.Static, 2, Format.Float, - BufferUtils.createFloatBuffer(uvCoordinates.toArray(new Vector2f[uvCoordinates.size()]))); - } - - // generating meshes - FloatBuffer verticesColorsBuffer = this.createFloatBuffer(verticesColors); - for (Entry> meshEntry : meshesMap.entrySet()) { - Mesh mesh = new Mesh(); - - // creating vertices indices for this mesh - List indexList = meshEntry.getValue(); - short[] indices = new short[indexList.size()];//TODO: check if the model doesn't have more than 32767 vertices - for (int i = 0; i < indexList.size(); ++i) {//if yes then mesh.getVertices method must be changed to accept other than ShortBuffer - indices[i] = indexList.get(i).shortValue(); - } - - // setting vertices - mesh.setBuffer(Type.Index, 1, BufferUtils.createShortBuffer(indices)); - mesh.setBuffer(verticesBuffer); - mesh.setBuffer(verticesBind); - - // setting vertices colors - if (verticesColorsBuffer != null) { - mesh.setBuffer(Type.Color, 4, verticesColorsBuffer); - } - - // setting weights for bones - if (verticesWeights != null) { - mesh.setMaxNumWeights(bonesGroups[0]); - mesh.setBuffer(verticesWeights); - mesh.setBuffer(verticesWeightsIndices); - } - - // setting faces' normals - mesh.setBuffer(normalsBuffer); - mesh.setBuffer(normalsBind); - - // setting uvCoords - if (uvCoordsBuffer != null) { - mesh.setBuffer(uvCoordsBuffer); - } - - // creating the result - Geometry geometry = new Geometry(name + (geometries.size() + 1), mesh); - if (materials != null) { - int materialNumber = meshEntry.getKey().intValue(); - Material material; - if (materialNumber >= 0) { - material = materials[materialNumber]; - if (materialNumberToTexture.containsKey(Integer.valueOf(materialNumber))) { - if (material.getMaterialDef().getAssetName().contains("Lighting")) { - if (!materialHelper.hasTexture(material, MaterialHelper.TEXTURE_TYPE_DIFFUSE)) { - material = material.clone(); - material.setTexture(MaterialHelper.TEXTURE_TYPE_DIFFUSE, - materialNumberToTexture.get(Integer.valueOf(materialNumber))); - } - } else { - if (!materialHelper.hasTexture(material, MaterialHelper.TEXTURE_TYPE_COLOR)) { - material = material.clone(); - material.setTexture(MaterialHelper.TEXTURE_TYPE_COLOR, - materialNumberToTexture.get(Integer.valueOf(materialNumber))); - } - } - } - } else { - materialNumber = -1 * (materialNumber + 1); - if (nonTexturedMaterials[materialNumber] == null) { - nonTexturedMaterials[materialNumber] = materialHelper.getNonTexturedMaterial(materials[materialNumber], - TextureHelper.TEX_IMAGE); - } - material = nonTexturedMaterials[materialNumber]; - } - geometry.setMaterial(material); - } else { - geometry.setMaterial(dataRepository.getDefaultMaterial()); - } - geometries.add(geometry); - } - dataRepository.addLoadedFeatures(structure.getOldMemoryAddress(), structure.getName(), structure, geometries); - return geometries; - } - - /** - * This method adds a normal to a normals' map. This map is used to merge normals of a vertor that should be rendered smooth. - * - * @param normalToAdd - * a normal to be added - * @param normalMap - * merges normals of faces that will be rendered smooth; the key is the vertex and the value - its normal vector - * @param smooth - * the variable that indicates wheather to merge normals (creating the smooth mesh) or not - * @param vertices - * a list of vertices read from the blender file - */ - protected void addNormal(Vector3f normalToAdd, Map normalMap, boolean smooth, Vector3f... vertices) { - for (Vector3f v : vertices) { - Vector3f n = normalMap.get(v); - if (!smooth || n == null) { - normalMap.put(v, normalToAdd.clone()); - } else { - n.addLocal(normalToAdd).normalizeLocal(); - } - } - } - - /** - * This method fills the vertex reference map. The vertices are loaded once and referenced many times in the model. This map is created - * 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 - * - the reference indices list. - * - * @param basicVertexIndex - * the index of the vertex from its basic table - * @param resultIndex - * the index of the vertex in its result vertex list - * @param vertexReferenceMap - * the reference map - */ - protected void appendVertexReference(int basicVertexIndex, int resultIndex, Map> vertexReferenceMap) { - List referenceList = vertexReferenceMap.get(Integer.valueOf(basicVertexIndex)); - if (referenceList == null) { - referenceList = new ArrayList(); - vertexReferenceMap.put(Integer.valueOf(basicVertexIndex), referenceList); - } - referenceList.add(Integer.valueOf(resultIndex)); - } - - /** - * This method returns the vertices colors. Each vertex is stored in float[4] array. - * - * @param meshStructure - * the structure containing the mesh data - * @param dataRepository - * the data repository - * @return a list of vertices colors, each color belongs to a single vertex - * @throws BlenderFileException - * this exception is thrown when the blend file structure is somehow invalid or corrupted - */ - public List getVerticesColors(Structure meshStructure, DataRepository dataRepository) throws BlenderFileException { - Pointer pMCol = (Pointer) meshStructure.getFieldValue("mcol"); - List verticesColors = null; - List mCol = null; - if (!pMCol.isNull()) { - verticesColors = new LinkedList(); - mCol = pMCol.fetchData(dataRepository.getInputStream()); - for (Structure color : mCol) { - float r = ((Number) color.getFieldValue("r")).byteValue() / 256.0f; - float g = ((Number) color.getFieldValue("g")).byteValue() / 256.0f; - float b = ((Number) color.getFieldValue("b")).byteValue() / 256.0f; - float a = ((Number) color.getFieldValue("a")).byteValue() / 256.0f; - verticesColors.add(new float[] { b, g, r, a }); - } - } - return verticesColors; - } - - /** - * This method returns the vertices. - * - * @param meshStructure - * the structure containing the mesh data - * @param dataRepository - * the data repository - * @return a list of vertices colors, each color belongs to a single vertex - * @throws BlenderFileException - * this exception is thrown when the blend file structure is somehow invalid or corrupted - */ - @SuppressWarnings("unchecked") - public Vector3f[] getVertices(Structure meshStructure, DataRepository dataRepository) throws BlenderFileException { - int verticesAmount = ((Number) meshStructure.getFieldValue("totvert")).intValue(); - Vector3f[] vertices = new Vector3f[verticesAmount]; - Pointer pMVert = (Pointer) meshStructure.getFieldValue("mvert"); - List mVerts = pMVert.fetchData(dataRepository.getInputStream()); - for (int i = 0; i < verticesAmount; ++i) { - DynamicArray coordinates = (DynamicArray) mVerts.get(i).getFieldValue("co"); - vertices[i] = new Vector3f(coordinates.get(0).floatValue(), coordinates.get(1).floatValue(), coordinates.get(2).floatValue()); - } - return vertices; - } - - /** - * 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 - * second element is a vertex buffer holding bone indices for vertices (the indices of bones the vertices are assigned to). - * - * @param meshStructure - * the mesh structure object - * @param vertexListSize - * a number of vertices in the model - * @param bonesGroups - * this is an output parameter, it should be a one-sized array; the maximum amount of weights per vertex (up to - * MAXIMUM_WEIGHTS_PER_VERTEX) is stored there - * @param vertexReferenceMap - * this reference map allows to map the original vertices read from blender to vertices that are really in the model; one - * vertex may appear several times in the result model - * @param groupToBoneIndexMap - * this object maps the group index (to which a vertices in blender belong) to bone index of the model - * @param dataRepository - * the data repository - * @return arrays of vertices weights and their bone indices and (as an outpot parameter) the maximum amount of weights for a vertex - * @throws BlenderFileException - * this exception is thrown when the blend file structure is somehow invalid or corrupted - */ - public VertexBuffer[] getBoneWeightAndIndexBuffer(Structure meshStructure, int vertexListSize, int[] bonesGroups, - Map> vertexReferenceMap, Map groupToBoneIndexMap, DataRepository dataRepository) - throws BlenderFileException { - Pointer pDvert = (Pointer) meshStructure.getFieldValue("dvert");// dvert = DeformVERTices - FloatBuffer weightsFloatData = BufferUtils.createFloatBuffer(vertexListSize * MAXIMUM_WEIGHTS_PER_VERTEX); - ByteBuffer indicesData = BufferUtils.createByteBuffer(vertexListSize * MAXIMUM_WEIGHTS_PER_VERTEX); - if (!pDvert.isNull()) {// assigning weights and bone indices - List dverts = pDvert.fetchData(dataRepository.getInputStream());// dverts.size() == verticesAmount (one dvert per - // vertex in blender) - int vertexIndex = 0; - for (Structure dvert : dverts) { - int totweight = ((Number) dvert.getFieldValue("totweight")).intValue();// total amount of weights assignet to the vertex - // (max. 4 in JME) - Pointer pDW = (Pointer) dvert.getFieldValue("dw"); - List vertexIndices = vertexReferenceMap.get(Integer.valueOf(vertexIndex));// we fetch the referenced vertices here - if (totweight > 0 && !pDW.isNull()) {// pDW should never be null here, but I check it just in case :) - int weightIndex = 0; - List dw = pDW.fetchData(dataRepository.getInputStream()); - for (Structure deformWeight : dw) { - Integer boneIndex = groupToBoneIndexMap.get(((Number) deformWeight.getFieldValue("def_nr")).intValue()); - if (boneIndex != null) {// null here means that we came accross group that has no bone attached to - float weight = ((Number) deformWeight.getFieldValue("weight")).floatValue(); - if (weight == 0.0f) { - weight = 1; - boneIndex = Integer.valueOf(0); - } - // we apply the weight to all referenced vertices - for (Integer index : vertexIndices) { - // all indices are always assigned to 0-indexed bone - // weightsFloatData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX + weightIndex, 1.0f); - // indicesData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX + weightIndex, (byte)0); - // if(weight != 0.0f) { - weightsFloatData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX + weightIndex, weight); - indicesData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX + weightIndex, boneIndex.byteValue()); - // } - } - } - ++weightIndex; - } - } else { - for (Integer index : vertexIndices) { - weightsFloatData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX, 1.0f); - indicesData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX, (byte) 0); - } - } - ++vertexIndex; - } - } else { - // always bind all vertices to 0-indexed bone - // this bone makes the model look normally if vertices have no bone assigned - // and it is used in object animation, so if we come accross object animation - // we can use the 0-indexed bone for this - for (List vertexIndexList : vertexReferenceMap.values()) { - // we apply the weight to all referenced vertices - for (Integer index : vertexIndexList) { - weightsFloatData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX, 1.0f); - indicesData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX, (byte) 0); - } - } - } - - bonesGroups[0] = this.endBoneAssigns(vertexListSize, weightsFloatData); - VertexBuffer verticesWeights = new VertexBuffer(Type.BoneWeight); - verticesWeights.setupData(Usage.CpuOnly, bonesGroups[0], Format.Float, weightsFloatData); - - VertexBuffer verticesWeightsIndices = new VertexBuffer(Type.BoneIndex); - verticesWeightsIndices.setupData(Usage.CpuOnly, bonesGroups[0], Format.UnsignedByte, indicesData); - return new VertexBuffer[] { verticesWeights, verticesWeightsIndices }; - } - - /** - * Normalizes weights if needed and finds largest amount of weights used for all vertices in the buffer. - */ - protected int endBoneAssigns(int vertCount, FloatBuffer weightsFloatData) { - int maxWeightsPerVert = 0; - weightsFloatData.rewind(); - for (int v = 0; v < vertCount; ++v) { - float w0 = weightsFloatData.get(), w1 = weightsFloatData.get(), w2 = weightsFloatData.get(), w3 = weightsFloatData.get(); - - if (w3 != 0) { - maxWeightsPerVert = Math.max(maxWeightsPerVert, 4); - } else if (w2 != 0) { - maxWeightsPerVert = Math.max(maxWeightsPerVert, 3); - } else if (w1 != 0) { - maxWeightsPerVert = Math.max(maxWeightsPerVert, 2); - } else if (w0 != 0) { - maxWeightsPerVert = Math.max(maxWeightsPerVert, 1); - } - - float sum = w0 + w1 + w2 + w3; - if (sum != 1f && sum != 0.0f) { - weightsFloatData.position(weightsFloatData.position() - 4); - // compute new vals based on sum - float sumToB = 1f / sum; - weightsFloatData.put(w0 * sumToB); - weightsFloatData.put(w1 * sumToB); - weightsFloatData.put(w2 * sumToB); - weightsFloatData.put(w3 * sumToB); - } - } - weightsFloatData.rewind(); - - // mesh.setMaxNumWeights(maxWeightsPerVert); - return maxWeightsPerVert; - } + + protected static final int MAXIMUM_WEIGHTS_PER_VERTEX = 4; // have no idea why 4, could someone please explain ? + + /** + * This constructor parses the given blender version and stores the result. Some functionalities may differ in different blender + * versions. + * + * @param blenderVersion + * the version read from the blend file + */ + public MeshHelper(String blenderVersion) { + super(blenderVersion); + } + + /** + * This method reads converts the given structure into mesh. The given structure needs to be filled with the appropriate data. + * + * @param structure + * the structure we read the mesh from + * @return the mesh feature + * @throws BlenderFileException + */ + @SuppressWarnings("unchecked") + public List toMesh(Structure structure, DataRepository dataRepository) throws BlenderFileException { + List geometries = (List) dataRepository.getLoadedFeature(structure.getOldMemoryAddress(), + LoadedFeatureDataType.LOADED_FEATURE); + if (geometries != null) { + List copiedGeometries = new ArrayList(geometries.size()); + for (Geometry geometry : geometries) { + copiedGeometries.add(geometry.clone()); + } + return copiedGeometries; + } + + // helpers + TextureHelper textureHelper = dataRepository.getHelper(TextureHelper.class); + + // reading mesh data + String name = structure.getName(); + + // reading vertices + Vector3f[] vertices = this.getVertices(structure, dataRepository); + int verticesAmount = vertices.length; + + // vertices Colors + List verticesColors = this.getVerticesColors(structure, dataRepository); + + // reading faces + // the following map sorts faces by material number (because in jme Mesh can have only one material) + Map> meshesMap = new HashMap>(); + Pointer pMFace = (Pointer) structure.getFieldValue("mface"); + List mFaces = pMFace.fetchData(dataRepository.getInputStream()); + + Pointer pMTFace = (Pointer) structure.getFieldValue("mtface"); + List uvCoordinates = null; + List mtFaces = null; + + if (!pMTFace.isNull()) { + mtFaces = pMTFace.fetchData(dataRepository.getInputStream()); + int facesAmount = ((Number) structure.getFieldValue("totface")).intValue(); + if (mtFaces.size() != facesAmount) { + throw new BlenderFileException("The amount of faces uv coordinates is not equal to faces amount!"); + } + uvCoordinates = new ArrayList();// TODO: calculate the amount of coordinates if possible + } + + // normalMap merges normals of faces that will be rendered smooth + Map normalMap = new HashMap(verticesAmount); + + List normalList = new ArrayList(); + List vertexList = new ArrayList(); + Map materialNumberToTexture = new HashMap();// indicates if the material with the specified + // number should have a texture attached + // this map's key is the vertex index from 'vertices 'table and the value are indices from 'vertexList' + // positions (it simply tells which vertex is referenced where in the result list) + Map> vertexReferenceMap = new HashMap>(verticesAmount); + int vertexColorIndex = 0; + for (int i = 0; i < mFaces.size(); ++i) { + Structure mFace = mFaces.get(i); + boolean smooth = (((Number) mFace.getFieldValue("flag")).byteValue() & 0x01) != 0x00; + DynamicArray uvs = null; + boolean materialWithoutTextures = false; + Pointer pImage = null; + if (mtFaces != null) { + Structure mtFace = mtFaces.get(i); + pImage = (Pointer) mtFace.getFieldValue("tpage"); + materialWithoutTextures = pImage.isNull(); + // uvs always must be added wheater we have texture or not + uvs = (DynamicArray) mtFace.getFieldValue("uv"); + uvCoordinates.add(new Vector2f(uvs.get(0, 0).floatValue(), uvs.get(0, 1).floatValue())); + uvCoordinates.add(new Vector2f(uvs.get(1, 0).floatValue(), uvs.get(1, 1).floatValue())); + uvCoordinates.add(new Vector2f(uvs.get(2, 0).floatValue(), uvs.get(2, 1).floatValue())); + } + int matNr = ((Number) mFace.getFieldValue("mat_nr")).intValue(); + Integer materialNumber = Integer.valueOf(materialWithoutTextures ? -1 * matNr - 1 : matNr); + List indexList = meshesMap.get(materialNumber); + if (indexList == null) { + indexList = new ArrayList(); + meshesMap.put(materialNumber, indexList); + } + if (pImage != null && !pImage.isNull() && !materialNumberToTexture.containsKey(materialNumber)) {// attaching image to texture + // (face can have UV's and + // image whlie its material + // may have no texture + // attached) + Texture texture = textureHelper.getTextureFromImage(pImage.fetchData(dataRepository.getInputStream()).get(0), + dataRepository); + if (texture != null) { + materialNumberToTexture.put(materialNumber, texture); + } + } + + int v1 = ((Number) mFace.getFieldValue("v1")).intValue(); + int v2 = ((Number) mFace.getFieldValue("v2")).intValue(); + int v3 = ((Number) mFace.getFieldValue("v3")).intValue(); + int v4 = ((Number) mFace.getFieldValue("v4")).intValue(); + + Vector3f n = FastMath.computeNormal(vertices[v1], vertices[v2], vertices[v3]); + this.addNormal(n, normalMap, smooth, vertices[v1], vertices[v2], vertices[v3]); + normalList.add(normalMap.get(vertices[v1])); + normalList.add(normalMap.get(vertices[v2])); + normalList.add(normalMap.get(vertices[v3])); + + this.appendVertexReference(v1, vertexList.size(), vertexReferenceMap); + indexList.add(vertexList.size()); + vertexList.add(vertices[v1]); + + this.appendVertexReference(v2, vertexList.size(), vertexReferenceMap); + indexList.add(vertexList.size()); + vertexList.add(vertices[v2]); + + this.appendVertexReference(v3, vertexList.size(), vertexReferenceMap); + indexList.add(vertexList.size()); + vertexList.add(vertices[v3]); + + if (v4 > 0) { + if (uvs != null) { + uvCoordinates.add(new Vector2f(uvs.get(0, 0).floatValue(), uvs.get(0, 1).floatValue())); + uvCoordinates.add(new Vector2f(uvs.get(2, 0).floatValue(), uvs.get(2, 1).floatValue())); + uvCoordinates.add(new Vector2f(uvs.get(3, 0).floatValue(), uvs.get(3, 1).floatValue())); + } + this.appendVertexReference(v1, vertexList.size(), vertexReferenceMap); + indexList.add(vertexList.size()); + vertexList.add(vertices[v1]); + + this.appendVertexReference(v3, vertexList.size(), vertexReferenceMap); + indexList.add(vertexList.size()); + vertexList.add(vertices[v3]); + + this.appendVertexReference(v4, vertexList.size(), vertexReferenceMap); + indexList.add(vertexList.size()); + vertexList.add(vertices[v4]); + + this.addNormal(n, normalMap, smooth, vertices[v4]); + normalList.add(normalMap.get(vertices[v1])); + normalList.add(normalMap.get(vertices[v3])); + normalList.add(normalMap.get(vertices[v4])); + + if (verticesColors != null) { + verticesColors.add(vertexColorIndex + 3, verticesColors.get(vertexColorIndex)); + verticesColors.add(vertexColorIndex + 4, verticesColors.get(vertexColorIndex + 2)); + } + vertexColorIndex += 6; + } else { + if (verticesColors != null) { + verticesColors.remove(vertexColorIndex + 3); + vertexColorIndex += 3; + } + } + } + Vector3f[] normals = normalList.toArray(new Vector3f[normalList.size()]); + + // reading vertices groups (from the parent) + Structure parent = dataRepository.peekParent(); + Structure defbase = (Structure) parent.getFieldValue("defbase"); + List defs = defbase.evaluateListBase(dataRepository); + String[] verticesGroups = new String[defs.size()]; + int defIndex = 0; + for (Structure def : defs) { + verticesGroups[defIndex++] = def.getFieldValue("name").toString(); + } + + // vertices bone weights and indices + ArmatureHelper armatureHelper = dataRepository.getHelper(ArmatureHelper.class); + Structure defBase = (Structure) parent.getFieldValue("defbase"); + Map groupToBoneIndexMap = armatureHelper.getGroupToBoneIndexMap(defBase, dataRepository); + + VertexBuffer verticesWeights = null, verticesWeightsIndices = null; + int[] bonesGroups = new int[]{0}; + VertexBuffer[] boneWeightsAndIndex = this.getBoneWeightAndIndexBuffer(structure, vertexList.size(), bonesGroups, + vertexReferenceMap, groupToBoneIndexMap, dataRepository); + verticesWeights = boneWeightsAndIndex[0]; + verticesWeightsIndices = boneWeightsAndIndex[1]; + + // reading materials + MaterialHelper materialHelper = dataRepository.getHelper(MaterialHelper.class); + Material[] materials = null; + Material[] nonTexturedMaterials = null; + if ((dataRepository.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.MATERIALS) != 0) { + materials = materialHelper.getMaterials(structure, dataRepository); + nonTexturedMaterials = materials == null ? null : new Material[materials.length];// fill it when needed + } + + // creating the result meshes + geometries = new ArrayList(meshesMap.size()); + + VertexBuffer verticesBuffer = new VertexBuffer(Type.Position); + verticesBuffer.setupData(Usage.Stream, 3, Format.Float, + BufferUtils.createFloatBuffer(vertexList.toArray(new Vector3f[vertexList.size()]))); + + // initial vertex position (used with animation) + VertexBuffer verticesBind = new VertexBuffer(Type.BindPosePosition); + verticesBind.setupData(Usage.CpuOnly, 3, Format.Float, BufferUtils.clone(verticesBuffer.getData())); + + VertexBuffer normalsBuffer = new VertexBuffer(Type.Normal); + normalsBuffer.setupData(Usage.Stream, 3, Format.Float, BufferUtils.createFloatBuffer(normals)); + + // initial normals position (used with animation) + VertexBuffer normalsBind = new VertexBuffer(Type.BindPoseNormal); + normalsBind.setupData(Usage.CpuOnly, 3, Format.Float, BufferUtils.clone(normalsBuffer.getData())); + + VertexBuffer uvCoordsBuffer = null; + if (uvCoordinates != null) { + uvCoordsBuffer = new VertexBuffer(Type.TexCoord); + uvCoordsBuffer.setupData(Usage.Static, 2, Format.Float, + BufferUtils.createFloatBuffer(uvCoordinates.toArray(new Vector2f[uvCoordinates.size()]))); + } + + // generating meshes + FloatBuffer verticesColorsBuffer = this.createFloatBuffer(verticesColors); + for (Entry> meshEntry : meshesMap.entrySet()) { + Mesh mesh = new Mesh(); + + // creating vertices indices for this mesh + List indexList = meshEntry.getValue(); + short[] indices = new short[indexList.size()];//TODO: check if the model doesn't have more than 32767 vertices + for (int i = 0; i < indexList.size(); ++i) {//if yes then mesh.getVertices method must be changed to accept other than ShortBuffer + indices[i] = indexList.get(i).shortValue(); + } + + // setting vertices + mesh.setBuffer(Type.Index, 1, BufferUtils.createShortBuffer(indices)); + mesh.setBuffer(verticesBuffer); + mesh.setBuffer(verticesBind); + + // setting vertices colors + if (verticesColorsBuffer != null) { + mesh.setBuffer(Type.Color, 4, verticesColorsBuffer); + } + + // setting weights for bones + if (verticesWeights != null) { + mesh.setMaxNumWeights(bonesGroups[0]); + mesh.setBuffer(verticesWeights); + mesh.setBuffer(verticesWeightsIndices); + } + + // setting faces' normals + mesh.setBuffer(normalsBuffer); + mesh.setBuffer(normalsBind); + + // setting uvCoords + if (uvCoordsBuffer != null) { + mesh.setBuffer(uvCoordsBuffer); + } + + // creating the result + Geometry geometry = new Geometry(name + (geometries.size() + 1), mesh); + if (materials != null) { + int materialNumber = meshEntry.getKey().intValue(); + Material material; + if (materialNumber >= 0) { + material = materials[materialNumber]; + if (materialNumberToTexture.containsKey(Integer.valueOf(materialNumber))) { + if (material.getMaterialDef().getAssetName().contains("Lighting")) { + if (!materialHelper.hasTexture(material, MaterialHelper.TEXTURE_TYPE_DIFFUSE)) { + material = material.clone(); + material.setTexture(MaterialHelper.TEXTURE_TYPE_DIFFUSE, + materialNumberToTexture.get(Integer.valueOf(materialNumber))); + } + } else { + if (!materialHelper.hasTexture(material, MaterialHelper.TEXTURE_TYPE_COLOR)) { + material = material.clone(); + material.setTexture(MaterialHelper.TEXTURE_TYPE_COLOR, + materialNumberToTexture.get(Integer.valueOf(materialNumber))); + } + } + } + } else { + materialNumber = -1 * (materialNumber + 1); + if (nonTexturedMaterials[materialNumber] == null) { + nonTexturedMaterials[materialNumber] = materialHelper.getNonTexturedMaterial(materials[materialNumber], + TextureHelper.TEX_IMAGE); + } + material = nonTexturedMaterials[materialNumber]; + } + geometry.setMaterial(material); + } else { + geometry.setMaterial(dataRepository.getDefaultMaterial()); + } + geometries.add(geometry); + } + dataRepository.addLoadedFeatures(structure.getOldMemoryAddress(), structure.getName(), structure, geometries); + return geometries; + } + + /** + * This method adds a normal to a normals' map. This map is used to merge normals of a vertor that should be rendered smooth. + * + * @param normalToAdd + * a normal to be added + * @param normalMap + * merges normals of faces that will be rendered smooth; the key is the vertex and the value - its normal vector + * @param smooth + * the variable that indicates wheather to merge normals (creating the smooth mesh) or not + * @param vertices + * a list of vertices read from the blender file + */ + protected void addNormal(Vector3f normalToAdd, Map normalMap, boolean smooth, Vector3f... vertices) { + for (Vector3f v : vertices) { + Vector3f n = normalMap.get(v); + if (!smooth || n == null) { + normalMap.put(v, normalToAdd.clone()); + } else { + n.addLocal(normalToAdd).normalizeLocal(); + } + } + } + + /** + * This method fills the vertex reference map. The vertices are loaded once and referenced many times in the model. This map is created + * 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 + * - the reference indices list. + * + * @param basicVertexIndex + * the index of the vertex from its basic table + * @param resultIndex + * the index of the vertex in its result vertex list + * @param vertexReferenceMap + * the reference map + */ + protected void appendVertexReference(int basicVertexIndex, int resultIndex, Map> vertexReferenceMap) { + List referenceList = vertexReferenceMap.get(Integer.valueOf(basicVertexIndex)); + if (referenceList == null) { + referenceList = new ArrayList(); + vertexReferenceMap.put(Integer.valueOf(basicVertexIndex), referenceList); + } + referenceList.add(Integer.valueOf(resultIndex)); + } + + /** + * This method returns the vertices colors. Each vertex is stored in float[4] array. + * + * @param meshStructure + * the structure containing the mesh data + * @param dataRepository + * the data repository + * @return a list of vertices colors, each color belongs to a single vertex + * @throws BlenderFileException + * this exception is thrown when the blend file structure is somehow invalid or corrupted + */ + public List getVerticesColors(Structure meshStructure, DataRepository dataRepository) throws BlenderFileException { + Pointer pMCol = (Pointer) meshStructure.getFieldValue("mcol"); + List verticesColors = null; + List mCol = null; + if (!pMCol.isNull()) { + verticesColors = new LinkedList(); + mCol = pMCol.fetchData(dataRepository.getInputStream()); + for (Structure color : mCol) { + float r = ((Number) color.getFieldValue("r")).byteValue() / 256.0f; + float g = ((Number) color.getFieldValue("g")).byteValue() / 256.0f; + float b = ((Number) color.getFieldValue("b")).byteValue() / 256.0f; + float a = ((Number) color.getFieldValue("a")).byteValue() / 256.0f; + verticesColors.add(new float[]{b, g, r, a}); + } + } + return verticesColors; + } + + /** + * This method returns the vertices. + * + * @param meshStructure + * the structure containing the mesh data + * @param dataRepository + * the data repository + * @return a list of vertices colors, each color belongs to a single vertex + * @throws BlenderFileException + * this exception is thrown when the blend file structure is somehow invalid or corrupted + */ + @SuppressWarnings("unchecked") + public Vector3f[] getVertices(Structure meshStructure, DataRepository dataRepository) throws BlenderFileException { + int verticesAmount = ((Number) meshStructure.getFieldValue("totvert")).intValue(); + Vector3f[] vertices = new Vector3f[verticesAmount]; + Pointer pMVert = (Pointer) meshStructure.getFieldValue("mvert"); + List mVerts = pMVert.fetchData(dataRepository.getInputStream()); + for (int i = 0; i < verticesAmount; ++i) { + DynamicArray coordinates = (DynamicArray) mVerts.get(i).getFieldValue("co"); + vertices[i] = new Vector3f(coordinates.get(0).floatValue(), coordinates.get(1).floatValue(), coordinates.get(2).floatValue()); + } + return vertices; + } + + /** + * 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 + * second element is a vertex buffer holding bone indices for vertices (the indices of bones the vertices are assigned to). + * + * @param meshStructure + * the mesh structure object + * @param vertexListSize + * a number of vertices in the model + * @param bonesGroups + * this is an output parameter, it should be a one-sized array; the maximum amount of weights per vertex (up to + * MAXIMUM_WEIGHTS_PER_VERTEX) is stored there + * @param vertexReferenceMap + * this reference map allows to map the original vertices read from blender to vertices that are really in the model; one + * vertex may appear several times in the result model + * @param groupToBoneIndexMap + * this object maps the group index (to which a vertices in blender belong) to bone index of the model + * @param dataRepository + * the data repository + * @return arrays of vertices weights and their bone indices and (as an outpot parameter) the maximum amount of weights for a vertex + * @throws BlenderFileException + * this exception is thrown when the blend file structure is somehow invalid or corrupted + */ + public VertexBuffer[] getBoneWeightAndIndexBuffer(Structure meshStructure, int vertexListSize, int[] bonesGroups, + Map> vertexReferenceMap, Map groupToBoneIndexMap, DataRepository dataRepository) + throws BlenderFileException { + Pointer pDvert = (Pointer) meshStructure.getFieldValue("dvert");// dvert = DeformVERTices + FloatBuffer weightsFloatData = BufferUtils.createFloatBuffer(vertexListSize * MAXIMUM_WEIGHTS_PER_VERTEX); + ByteBuffer indicesData = BufferUtils.createByteBuffer(vertexListSize * MAXIMUM_WEIGHTS_PER_VERTEX); + if (!pDvert.isNull()) {// assigning weights and bone indices + List dverts = pDvert.fetchData(dataRepository.getInputStream());// dverts.size() == verticesAmount (one dvert per + // vertex in blender) + int vertexIndex = 0; + for (Structure dvert : dverts) { + int totweight = ((Number) dvert.getFieldValue("totweight")).intValue();// total amount of weights assignet to the vertex + // (max. 4 in JME) + Pointer pDW = (Pointer) dvert.getFieldValue("dw"); + List vertexIndices = vertexReferenceMap.get(Integer.valueOf(vertexIndex));// we fetch the referenced vertices here + if (totweight > 0 && !pDW.isNull()) {// pDW should never be null here, but I check it just in case :) + int weightIndex = 0; + List dw = pDW.fetchData(dataRepository.getInputStream()); + for (Structure deformWeight : dw) { + Integer boneIndex = groupToBoneIndexMap.get(((Number) deformWeight.getFieldValue("def_nr")).intValue()); + if (boneIndex != null) {// null here means that we came accross group that has no bone attached to + float weight = ((Number) deformWeight.getFieldValue("weight")).floatValue(); + if (weight == 0.0f) { + weight = 1; + boneIndex = Integer.valueOf(0); + } + // we apply the weight to all referenced vertices + for (Integer index : vertexIndices) { + // all indices are always assigned to 0-indexed bone + // weightsFloatData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX + weightIndex, 1.0f); + // indicesData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX + weightIndex, (byte)0); + // if(weight != 0.0f) { + weightsFloatData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX + weightIndex, weight); + indicesData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX + weightIndex, boneIndex.byteValue()); + // } + } + } + ++weightIndex; + } + } else { + for (Integer index : vertexIndices) { + weightsFloatData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX, 1.0f); + indicesData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX, (byte) 0); + } + } + ++vertexIndex; + } + } else { + // always bind all vertices to 0-indexed bone + // this bone makes the model look normally if vertices have no bone assigned + // and it is used in object animation, so if we come accross object animation + // we can use the 0-indexed bone for this + for (List vertexIndexList : vertexReferenceMap.values()) { + // we apply the weight to all referenced vertices + for (Integer index : vertexIndexList) { + weightsFloatData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX, 1.0f); + indicesData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX, (byte) 0); + } + } + } + + bonesGroups[0] = this.endBoneAssigns(vertexListSize, weightsFloatData); + VertexBuffer verticesWeights = new VertexBuffer(Type.BoneWeight); + verticesWeights.setupData(Usage.CpuOnly, bonesGroups[0], Format.Float, weightsFloatData); + + VertexBuffer verticesWeightsIndices = new VertexBuffer(Type.BoneIndex); + verticesWeightsIndices.setupData(Usage.CpuOnly, bonesGroups[0], Format.UnsignedByte, indicesData); + return new VertexBuffer[]{verticesWeights, verticesWeightsIndices}; + } + + /** + * Normalizes weights if needed and finds largest amount of weights used for all vertices in the buffer. + */ + protected int endBoneAssigns(int vertCount, FloatBuffer weightsFloatData) { + int maxWeightsPerVert = 0; + weightsFloatData.rewind(); + for (int v = 0; v < vertCount; ++v) { + float w0 = weightsFloatData.get(), w1 = weightsFloatData.get(), w2 = weightsFloatData.get(), w3 = weightsFloatData.get(); + + if (w3 != 0) { + maxWeightsPerVert = Math.max(maxWeightsPerVert, 4); + } else if (w2 != 0) { + maxWeightsPerVert = Math.max(maxWeightsPerVert, 3); + } else if (w1 != 0) { + maxWeightsPerVert = Math.max(maxWeightsPerVert, 2); + } else if (w0 != 0) { + maxWeightsPerVert = Math.max(maxWeightsPerVert, 1); + } + + float sum = w0 + w1 + w2 + w3; + if (sum != 1f && sum != 0.0f) { + weightsFloatData.position(weightsFloatData.position() - 4); + // compute new vals based on sum + float sumToB = 1f / sum; + weightsFloatData.put(w0 * sumToB); + weightsFloatData.put(w1 * sumToB); + weightsFloatData.put(w2 * sumToB); + weightsFloatData.put(w3 * sumToB); + } + } + weightsFloatData.rewind(); + + // mesh.setMaxNumWeights(maxWeightsPerVert); + return maxWeightsPerVert; + } } diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/helpers/v249/ModifierHelper.java b/engine/src/blender/com/jme3/scene/plugins/blender/helpers/v249/ModifierHelper.java index 09ec8e2e1..879945449 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/helpers/v249/ModifierHelper.java +++ b/engine/src/blender/com/jme3/scene/plugins/blender/helpers/v249/ModifierHelper.java @@ -77,465 +77,466 @@ import com.jme3.scene.plugins.ogre.AnimData; * @author Marcin Roguski */ public class ModifierHelper extends AbstractBlenderHelper { - private static final Logger LOGGER = Logger.getLogger(ModifierHelper.class.getName()); - - /** - * This constructor parses the given blender version and stores the result. Some functionalities may differ in - * different blender versions. - * @param blenderVersion - * the version read from the blend file - */ - public ModifierHelper(String blenderVersion) { - super(blenderVersion); - } - - /** - * This method applies modifier to the object. - * @param node - * the loaded object - * @param modifier - * the modifier to apply - * @param dataRepository - * the data repository - * @return the node to whom the modifier was applied - */ - public Node applyModifier(Node node, Modifier modifier, DataRepository dataRepository) { - if(Modifier.ARMATURE_MODIFIER_DATA.equals(modifier.getType())) { - return this.applyArmatureModifierData(node, modifier, dataRepository); - } else if(Modifier.ARRAY_MODIFIER_DATA.equals(modifier.getType())) { - return this.applyArrayModifierData(node, modifier, dataRepository); - } else if(Modifier.PARTICLE_MODIFIER_DATA.equals(modifier.getType())) { - return this.applyParticleSystemModifierData(node, modifier, dataRepository); - } else { - LOGGER.warning("Modifier: " + modifier.getType() + " not yet implemented!!!"); - return node; - } - } - - /** - * This method reads the given object's modifiers. - * @param objectStructure - * the object structure - * @param dataRepository - * the data repository - * @param converter - * the converter object (in some cases we need to read an object first before loading the modifier) - * @throws BlenderFileException - * this exception is thrown when the blender file is somehow corrupted - */ - @SuppressWarnings("unchecked") - public void readModifiers(Structure objectStructure, DataRepository dataRepository) throws BlenderFileException { - Structure modifiersListBase = (Structure)objectStructure.getFieldValue("modifiers"); - List modifiers = modifiersListBase.evaluateListBase(dataRepository); - for(Structure modifier : modifiers) { - Object loadedModifier = null; - Object modifierAdditionalData = null; - if(Modifier.ARRAY_MODIFIER_DATA.equals(modifier.getType())) {//****************ARRAY MODIFIER - Map params = new HashMap(); - - Number fittype = (Number) modifier.getFieldValue("fit_type"); - params.put("fittype", fittype); - switch(fittype.intValue()) { - case 0://FIXED COUNT - params.put("count", modifier.getFieldValue("count")); - break; - case 1://FIXED LENGTH - params.put("length", modifier.getFieldValue("length")); - break; - case 2://FITCURVE - //TODO: implement after loading curves is added; warning will be generated during modifier applying - break; - default: - assert false : "Unknown array modifier fit type: " + fittype; - } - - //offset parameters - int offsettype = ((Number) modifier.getFieldValue("offset_type")).intValue(); - if((offsettype & 0x01) != 0) {//Constant offset - DynamicArray offsetArray = (DynamicArray)modifier.getFieldValue("offset"); - float[] offset = new float[] {offsetArray.get(0).floatValue(), offsetArray.get(1).floatValue(), offsetArray.get(2).floatValue()}; - params.put("offset", offset); - } - if((offsettype & 0x02) != 0) {//Relative offset - DynamicArray scaleArray = (DynamicArray)modifier.getFieldValue("scale"); - float[] scale = new float[] {scaleArray.get(0).floatValue(), scaleArray.get(1).floatValue(), scaleArray.get(2).floatValue()}; - params.put("scale", scale); - } - if((offsettype & 0x04) != 0) {//Object offset - Pointer pOffsetObject = (Pointer)modifier.getFieldValue("offset_ob"); - if(!pOffsetObject.isNull()) { - params.put("offsetob", pOffsetObject); - } - } - - //start cap and end cap - Pointer pStartCap = (Pointer)modifier.getFieldValue("start_cap"); - if(!pStartCap.isNull()) { - params.put("startcap", pStartCap); - } - Pointer pEndCap = (Pointer)modifier.getFieldValue("end_cap"); - if(!pEndCap.isNull()) { - params.put("endcap", pEndCap); - } - loadedModifier = params; - } else if(Modifier.ARMATURE_MODIFIER_DATA.equals(modifier.getType())) {//****************ARMATURE MODIFIER - Pointer pArmatureObject = (Pointer)modifier.getFieldValue("object"); - if(!pArmatureObject.isNull()) { - ObjectHelper objectHelper = dataRepository.getHelper(ObjectHelper.class); - Structure armatureObject = (Structure)dataRepository.getLoadedFeature(pArmatureObject.getOldMemoryAddress(), LoadedFeatureDataType.LOADED_STRUCTURE); - if(armatureObject == null) {//we check this first not to fetch the structure unnecessary - armatureObject = pArmatureObject.fetchData(dataRepository.getInputStream()).get(0); - objectHelper.toObject(armatureObject, dataRepository); - } - modifierAdditionalData = armatureObject.getOldMemoryAddress(); - ArmatureHelper armatureHelper = dataRepository.getHelper(ArmatureHelper.class); - - //changing bones matrices so that they fit the current object (taht is why we need a copy of a skeleton) - Matrix4f armatureObjectMatrix = objectHelper.getTransformationMatrix(armatureObject); - Matrix4f inverseMeshObjectMatrix = objectHelper.getTransformationMatrix(objectStructure).invert(); - Matrix4f additionalRootBoneTransformation = inverseMeshObjectMatrix.multLocal(armatureObjectMatrix); - Bone[] bones = armatureHelper.buildBonesStructure(Long.valueOf(0L), additionalRootBoneTransformation); - - String objectName = objectStructure.getName(); - Set animationNames = dataRepository.getBlenderKey().getAnimationNames(objectName); - if(animationNames != null && animationNames.size() > 0) { - ArrayList animations = new ArrayList(); - List actionHeaders = dataRepository.getFileBlocks(Integer.valueOf(FileBlockHeader.BLOCK_AC00)); - for(FileBlockHeader header : actionHeaders) { - Structure actionStructure = header.getStructure(dataRepository); - String actionName = actionStructure.getName(); - if(animationNames.contains(actionName)) { - int[] animationFrames = dataRepository.getBlenderKey().getAnimationFrames(objectName, actionName); - int fps = dataRepository.getBlenderKey().getFps(); - float start = (float)animationFrames[0] / (float)fps; - float stop = (float)animationFrames[1] / (float)fps; - BoneAnimation boneAnimation = new BoneAnimation(actionName, stop - start); - boneAnimation.setTracks(armatureHelper.getTracks(actionStructure, dataRepository, objectName, actionName)); - animations.add(boneAnimation); - } - } - loadedModifier = new AnimData(new Skeleton(bones), animations); - } - } else { - LOGGER.warning("Unsupported modifier type: " + modifier.getType()); - } - } else if(Modifier.PARTICLE_MODIFIER_DATA.equals(modifier.getType())) {//****************PARTICLES MODIFIER - Pointer pParticleSystem = (Pointer) modifier.getFieldValue("psys"); - if(!pParticleSystem.isNull()) { - ParticlesHelper particlesHelper = dataRepository.getHelper(ParticlesHelper.class); - Structure particleSystem = pParticleSystem.fetchData(dataRepository.getInputStream()).get(0); - loadedModifier = particlesHelper.toParticleEmitter(particleSystem, dataRepository); - } - } - //adding modifier to the modifier's lists - if(loadedModifier != null) { - dataRepository.addModifier(objectStructure.getOldMemoryAddress(), modifier.getType(), loadedModifier, modifierAdditionalData); - modifierAdditionalData = null; - } - } - } - - /** - * This method applies particles emitter to the given node. - * @param node the particles emitter node - * @param modifier the modifier containing the emitter data - * @param dataRepository the data repository - * @return node with particles' emitter applied - */ - protected Node applyParticleSystemModifierData(Node node, Modifier modifier, DataRepository dataRepository) { - MaterialHelper materialHelper = dataRepository.getHelper(MaterialHelper.class); - ParticleEmitter emitter = (ParticleEmitter) modifier.getJmeModifierRepresentation(); - emitter = emitter.clone(); - - //veryfying the alpha function for particles' texture - Integer alphaFunction = MaterialHelper.ALPHA_MASK_HYPERBOLE; - char nameSuffix = emitter.getName().charAt(emitter.getName().length()-1); - if(nameSuffix=='B' || nameSuffix=='N') { - alphaFunction = MaterialHelper.ALPHA_MASK_NONE; - } - //removing the type suffix from the name - emitter.setName(emitter.getName().substring(0, emitter.getName().length()-1)); - - //applying emitter shape - EmitterShape emitterShape = emitter.getShape(); - List meshes = new ArrayList(); - for(Spatial spatial : node.getChildren()) { - if(spatial instanceof Geometry) { - Mesh mesh = ((Geometry) spatial).getMesh(); - if(mesh != null) { - meshes.add(mesh); - Material material = materialHelper.getParticlesMaterial(((Geometry) spatial).getMaterial(), alphaFunction, dataRepository); - emitter.setMaterial(material);//TODO: divide into several pieces - } - } - } - if(meshes.size()>0 && emitterShape instanceof EmitterMeshVertexShape) { - ((EmitterMeshVertexShape) emitterShape).setMeshes(meshes); - } - - node.attachChild(emitter); - return node; - } - - /** - * This method applies ArmatureModifierData to the loaded object. - * @param node - * the loaded object - * @param modifier - * the modifier to apply - * @param dataRepository - * the data repository - * @return the node to whom the modifier was applied - */ - protected Node applyArmatureModifierData(Node node, Modifier modifier, DataRepository dataRepository) { - AnimData ad = (AnimData)modifier.getJmeModifierRepresentation(); - ArrayList animList = ad.anims; - Long modifierArmatureObject = (Long)modifier.getAdditionalData(); - if(animList != null && animList.size() > 0) { - ConstraintHelper constraintHelper = dataRepository.getHelper(ConstraintHelper.class); - Constraint[] constraints = constraintHelper.getConstraints(modifierArmatureObject); - HashMap anims = new HashMap(); - for(int i = 0; i < animList.size(); ++i) { - BoneAnimation boneAnimation = this.cloneBoneAnimation(animList.get(i)); - - //baking constraints into animations - if(constraints != null && constraints.length > 0) { - for(Constraint constraint : constraints) { - constraint.affectAnimation(ad.skeleton, boneAnimation); - } - } - - anims.put(boneAnimation.getName(), boneAnimation); - } - - //getting meshes - Mesh[] meshes = null; - List meshesList = new ArrayList(); - List children = node.getChildren(); - for(Spatial child : children) { - if(child instanceof Geometry) { - meshesList.add(((Geometry)child).getMesh()); - } - } - if(meshesList.size() > 0) { - meshes = meshesList.toArray(new Mesh[meshesList.size()]); - } - - //applying the control to the node - SkeletonControl skeletonControl = new SkeletonControl(meshes, ad.skeleton); - AnimControl control = node.getControl(AnimControl.class); - - if(control == null) { - control = new AnimControl(ad.skeleton); - } else { - //merging skeletons - Skeleton controlSkeleton = control.getSkeleton(); - int boneIndexIncrease = controlSkeleton.getBoneCount(); - Skeleton skeleton = this.merge(controlSkeleton, ad.skeleton); - - //merging animations - HashMap animations = new HashMap(); - for(String animationName : control.getAnimationNames()) { - animations.put(animationName, control.getAnim(animationName)); - } - for(Entry animEntry : anims.entrySet()) { - BoneAnimation ba = animEntry.getValue(); - for(int i = 0; i < ba.getTracks().length; ++i) { - BoneTrack bt = ba.getTracks()[i]; - int newBoneIndex = bt.getTargetBoneIndex() + boneIndexIncrease; - ba.getTracks()[i] = new BoneTrack(newBoneIndex, bt.getTimes(), bt.getTranslations(), bt.getRotations(), bt.getScales()); - } - animations.put(animEntry.getKey(), animEntry.getValue()); - } - - //replacing the control - node.removeControl(control); - control = new AnimControl(skeleton); - } - control.setAnimations(anims); - node.addControl(control); - node.addControl(skeletonControl); - } - return node; - } - - /** - * This method applies the array modifier to the node. - * @param node - * the object the modifier will be applied to - * @param modifier - * the modifier to be applied - * @param dataRepository - * the data repository - * @return object node with arry modifier applied - */ - @SuppressWarnings("unchecked") - protected Node applyArrayModifierData(Node node, Modifier modifier, DataRepository dataRepository) { - Map modifierData = (Map) modifier.getJmeModifierRepresentation(); - int fittype = ((Number)modifierData.get("fittype")).intValue(); - float[] offset = (float[])modifierData.get("offset"); - if(offset==null) {//the node will be repeated several times in the same place - offset = new float[] {0.0f, 0.0f, 0.0f}; - } - float[] scale = (float[])modifierData.get("scale"); - if(scale==null) {//the node will be repeated several times in the same place - scale = new float[] {0.0f, 0.0f, 0.0f}; - } else { - //getting bounding box - node.updateModelBound(); - BoundingVolume boundingVolume = node.getWorldBound(); - if(boundingVolume instanceof BoundingBox) { - scale[0] *= ((BoundingBox) boundingVolume).getXExtent() * 2.0f; - scale[1] *= ((BoundingBox) boundingVolume).getYExtent() * 2.0f; - scale[2] *= ((BoundingBox) boundingVolume).getZExtent() * 2.0f; - } else if(boundingVolume instanceof BoundingSphere) { - float radius = ((BoundingSphere) boundingVolume).getRadius(); - scale[0] *= radius * 2.0f; - scale[1] *= radius * 2.0f; - scale[2] *= radius * 2.0f; - } else { - throw new IllegalStateException("Unknown bounding volume type: " + boundingVolume.getClass().getName()); - } - } - - //adding object's offset - float[] objectOffset = new float[] {0.0f, 0.0f, 0.0f}; - Pointer pOffsetObject = (Pointer) modifierData.get("offsetob"); - if(pOffsetObject!=null) { - FileBlockHeader offsetObjectBlock = dataRepository.getFileBlock(pOffsetObject.getOldMemoryAddress()); - ObjectHelper objectHelper = dataRepository.getHelper(ObjectHelper.class); - try {//we take the structure in case the object was not yet loaded - Structure offsetStructure = offsetObjectBlock.getStructure(dataRepository); - Vector3f translation = objectHelper.getTransformation(offsetStructure).getTranslation(); - objectOffset[0] = translation.x; - objectOffset[1] = translation.y; - objectOffset[2] = translation.z; - } catch (BlenderFileException e) { - LOGGER.warning("Problems in blender file structure! Object offset cannot be applied! The problem: " + e.getMessage()); - } - } - - //getting start and end caps - Node[] caps = new Node[] {null, null}; - Pointer[] pCaps = new Pointer[] {(Pointer) modifierData.get("startcap"), (Pointer) modifierData.get("endcap")}; - for(int i=0;i0.0f) { - count = (int)(length / translationVector.length()) - 1; - } - } else if(fittype==2) {//Fit curve - LOGGER.warning("Fit curve mode in array modifier not yet implemented!");//TODO: implement fit curve - } else { - throw new IllegalStateException("Unknown fit type: " + fittype); - } - - //adding translated nodes and caps - if(count>0) { - Node[] arrayNodes = new Node[count]; - Vector3f newTranslation = node.getLocalTranslation().clone(); - for(int i=0;i bones = new ArrayList(s1.getBoneCount() + s2.getBoneCount()); - for(int i = 0; i < s1.getBoneCount(); ++i) { - bones.add(s1.getBone(i)); - } - for(int i = 1; i < s2.getBoneCount(); ++i) {//ommit objectAnimationBone - bones.add(s2.getBone(i)); - } - return new Skeleton(bones.toArray(new Bone[bones.size()])); - } + + private static final Logger LOGGER = Logger.getLogger(ModifierHelper.class.getName()); + + /** + * This constructor parses the given blender version and stores the result. Some functionalities may differ in + * different blender versions. + * @param blenderVersion + * the version read from the blend file + */ + public ModifierHelper(String blenderVersion) { + super(blenderVersion); + } + + /** + * This method applies modifier to the object. + * @param node + * the loaded object + * @param modifier + * the modifier to apply + * @param dataRepository + * the data repository + * @return the node to whom the modifier was applied + */ + public Node applyModifier(Node node, Modifier modifier, DataRepository dataRepository) { + if (Modifier.ARMATURE_MODIFIER_DATA.equals(modifier.getType())) { + return this.applyArmatureModifierData(node, modifier, dataRepository); + } else if (Modifier.ARRAY_MODIFIER_DATA.equals(modifier.getType())) { + return this.applyArrayModifierData(node, modifier, dataRepository); + } else if (Modifier.PARTICLE_MODIFIER_DATA.equals(modifier.getType())) { + return this.applyParticleSystemModifierData(node, modifier, dataRepository); + } else { + LOGGER.warning("Modifier: " + modifier.getType() + " not yet implemented!!!"); + return node; + } + } + + /** + * This method reads the given object's modifiers. + * @param objectStructure + * the object structure + * @param dataRepository + * the data repository + * @param converter + * the converter object (in some cases we need to read an object first before loading the modifier) + * @throws BlenderFileException + * this exception is thrown when the blender file is somehow corrupted + */ + @SuppressWarnings("unchecked") + public void readModifiers(Structure objectStructure, DataRepository dataRepository) throws BlenderFileException { + Structure modifiersListBase = (Structure) objectStructure.getFieldValue("modifiers"); + List modifiers = modifiersListBase.evaluateListBase(dataRepository); + for (Structure modifier : modifiers) { + Object loadedModifier = null; + Object modifierAdditionalData = null; + if (Modifier.ARRAY_MODIFIER_DATA.equals(modifier.getType())) {//****************ARRAY MODIFIER + Map params = new HashMap(); + + Number fittype = (Number) modifier.getFieldValue("fit_type"); + params.put("fittype", fittype); + switch (fittype.intValue()) { + case 0://FIXED COUNT + params.put("count", modifier.getFieldValue("count")); + break; + case 1://FIXED LENGTH + params.put("length", modifier.getFieldValue("length")); + break; + case 2://FITCURVE + //TODO: implement after loading curves is added; warning will be generated during modifier applying + break; + default: + assert false : "Unknown array modifier fit type: " + fittype; + } + + //offset parameters + int offsettype = ((Number) modifier.getFieldValue("offset_type")).intValue(); + if ((offsettype & 0x01) != 0) {//Constant offset + DynamicArray offsetArray = (DynamicArray) modifier.getFieldValue("offset"); + float[] offset = new float[]{offsetArray.get(0).floatValue(), offsetArray.get(1).floatValue(), offsetArray.get(2).floatValue()}; + params.put("offset", offset); + } + if ((offsettype & 0x02) != 0) {//Relative offset + DynamicArray scaleArray = (DynamicArray) modifier.getFieldValue("scale"); + float[] scale = new float[]{scaleArray.get(0).floatValue(), scaleArray.get(1).floatValue(), scaleArray.get(2).floatValue()}; + params.put("scale", scale); + } + if ((offsettype & 0x04) != 0) {//Object offset + Pointer pOffsetObject = (Pointer) modifier.getFieldValue("offset_ob"); + if (!pOffsetObject.isNull()) { + params.put("offsetob", pOffsetObject); + } + } + + //start cap and end cap + Pointer pStartCap = (Pointer) modifier.getFieldValue("start_cap"); + if (!pStartCap.isNull()) { + params.put("startcap", pStartCap); + } + Pointer pEndCap = (Pointer) modifier.getFieldValue("end_cap"); + if (!pEndCap.isNull()) { + params.put("endcap", pEndCap); + } + loadedModifier = params; + } else if (Modifier.ARMATURE_MODIFIER_DATA.equals(modifier.getType())) {//****************ARMATURE MODIFIER + Pointer pArmatureObject = (Pointer) modifier.getFieldValue("object"); + if (!pArmatureObject.isNull()) { + ObjectHelper objectHelper = dataRepository.getHelper(ObjectHelper.class); + Structure armatureObject = (Structure) dataRepository.getLoadedFeature(pArmatureObject.getOldMemoryAddress(), LoadedFeatureDataType.LOADED_STRUCTURE); + if (armatureObject == null) {//we check this first not to fetch the structure unnecessary + armatureObject = pArmatureObject.fetchData(dataRepository.getInputStream()).get(0); + objectHelper.toObject(armatureObject, dataRepository); + } + modifierAdditionalData = armatureObject.getOldMemoryAddress(); + ArmatureHelper armatureHelper = dataRepository.getHelper(ArmatureHelper.class); + + //changing bones matrices so that they fit the current object (taht is why we need a copy of a skeleton) + Matrix4f armatureObjectMatrix = objectHelper.getTransformationMatrix(armatureObject); + Matrix4f inverseMeshObjectMatrix = objectHelper.getTransformationMatrix(objectStructure).invert(); + Matrix4f additionalRootBoneTransformation = inverseMeshObjectMatrix.multLocal(armatureObjectMatrix); + Bone[] bones = armatureHelper.buildBonesStructure(Long.valueOf(0L), additionalRootBoneTransformation); + + String objectName = objectStructure.getName(); + Set animationNames = dataRepository.getBlenderKey().getAnimationNames(objectName); + if (animationNames != null && animationNames.size() > 0) { + ArrayList animations = new ArrayList(); + List actionHeaders = dataRepository.getFileBlocks(Integer.valueOf(FileBlockHeader.BLOCK_AC00)); + for (FileBlockHeader header : actionHeaders) { + Structure actionStructure = header.getStructure(dataRepository); + String actionName = actionStructure.getName(); + if (animationNames.contains(actionName)) { + int[] animationFrames = dataRepository.getBlenderKey().getAnimationFrames(objectName, actionName); + int fps = dataRepository.getBlenderKey().getFps(); + float start = (float) animationFrames[0] / (float) fps; + float stop = (float) animationFrames[1] / (float) fps; + BoneAnimation boneAnimation = new BoneAnimation(actionName, stop - start); + boneAnimation.setTracks(armatureHelper.getTracks(actionStructure, dataRepository, objectName, actionName)); + animations.add(boneAnimation); + } + } + loadedModifier = new AnimData(new Skeleton(bones), animations); + } + } else { + LOGGER.warning("Unsupported modifier type: " + modifier.getType()); + } + } else if (Modifier.PARTICLE_MODIFIER_DATA.equals(modifier.getType())) {//****************PARTICLES MODIFIER + Pointer pParticleSystem = (Pointer) modifier.getFieldValue("psys"); + if (!pParticleSystem.isNull()) { + ParticlesHelper particlesHelper = dataRepository.getHelper(ParticlesHelper.class); + Structure particleSystem = pParticleSystem.fetchData(dataRepository.getInputStream()).get(0); + loadedModifier = particlesHelper.toParticleEmitter(particleSystem, dataRepository); + } + } + //adding modifier to the modifier's lists + if (loadedModifier != null) { + dataRepository.addModifier(objectStructure.getOldMemoryAddress(), modifier.getType(), loadedModifier, modifierAdditionalData); + modifierAdditionalData = null; + } + } + } + + /** + * This method applies particles emitter to the given node. + * @param node the particles emitter node + * @param modifier the modifier containing the emitter data + * @param dataRepository the data repository + * @return node with particles' emitter applied + */ + protected Node applyParticleSystemModifierData(Node node, Modifier modifier, DataRepository dataRepository) { + MaterialHelper materialHelper = dataRepository.getHelper(MaterialHelper.class); + ParticleEmitter emitter = (ParticleEmitter) modifier.getJmeModifierRepresentation(); + emitter = emitter.clone(); + + //veryfying the alpha function for particles' texture + Integer alphaFunction = MaterialHelper.ALPHA_MASK_HYPERBOLE; + char nameSuffix = emitter.getName().charAt(emitter.getName().length() - 1); + if (nameSuffix == 'B' || nameSuffix == 'N') { + alphaFunction = MaterialHelper.ALPHA_MASK_NONE; + } + //removing the type suffix from the name + emitter.setName(emitter.getName().substring(0, emitter.getName().length() - 1)); + + //applying emitter shape + EmitterShape emitterShape = emitter.getShape(); + List meshes = new ArrayList(); + for (Spatial spatial : node.getChildren()) { + if (spatial instanceof Geometry) { + Mesh mesh = ((Geometry) spatial).getMesh(); + if (mesh != null) { + meshes.add(mesh); + Material material = materialHelper.getParticlesMaterial(((Geometry) spatial).getMaterial(), alphaFunction, dataRepository); + emitter.setMaterial(material);//TODO: divide into several pieces + } + } + } + if (meshes.size() > 0 && emitterShape instanceof EmitterMeshVertexShape) { + ((EmitterMeshVertexShape) emitterShape).setMeshes(meshes); + } + + node.attachChild(emitter); + return node; + } + + /** + * This method applies ArmatureModifierData to the loaded object. + * @param node + * the loaded object + * @param modifier + * the modifier to apply + * @param dataRepository + * the data repository + * @return the node to whom the modifier was applied + */ + protected Node applyArmatureModifierData(Node node, Modifier modifier, DataRepository dataRepository) { + AnimData ad = (AnimData) modifier.getJmeModifierRepresentation(); + ArrayList animList = ad.anims; + Long modifierArmatureObject = (Long) modifier.getAdditionalData(); + if (animList != null && animList.size() > 0) { + ConstraintHelper constraintHelper = dataRepository.getHelper(ConstraintHelper.class); + Constraint[] constraints = constraintHelper.getConstraints(modifierArmatureObject); + HashMap anims = new HashMap(); + for (int i = 0; i < animList.size(); ++i) { + BoneAnimation boneAnimation = this.cloneBoneAnimation(animList.get(i)); + + //baking constraints into animations + if (constraints != null && constraints.length > 0) { + for (Constraint constraint : constraints) { + constraint.affectAnimation(ad.skeleton, boneAnimation); + } + } + + anims.put(boneAnimation.getName(), boneAnimation); + } + + //getting meshes + Mesh[] meshes = null; + List meshesList = new ArrayList(); + List children = node.getChildren(); + for (Spatial child : children) { + if (child instanceof Geometry) { + meshesList.add(((Geometry) child).getMesh()); + } + } + if (meshesList.size() > 0) { + meshes = meshesList.toArray(new Mesh[meshesList.size()]); + } + + //applying the control to the node + SkeletonControl skeletonControl = new SkeletonControl(meshes, ad.skeleton); + AnimControl control = node.getControl(AnimControl.class); + + if (control == null) { + control = new AnimControl(ad.skeleton); + } else { + //merging skeletons + Skeleton controlSkeleton = control.getSkeleton(); + int boneIndexIncrease = controlSkeleton.getBoneCount(); + Skeleton skeleton = this.merge(controlSkeleton, ad.skeleton); + + //merging animations + HashMap animations = new HashMap(); + for (String animationName : control.getAnimationNames()) { + animations.put(animationName, control.getAnim(animationName)); + } + for (Entry animEntry : anims.entrySet()) { + BoneAnimation ba = animEntry.getValue(); + for (int i = 0; i < ba.getTracks().length; ++i) { + BoneTrack bt = ba.getTracks()[i]; + int newBoneIndex = bt.getTargetBoneIndex() + boneIndexIncrease; + ba.getTracks()[i] = new BoneTrack(newBoneIndex, bt.getTimes(), bt.getTranslations(), bt.getRotations(), bt.getScales()); + } + animations.put(animEntry.getKey(), animEntry.getValue()); + } + + //replacing the control + node.removeControl(control); + control = new AnimControl(skeleton); + } + control.setAnimations(anims); + node.addControl(control); + node.addControl(skeletonControl); + } + return node; + } + + /** + * This method applies the array modifier to the node. + * @param node + * the object the modifier will be applied to + * @param modifier + * the modifier to be applied + * @param dataRepository + * the data repository + * @return object node with arry modifier applied + */ + @SuppressWarnings("unchecked") + protected Node applyArrayModifierData(Node node, Modifier modifier, DataRepository dataRepository) { + Map modifierData = (Map) modifier.getJmeModifierRepresentation(); + int fittype = ((Number) modifierData.get("fittype")).intValue(); + float[] offset = (float[]) modifierData.get("offset"); + if (offset == null) {//the node will be repeated several times in the same place + offset = new float[]{0.0f, 0.0f, 0.0f}; + } + float[] scale = (float[]) modifierData.get("scale"); + if (scale == null) {//the node will be repeated several times in the same place + scale = new float[]{0.0f, 0.0f, 0.0f}; + } else { + //getting bounding box + node.updateModelBound(); + BoundingVolume boundingVolume = node.getWorldBound(); + if (boundingVolume instanceof BoundingBox) { + scale[0] *= ((BoundingBox) boundingVolume).getXExtent() * 2.0f; + scale[1] *= ((BoundingBox) boundingVolume).getYExtent() * 2.0f; + scale[2] *= ((BoundingBox) boundingVolume).getZExtent() * 2.0f; + } else if (boundingVolume instanceof BoundingSphere) { + float radius = ((BoundingSphere) boundingVolume).getRadius(); + scale[0] *= radius * 2.0f; + scale[1] *= radius * 2.0f; + scale[2] *= radius * 2.0f; + } else { + throw new IllegalStateException("Unknown bounding volume type: " + boundingVolume.getClass().getName()); + } + } + + //adding object's offset + float[] objectOffset = new float[]{0.0f, 0.0f, 0.0f}; + Pointer pOffsetObject = (Pointer) modifierData.get("offsetob"); + if (pOffsetObject != null) { + FileBlockHeader offsetObjectBlock = dataRepository.getFileBlock(pOffsetObject.getOldMemoryAddress()); + ObjectHelper objectHelper = dataRepository.getHelper(ObjectHelper.class); + try {//we take the structure in case the object was not yet loaded + Structure offsetStructure = offsetObjectBlock.getStructure(dataRepository); + Vector3f translation = objectHelper.getTransformation(offsetStructure).getTranslation(); + objectOffset[0] = translation.x; + objectOffset[1] = translation.y; + objectOffset[2] = translation.z; + } catch (BlenderFileException e) { + LOGGER.warning("Problems in blender file structure! Object offset cannot be applied! The problem: " + e.getMessage()); + } + } + + //getting start and end caps + Node[] caps = new Node[]{null, null}; + Pointer[] pCaps = new Pointer[]{(Pointer) modifierData.get("startcap"), (Pointer) modifierData.get("endcap")}; + for (int i = 0; i < pCaps.length; ++i) { + if (pCaps[i] != null) { + caps[i] = (Node) dataRepository.getLoadedFeature(pCaps[i].getOldMemoryAddress(), LoadedFeatureDataType.LOADED_FEATURE); + if (caps[i] != null) { + caps[i] = (Node) caps[i].clone(); + } else { + FileBlockHeader capBlock = dataRepository.getFileBlock(pOffsetObject.getOldMemoryAddress()); + try {//we take the structure in case the object was not yet loaded + Structure capStructure = capBlock.getStructure(dataRepository); + ObjectHelper objectHelper = dataRepository.getHelper(ObjectHelper.class); + caps[i] = (Node) objectHelper.toObject(capStructure, dataRepository); + if (caps[i] == null) { + LOGGER.warning("Cap object '" + capStructure.getName() + "' couldn't be loaded!"); + } + } catch (BlenderFileException e) { + LOGGER.warning("Problems in blender file structure! Cap object cannot be applied! The problem: " + e.getMessage()); + } + } + } + } + + Vector3f translationVector = new Vector3f(offset[0] + scale[0] + objectOffset[0], + offset[1] + scale[1] + objectOffset[1], + offset[2] + scale[2] + objectOffset[2]); + + //getting/calculating repeats amount + int count = 0; + if (fittype == 0) {//Fixed count + count = ((Number) modifierData.get("count")).intValue() - 1; + } else if (fittype == 1) {//Fixed length + float length = ((Number) modifierData.get("length")).floatValue(); + if (translationVector.length() > 0.0f) { + count = (int) (length / translationVector.length()) - 1; + } + } else if (fittype == 2) {//Fit curve + LOGGER.warning("Fit curve mode in array modifier not yet implemented!");//TODO: implement fit curve + } else { + throw new IllegalStateException("Unknown fit type: " + fittype); + } + + //adding translated nodes and caps + if (count > 0) { + Node[] arrayNodes = new Node[count]; + Vector3f newTranslation = node.getLocalTranslation().clone(); + for (int i = 0; i < count; ++i) { + newTranslation.addLocal(translationVector); + Node nodeClone = (Node) node.clone(); + nodeClone.setLocalTranslation(newTranslation); + arrayNodes[i] = nodeClone; + } + for (Node nodeClone : arrayNodes) { + node.attachChild(nodeClone); + } + if (caps[0] != null) { + caps[0].getLocalTranslation().set(node.getLocalTranslation()).subtractLocal(translationVector); + node.attachChild(caps[0]); + } + if (caps[1] != null) { + caps[1].getLocalTranslation().set(newTranslation).addLocal(translationVector); + node.attachChild(caps[1]); + } + } + return node; + } + + /** + * This class clones the bone animation data. + * @param source + * the source that is to be cloned + * @return the copy of the given bone animation + */ + protected BoneAnimation cloneBoneAnimation(BoneAnimation source) { + BoneAnimation result = new BoneAnimation(source.getName(), source.getLength()); + + //copying tracks and applying constraints + BoneTrack[] sourceTracks = source.getTracks(); + BoneTrack[] boneTracks = new BoneTrack[sourceTracks.length]; + for (int i = 0; i < sourceTracks.length; ++i) { + int tablesLength = sourceTracks[i].getTimes().length; + + Vector3f[] sourceTranslations = sourceTracks[i].getTranslations(); + Quaternion[] sourceRotations = sourceTracks[i].getRotations(); + Vector3f[] sourceScales = sourceTracks[i].getScales(); + + Vector3f[] translations = new Vector3f[tablesLength]; + Quaternion[] rotations = new Quaternion[tablesLength]; + Vector3f[] scales = sourceScales == null ? null : new Vector3f[tablesLength]; + for (int j = 0; j < tablesLength; ++j) { + translations[j] = sourceTranslations[j].clone(); + rotations[j] = sourceRotations[j].clone(); + if (sourceScales != null) {//only scales may not be applied + scales[j] = sourceScales[j].clone(); + } + } + boneTracks[i] = new BoneTrack(sourceTracks[i].getTargetBoneIndex(), sourceTracks[i].getTimes(),//times do not change, no need to clone them, + translations, rotations, scales); + } + result.setTracks(boneTracks); + return result; + } + + /** + * This method merges two skeletons into one. I assume that each skeleton's 0-indexed bone is objectAnimationBone so + * only one such bone should be placed in the result + * @param s1 + * first skeleton + * @param s2 + * second skeleton + * @return merged skeleton + */ + protected Skeleton merge(Skeleton s1, Skeleton s2) { + List bones = new ArrayList(s1.getBoneCount() + s2.getBoneCount()); + for (int i = 0; i < s1.getBoneCount(); ++i) { + bones.add(s1.getBone(i)); + } + for (int i = 1; i < s2.getBoneCount(); ++i) {//ommit objectAnimationBone + bones.add(s2.getBone(i)); + } + return new Skeleton(bones.toArray(new Bone[bones.size()])); + } } diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/helpers/v249/NoiseHelper.java b/engine/src/blender/com/jme3/scene/plugins/blender/helpers/v249/NoiseHelper.java index 75c6a0428..711054016 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/helpers/v249/NoiseHelper.java +++ b/engine/src/blender/com/jme3/scene/plugins/blender/helpers/v249/NoiseHelper.java @@ -28,7 +28,6 @@ * ***** END GPL LICENSE BLOCK ***** * */ - package com.jme3.scene.plugins.blender.helpers.v249; import java.io.IOException; @@ -56,1476 +55,1504 @@ import com.jme3.scene.plugins.blender.utils.DataRepository; * @author Marcin Roguski (Kaelthas) */ public class NoiseHelper extends AbstractBlenderHelper { - private static final Logger LOGGER = Logger.getLogger(NoiseHelper.class.getName()); - - /* return value */ - protected static final int TEX_INT = 0; - protected static final int TEX_RGB = 1; - protected static final int TEX_NOR = 2; - - /* noisetype */ - protected static final int TEX_NOISESOFT = 0; - protected static final int TEX_NOISEPERL = 1; - - /* tex->stype in texture.c - cloud types */ - protected static final int TEX_DEFAULT = 0; - protected static final int TEX_COLOR = 1; - - /* flag */ - protected static final int TEX_COLORBAND = 1; - protected static final int TEX_FLIPBLEND = 2; - protected static final int TEX_NEGALPHA = 4; - protected static final int TEX_CHECKER_ODD = 8; - protected static final int TEX_CHECKER_EVEN = 16; - protected static final int TEX_PRV_ALPHA = 32; - protected static final int TEX_PRV_NOR = 64; - protected static final int TEX_REPEAT_XMIR = 128; - protected static final int TEX_REPEAT_YMIR = 256; - 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; - - /* tex->noisebasis2 in texture.c - wood waveforms */ - protected static final int TEX_SIN = 0; - protected static final int TEX_SAW = 1; - protected static final int TEX_TRI = 2; - - /* tex->stype in texture.c - marble types */ - protected static final int TEX_SOFT = 0; - protected static final int TEX_SHARP = 1; - protected static final int TEX_SHARPER = 2; - - /* tex->stype in texture.c - wood types */ - protected static final int TEX_BAND = 0; - protected static final int TEX_RING = 1; - protected static final int TEX_BANDNOISE = 2; - protected static final int TEX_RINGNOISE = 3; - - /* tex->stype in texture.c - blend types */ - protected static final int TEX_LIN = 0; - protected static final int TEX_QUAD = 1; - protected static final int TEX_EASE = 2; - protected static final int TEX_DIAG = 3; - protected static final int TEX_SPHERE = 4; - protected static final int TEX_HALO = 5; - protected static final int TEX_RAD = 6; - - /* tex->stype in texture.c - stucci types */ - protected static final int TEX_PLASTIC = 0; - protected static final int TEX_WALLIN = 1; - protected static final int TEX_WALLOUT = 2; - - /* musgrave stype */ - protected static final int TEX_MFRACTAL = 0; - protected static final int TEX_RIDGEDMF = 1; - protected static final int TEX_HYBRIDMF = 2; - protected static final int TEX_FBM = 3; - protected static final int TEX_HTERRAIN = 4; - - /* keyblock->type */ - protected static final int KEY_LINEAR = 0; - protected static final int KEY_CARDINAL = 1; - protected static final int KEY_BSPLINE = 2; - - /* CONSTANTS (read from file) */ - protected static float[] hashpntf; - protected static short[] hash; - protected static float[] hashvectf; - protected static short[] p; - protected static float[][] g; - - /** - * Constructor. Stores the blender version number and loads the constants needed for computations. - * @param blenderVersion - * the number of blender version - */ - public NoiseHelper(String blenderVersion) { - super(blenderVersion); - this.loadConstants(); - } - - /** - * This method loads the constants needed for computations. They are exactly like the ones the blender uses. Each - * deriving class should override this method and load its own constraints. Be carefult with overriding though, if - * an exception will be thrown the class will not be instantiated. - */ - protected void loadConstants() { - InputStream is = NoiseHelper.class.getResourceAsStream("noiseconstants.dat"); - try { - ObjectInputStream ois = new ObjectInputStream(is); - hashpntf = (float[])ois.readObject(); - hash = (short[])ois.readObject(); - hashvectf = (float[])ois.readObject(); - p = (short[])ois.readObject(); - g = (float[][])ois.readObject(); - } catch(IOException e) { - LOGGER.log(Level.SEVERE, e.getLocalizedMessage(), e); - } catch(ClassNotFoundException e) { - assert false : "Constants' classes should be arrays of primitive types, so they are ALWAYS known!"; - } finally { - if(is != null) { - try { - is.close(); - } catch(IOException e) { - LOGGER.log(Level.WARNING, e.getLocalizedMessage()); - } - } - } - } - - protected static Map noiseFunctions = new HashMap(); - static { - // orgBlenderNoise (*Was BLI_hnoise(), removed noisesize, so other functions can call it without scaling.*) - noiseFunctions.put(Integer.valueOf(0), new AbstractNoiseFunc() { - @Override - public float execute(float x, float y, float z) { - return this.orgBlenderNoise(x, y, z); - } - - @Override - public float executeS(float x, float y, float z) { - return 2.0f * this.orgBlenderNoise(x, y, z) - 1.0f; - } - }); - // orgPerlinNoise (*For use with BLI_gNoise/gTurbulence, returns signed noise.*) - noiseFunctions.put(Integer.valueOf(1), new AbstractNoiseFunc() { - @Override - public float execute(float x, float y, float z) { - return 0.5f + 0.5f * this.noise3Perlin(new float[] {x, y, z}); - } - - @Override - public float executeS(float x, float y, float z) { - return this.noise3Perlin(new float[] {x, y, z}); - } - }); - // newPerlin (* for use with BLI_gNoise()/BLI_gTurbulence(), returns unsigned improved perlin noise *) - noiseFunctions.put(Integer.valueOf(2), new AbstractNoiseFunc() { - @Override - public float execute(float x, float y, float z) { - return 0.5f + 0.5f * this.newPerlin(x, y, z); - } - - @Override - public float executeS(float x, float y, float z) { - return this.execute(x, y, z); - } - }); - // voronoi_F1 - noiseFunctions.put(Integer.valueOf(3), new AbstractNoiseFunc() { - @Override - public float execute(float x, float y, float z) { - float[] da = new float[4], pa = new float[12]; - AbstractNoiseFunc.voronoi(x, y, z, da, pa, 1, 0); - return da[0]; - } - - @Override - public float executeS(float x, float y, float z) { - float[] da = new float[4], pa = new float[12]; - AbstractNoiseFunc.voronoi(x, y, z, da, pa, 1, 0); - return 2.0f * da[0] - 1.0f; - } - }); - // voronoi_F2 - noiseFunctions.put(Integer.valueOf(4), new AbstractNoiseFunc() { - @Override - public float execute(float x, float y, float z) { - float[] da = new float[4], pa = new float[12]; - AbstractNoiseFunc.voronoi(x, y, z, da, pa, 1, 0); - return da[1]; - } - - @Override - public float executeS(float x, float y, float z) { - float[] da = new float[4], pa = new float[12]; - AbstractNoiseFunc.voronoi(x, y, z, da, pa, 1, 0); - return 2.0f * da[1] - 1.0f; - } - }); - // voronoi_F3 - noiseFunctions.put(Integer.valueOf(5), new AbstractNoiseFunc() { - @Override - public float execute(float x, float y, float z) { - float[] da = new float[4], pa = new float[12]; - AbstractNoiseFunc.voronoi(x, y, z, da, pa, 1, 0); - return da[2]; - } - - @Override - public float executeS(float x, float y, float z) { - float[] da = new float[4], pa = new float[12]; - AbstractNoiseFunc.voronoi(x, y, z, da, pa, 1, 0); - return 2.0f * da[2] - 1.0f; - } - }); - // voronoi_F4 - noiseFunctions.put(Integer.valueOf(6), new AbstractNoiseFunc() { - @Override - public float execute(float x, float y, float z) { - float[] da = new float[4], pa = new float[12]; - AbstractNoiseFunc.voronoi(x, y, z, da, pa, 1, 0); - return da[3]; - } - - @Override - public float executeS(float x, float y, float z) { - float[] da = new float[4], pa = new float[12]; - AbstractNoiseFunc.voronoi(x, y, z, da, pa, 1, 0); - return 2.0f * da[3] - 1.0f; - } - }); - // voronoi_F1F2 - noiseFunctions.put(Integer.valueOf(7), new AbstractNoiseFunc() { - @Override - public float execute(float x, float y, float z) { - float[] da = new float[4], pa = new float[12]; - AbstractNoiseFunc.voronoi(x, y, z, da, pa, 1, 0); - return da[1] - da[0]; - } - - @Override - public float executeS(float x, float y, float z) { - float[] da = new float[4], pa = new float[12]; - AbstractNoiseFunc.voronoi(x, y, z, da, pa, 1, 0); - return 2.0f * (da[1] - da[0]) - 1.0f; - } - }); - // voronoi_Cr - noiseFunctions.put(Integer.valueOf(8), new AbstractNoiseFunc() { - @Override - public float execute(float x, float y, float z) { - float t = 10 * noiseFunctions.get(Integer.valueOf(7)).execute(x, y, z);// voronoi_F1F2 - return t > 1.0f ? 1.0f : t; - } - - @Override - public float executeS(float x, float y, float z) { - float t = 10.0f * noiseFunctions.get(Integer.valueOf(7)).execute(x, y, z);// voronoi_F1F2 - return t > 1.0f ? 1.0f : 2.0f * t - 1.0f; - } - }); - // cellNoise - noiseFunctions.put(Integer.valueOf(14), new AbstractNoiseFunc() { - @Override - public float execute(float x, float y, float z) { - int xi = (int)Math.floor(x); - int yi = (int)Math.floor(y); - int zi = (int)Math.floor(z); - long n = xi + yi * 1301 + zi * 314159; - n ^= n << 13; - return (n * (n * n * 15731 + 789221) + 1376312589) / 4294967296.0f; - } - - @Override - public float executeS(float x, float y, float z) { - return 2.0f * this.execute(x, y, z) - 1.0f; - } - }); - } - - /** Distance metrics for voronoi. e parameter only used in Minkovsky. */ - protected static Map distanceFunctions = new HashMap(); - static { - // real distance - distanceFunctions.put(Integer.valueOf(0), new IDistanceFunc() { - @Override - public float execute(float x, float y, float z, float e) { - return (float)Math.sqrt(x * x + y * y + z * z); - } - }); - // distance squared - distanceFunctions.put(Integer.valueOf(1), new IDistanceFunc() { - @Override - public float execute(float x, float y, float z, float e) { - return x * x + y * y + z * z; - } - }); - // manhattan/taxicab/cityblock distance - distanceFunctions.put(Integer.valueOf(2), new IDistanceFunc() { - @Override - public float execute(float x, float y, float z, float e) { - return FastMath.abs(x) + FastMath.abs(y) + FastMath.abs(z); - } - }); - // Chebychev - distanceFunctions.put(Integer.valueOf(3), new IDistanceFunc() { - @Override - public float execute(float x, float y, float z, float e) { - x = FastMath.abs(x); - y = FastMath.abs(y); - z = FastMath.abs(z); - float t = x > y ? x : y; - return z > t ? z : t; - } - }); - // minkovsky preset exponent 0.5 (MinkovskyH) - distanceFunctions.put(Integer.valueOf(4), new IDistanceFunc() { - @Override - public float execute(float x, float y, float z, float e) { - float d = (float)(Math.sqrt(FastMath.abs(x)) + Math.sqrt(FastMath.abs(y)) + Math.sqrt(FastMath.abs(z))); - return d * d; - } - }); - // minkovsky preset exponent 4 (Minkovsky4) - distanceFunctions.put(Integer.valueOf(5), new IDistanceFunc() { - @Override - public float execute(float x, float y, float z, float e) { - x *= x; - y *= y; - z *= z; - return (float)Math.sqrt(Math.sqrt(x * x + y * y + z * z)); - } - }); - // Minkovsky, general case, slow, maybe too slow to be useful - distanceFunctions.put(Integer.valueOf(6), new IDistanceFunc() { - @Override - public float execute(float x, float y, float z, float e) { - 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); - } - }); - } - - protected static Map musgraveFunctions = new HashMap(); - static { - musgraveFunctions.put(Integer.valueOf(TEX_MFRACTAL), new IMusgraveFunction() { - @Override - public float execute(Structure tex, float x, float y, float z) { - float mg_H = ((Number)tex.getFieldValue("mg_H")).floatValue(); - float mg_lacunarity = ((Number)tex.getFieldValue("mg_lacunarity")).floatValue(); - float mg_octaves = ((Number)tex.getFieldValue("mg_octaves")).floatValue(); - int noisebasis = ((Number)tex.getFieldValue("noisebasis")).intValue(); - - float rmd, value = 1.0f, pwr = 1.0f, pwHL = (float)Math.pow(mg_lacunarity, -mg_H); - AbstractNoiseFunc abstractNoiseFunc = noiseFunctions.get(Integer.valueOf(noisebasis)); - if(abstractNoiseFunc == null) { - abstractNoiseFunc = noiseFunctions.get(Integer.valueOf(0)); - } - - for(int i = 0; i < (int)mg_octaves; ++i) { - value *= pwr * abstractNoiseFunc.executeS(x, y, z) + 1.0f; - pwr *= pwHL; - x *= mg_lacunarity; - y *= mg_lacunarity; - z *= mg_lacunarity; - } - rmd = (float)(mg_octaves - Math.floor(mg_octaves)); - if(rmd != 0.0f) { - value *= rmd * abstractNoiseFunc.executeS(x, y, z) * pwr + 1.0f; - } - return value; - } - }); - musgraveFunctions.put(Integer.valueOf(TEX_RIDGEDMF), new IMusgraveFunction() { - @Override - public float execute(Structure tex, float x, float y, float z) { - float mg_H = ((Number)tex.getFieldValue("mg_H")).floatValue(); - float mg_lacunarity = ((Number)tex.getFieldValue("mg_lacunarity")).floatValue(); - float mg_octaves = ((Number)tex.getFieldValue("mg_octaves")).floatValue(); - float mg_offset = ((Number)tex.getFieldValue("mg_offset")).floatValue(); - int noisebasis = ((Number)tex.getFieldValue("noisebasis")).intValue(); - float mg_gain = ((Number)tex.getFieldValue("mg_gain")).floatValue(); - float result, signal, weight; - float pwHL = (float)Math.pow(mg_lacunarity, -mg_H); - float pwr = pwHL; /* starts with i=1 instead of 0 */ - - AbstractNoiseFunc abstractNoiseFunc = noiseFunctions.get(Integer.valueOf(noisebasis)); - if(abstractNoiseFunc == null) { - abstractNoiseFunc = noiseFunctions.get(Integer.valueOf(0)); - } - - signal = mg_offset - FastMath.abs(abstractNoiseFunc.executeS(x, y, z)); - signal *= signal; - result = signal; - weight = 1.0f; - - for(int i = 1; i < (int)mg_octaves; ++i) { - x *= mg_lacunarity; - y *= mg_lacunarity; - z *= mg_lacunarity; - weight = signal * mg_gain; - if(weight > 1.0f) { - weight = 1.0f; - } else if(weight < 0.0) { - weight = 0.0f; - } - signal = mg_offset - FastMath.abs(abstractNoiseFunc.executeS(x, y, z)); - signal *= signal; - signal *= weight; - result += signal * pwr; - pwr *= pwHL; - } - return result; - } - }); - musgraveFunctions.put(Integer.valueOf(TEX_HYBRIDMF), new IMusgraveFunction() { - @Override - public float execute(Structure tex, float x, float y, float z) { - float mg_H = ((Number)tex.getFieldValue("mg_H")).floatValue(); - float mg_lacunarity = ((Number)tex.getFieldValue("mg_lacunarity")).floatValue(); - float mg_octaves = ((Number)tex.getFieldValue("mg_octaves")).floatValue(); - float mg_offset = ((Number)tex.getFieldValue("mg_offset")).floatValue(); - int noisebasis = ((Number)tex.getFieldValue("noisebasis")).intValue(); - float mg_gain = ((Number)tex.getFieldValue("mg_gain")).floatValue(); - float result, signal, weight, rmd; - float pwHL = (float)Math.pow(mg_lacunarity, -mg_H); - float pwr = pwHL; /* starts with i=1 instead of 0 */ - AbstractNoiseFunc abstractNoiseFunc = noiseFunctions.get(Integer.valueOf(noisebasis)); - if(abstractNoiseFunc == null) { - abstractNoiseFunc = noiseFunctions.get(Integer.valueOf(0)); - } - - result = abstractNoiseFunc.executeS(x, y, z) + mg_offset; - weight = mg_gain * result; - x *= mg_lacunarity; - y *= mg_lacunarity; - z *= mg_lacunarity; - - for(int i = 1; weight > 0.001f && i < (int)mg_octaves; ++i) { - if(weight > 1.0f) { - weight = 1.0f; - } - signal = (abstractNoiseFunc.executeS(x, y, z) + mg_offset) * pwr; - pwr *= pwHL; - result += weight * signal; - weight *= mg_gain * signal; - x *= mg_lacunarity; - y *= mg_lacunarity; - z *= mg_lacunarity; - } - - rmd = mg_octaves - (float)Math.floor(mg_octaves); - if(rmd != 0.0f) { - result += rmd * (abstractNoiseFunc.executeS(x, y, z) + mg_offset) * pwr; - } - return result; - } - }); - musgraveFunctions.put(Integer.valueOf(TEX_FBM), new IMusgraveFunction() { - @Override - public float execute(Structure tex, float x, float y, float z) { - float mg_H = ((Number)tex.getFieldValue("mg_H")).floatValue(); - float mg_lacunarity = ((Number)tex.getFieldValue("mg_lacunarity")).floatValue(); - float mg_octaves = ((Number)tex.getFieldValue("mg_octaves")).floatValue(); - int noisebasis = ((Number)tex.getFieldValue("noisebasis")).intValue(); - float rmd, value = 0.0f, pwr = 1.0f, pwHL = (float)Math.pow(mg_lacunarity, -mg_H); - - AbstractNoiseFunc abstractNoiseFunc = noiseFunctions.get(Integer.valueOf(noisebasis)); - if(abstractNoiseFunc == null) { - abstractNoiseFunc = noiseFunctions.get(Integer.valueOf(0)); - } - - for(int i = 0; i < (int)mg_octaves; ++i) { - value += abstractNoiseFunc.executeS(x, y, z) * pwr; - pwr *= pwHL; - x *= mg_lacunarity; - y *= mg_lacunarity; - z *= mg_lacunarity; - } - - rmd = (float)(mg_octaves - Math.floor(mg_octaves)); - if(rmd != 0.f) { - value += rmd * abstractNoiseFunc.executeS(x, y, z) * pwr; - } - return value; - } - }); - musgraveFunctions.put(Integer.valueOf(TEX_HTERRAIN), new IMusgraveFunction() { - @Override - public float execute(Structure tex, float x, float y, float z) { - float mg_H = ((Number)tex.getFieldValue("mg_H")).floatValue(); - float mg_lacunarity = ((Number)tex.getFieldValue("mg_lacunarity")).floatValue(); - float mg_octaves = ((Number)tex.getFieldValue("mg_octaves")).floatValue(); - int noisebasis = ((Number)tex.getFieldValue("noisebasis")).intValue(); - float mg_offset = ((Number)tex.getFieldValue("mg_offset")).floatValue(); - float value, increment, rmd; - float pwHL = (float)Math.pow(mg_lacunarity, -mg_H); - float pwr = pwHL; /* starts with i=1 instead of 0 */ - AbstractNoiseFunc abstractNoiseFunc = noiseFunctions.get(Integer.valueOf(noisebasis)); - if(abstractNoiseFunc == null) { - abstractNoiseFunc = noiseFunctions.get(Integer.valueOf(0)); - } - - /* first unscaled octave of function; later octaves are scaled */ - value = mg_offset + abstractNoiseFunc.executeS(x, y, z); - x *= mg_lacunarity; - y *= mg_lacunarity; - z *= mg_lacunarity; - - for(int i = 1; i < (int)mg_octaves; ++i) { - increment = (abstractNoiseFunc.executeS(x, y, z) + mg_offset) * pwr * value; - value += increment; - pwr *= pwHL; - x *= mg_lacunarity; - y *= mg_lacunarity; - z *= mg_lacunarity; - } - - rmd = mg_octaves - (float)Math.floor(mg_octaves); - if(rmd != 0.0) { - increment = (abstractNoiseFunc.executeS(x, y, z) + mg_offset) * pwr * value; - value += rmd * increment; - } - return value; - } - }); - } - - /** - * THE FOLLOWING METHODS HELP IN COMPUTATION OF THE TEXTURES. - */ - protected void brightnesAndContrast(TexResult texres, float contrast, float brightness) { - texres.tin = (texres.tin - 0.5f) * contrast + brightness - 0.5f; - if(texres.tin < 0.0f) { - texres.tin = 0.0f; - } else if(texres.tin > 1.0f) { - texres.tin = 1.0f; - } - } - - protected void brightnesAndContrastRGB(Structure tex, TexResult texres) { - float contrast = ((Number)tex.getFieldValue("contrast")).floatValue(); - float bright = ((Number)tex.getFieldValue("bright")).floatValue(); - float rfac = ((Number)tex.getFieldValue("rfac")).floatValue(); - float gfac = ((Number)tex.getFieldValue("gfac")).floatValue(); - float bfac = ((Number)tex.getFieldValue("bfac")).floatValue(); - - texres.tr = rfac * ((texres.tr - 0.5f) * contrast + bright - 0.5f); - if(texres.tr < 0.0f) { - texres.tr = 0.0f; - } - texres.tg = gfac * ((texres.tg - 0.5f) * contrast + bright - 0.5f); - if(texres.tg < 0.0f) { - texres.tg = 0.0f; - } - texres.tb = bfac * ((texres.tb - 0.5f) * contrast + bright - 0.5f); - if(texres.tb < 0.0f) { - texres.tb = 0.0f; - } - } - - /* this allows colorbanded textures to control normals as well */ - public void texNormalDerivate(ColorBand colorBand, TexResult texres, DataRepository dataRepository) { - if(texres.nor != null) { - TexResult fakeTexresult; - try { - fakeTexresult = (TexResult)texres.clone(); - } catch(CloneNotSupportedException e) { - throw new IllegalStateException("Texture result class MUST support cloning!", e); - } - - float fac0 = fakeTexresult.tr + fakeTexresult.tg + fakeTexresult.tb; - fakeTexresult.tin = texres.nor[0]; - this.doColorband(colorBand, fakeTexresult, dataRepository); - - float fac1 = fakeTexresult.tr + fakeTexresult.tg + fakeTexresult.tb; - fakeTexresult.tin = texres.nor[1]; - this.doColorband(colorBand, fakeTexresult, dataRepository); - - float fac2 = fakeTexresult.tr + fakeTexresult.tg + fakeTexresult.tb; - fakeTexresult.tin = texres.nor[2]; - this.doColorband(colorBand, fakeTexresult, dataRepository); - - float fac3 = fakeTexresult.tr + fakeTexresult.tg + fakeTexresult.tb; - - texres.nor[0] = 0.3333f * (fac0 - fac1); - texres.nor[1] = 0.3333f * (fac0 - fac2); - texres.nor[2] = 0.3333f * (fac0 - fac3); - - texres.nor[0] = texres.tin - texres.nor[0]; - texres.nor[1] = texres.tin - texres.nor[1]; - texres.nor[2] = texres.tin - texres.nor[2]; - } - } - - /** - * This method calculates the colorband for the texture. - * @param colorBand - * the colorband data - * @param texres - * the texture pixel result - * @param dataRepository - * the data repository - * @return true if calculation suceedess and false otherwise - */ - public boolean doColorband(ColorBand colorBand, TexResult texres, DataRepository dataRepository) { - CBData cbd1, cbd2, cbd0, cbd3; - int i1 = 0, i2 = 0, a; - float fac, mfac; - float[] t = new float[4]; - - if(colorBand == null || colorBand.tot == 0) { - return true; - } - - cbd1 = colorBand.data[0]; - if(colorBand.tot == 1) { - texres.tr = cbd1.r; - texres.tg = cbd1.g; - texres.tb = cbd1.b; - texres.ta = cbd1.a; - } else { - if(texres.tin <= cbd1.pos && colorBand.ipotype < 2) { - texres.tr = cbd1.r; - texres.tg = cbd1.g; - texres.tb = cbd1.b; - texres.ta = cbd1.a; - } else { - /* we're looking for first pos > in */ - for(a = 0; a < colorBand.tot; ++a, ++i1) { - cbd1 = colorBand.data[i1]; - if(cbd1.pos > texres.tin) { - break; - } - } - - if(a == colorBand.tot) { - cbd2 = colorBand.data[i1 - 1]; - try { - cbd1 = (CBData)cbd2.clone(); - } catch(CloneNotSupportedException e) { - throw new IllegalStateException("Clone not supported for " + CBData.class.getName() + " class! Fix that!"); - } - cbd1.pos = 1.0f; - } else if(a == 0) { - try { - cbd2 = (CBData)cbd1.clone(); - } catch(CloneNotSupportedException e) { - throw new IllegalStateException("Clone not supported for " + CBData.class.getName() + " class! Fix that!"); - } - cbd2.pos = 0.0f; - } else { - cbd2 = colorBand.data[i1 - 1]; - } - - if(texres.tin >= cbd1.pos && colorBand.ipotype < 2) { - texres.tr = cbd1.r; - texres.tg = cbd1.g; - texres.tb = cbd1.b; - texres.ta = cbd1.a; - } else { - - if(cbd2.pos != cbd1.pos) { - fac = (texres.tin - cbd1.pos) / (cbd2.pos - cbd1.pos); - } else { - fac = 0.0f; - } - - if(colorBand.ipotype == 4) { - /* constant */ - texres.tr = cbd2.r; - texres.tg = cbd2.g; - texres.tb = cbd2.b; - texres.ta = cbd2.a; - return true; - } - - if(colorBand.ipotype >= 2) { - /* ipo from right to left: 3 2 1 0 */ - - if(a >= colorBand.tot - 1) { - cbd0 = cbd1; - } else { - cbd0 = colorBand.data[i1 + 1]; - } - if(a < 2) { - cbd3 = cbd2; - } else { - cbd3 = colorBand.data[i2 - 1]; - } - - fac = FastMath.clamp(fac, 0.0f, 1.0f); - - if(colorBand.ipotype == 3) { - this.setFourIpo(fac, t, KEY_CARDINAL); - } else { - this.setFourIpo(fac, t, KEY_BSPLINE); - } - - texres.tr = t[3] * cbd3.r + t[2] * cbd2.r + t[1] * cbd1.r + t[0] * cbd0.r; - texres.tg = t[3] * cbd3.g + t[2] * cbd2.g + t[1] * cbd1.g + t[0] * cbd0.g; - texres.tb = t[3] * cbd3.b + t[2] * cbd2.b + t[1] * cbd1.b + t[0] * cbd0.b; - texres.ta = t[3] * cbd3.a + t[2] * cbd2.a + t[1] * cbd1.a + t[0] * cbd0.a; - texres.tr = FastMath.clamp(texres.tr, 0.0f, 1.0f); - texres.tg = FastMath.clamp(texres.tg, 0.0f, 1.0f); - texres.tb = FastMath.clamp(texres.tb, 0.0f, 1.0f); - texres.ta = FastMath.clamp(texres.ta, 0.0f, 1.0f); - } else { - - if(colorBand.ipotype == 1) { /* EASE */ - mfac = fac * fac; - fac = 3.0f * mfac - 2.0f * mfac * fac; - } - mfac = 1.0f - fac; - - texres.tr = mfac * cbd1.r + fac * cbd2.r; - texres.tg = mfac * cbd1.g + fac * cbd2.g; - texres.tb = mfac * cbd1.b + fac * cbd2.b; - texres.ta = mfac * cbd1.a + fac * cbd2.a; - } - } - } - } - return true; - } - - protected void setFourIpo(float d, float[] data, int type) { - if(type == KEY_LINEAR) { - data[0] = 0.0f; - data[1] = 1.0f - d; - data[2] = d; - data[3] = 0.0f; - } else { - float d2 = d * d; - float d3 = d2 * d; - if(type == KEY_CARDINAL) { - float fc = 0.71f; - data[0] = -fc * d3 + 2.0f * fc * d2 - fc * d; - data[1] = (2.0f - fc) * d3 + (fc - 3.0f) * d2 + 1.0f; - data[2] = (fc - 2.0f) * d3 + (3.0f - 2.0f * fc) * d2 + fc * d; - data[3] = fc * d3 - fc * d2; - } else if(type == KEY_BSPLINE) { - data[0] = -0.16666666f * d3 + 0.5f * d2 - 0.5f * d + 0.16666666f; - data[1] = 0.5f * d3 - d2 + 0.6666666f; - data[2] = -0.5f * d3 + 0.5f * d2 + 0.5f * d + 0.16666666f; - data[3] = 0.16666666f * d3; - } - } - } - - interface IWaveForm { - float execute(float x); - } - - protected static IWaveForm[] waveformFunctions = new IWaveForm[3]; - static { - waveformFunctions[0] = new IWaveForm() {// tex_sin - @Override - public float execute(float x) { - return 0.5f + 0.5f * (float)Math.sin(x); - } - }; - waveformFunctions[1] = new IWaveForm() {// tex_saw - @Override - public float execute(float x) { - int n = (int)(x / FastMath.TWO_PI); - x -= n * FastMath.TWO_PI; - if(x < 0.0f) { - x += FastMath.TWO_PI; - } - return x / FastMath.TWO_PI; - } - }; - waveformFunctions[2] = new IWaveForm() {// tex_tri - @Override - public float execute(float x) { - return 1.0f - 2.0f * FastMath.abs((float)Math.floor(x * 1.0f / FastMath.TWO_PI + 0.5f) - x * 1.0f / FastMath.TWO_PI); - } - }; - } - - /* computes basic wood intensity value at x,y,z */ - public float woodInt(Structure tex, float x, float y, float z, DataRepository dataRepository) { - int noisebasis2 = ((Number)tex.getFieldValue("noisebasis2")).intValue(); - int noisebasis = ((Number)tex.getFieldValue("noisebasis")).intValue(); - int stype = ((Number)tex.getFieldValue("stype")).intValue(); - float noisesize = ((Number)tex.getFieldValue("noisesize")).floatValue(); - float turbul = ((Number)tex.getFieldValue("turbul")).floatValue(); - int noiseType = ((Number)tex.getFieldValue("noisetype")).intValue(); - float wi = 0; - int waveform = noisebasis2; /* wave form: TEX_SIN=0, TEX_SAW=1, TEX_TRI=2 */ - int wt = stype; /* wood type: TEX_BAND=0, TEX_RING=1, TEX_BANDNOISE=2, TEX_RINGNOISE=3 */ - - if(waveform > TEX_TRI || waveform < TEX_SIN) { - waveform = 0; /* check to be sure noisebasis2 is initialized ahead of time */ - } - - if(wt == TEX_BAND) { - wi = waveformFunctions[waveform].execute((x + y + z) * 10.0f); - } else if(wt == TEX_RING) { - wi = waveformFunctions[waveform].execute((float)Math.sqrt(x * x + y * y + z * z) * 20.0f); - } else if(wt == TEX_BANDNOISE) { - wi = turbul * this.bliGNoise(noisesize, x, y, z, noiseType != TEX_NOISESOFT, noisebasis); - wi = waveformFunctions[waveform].execute((x + y + z) * 10.0f + wi); - } else if(wt == TEX_RINGNOISE) { - wi = turbul * this.bliGNoise(noisesize, x, y, z, noiseType != TEX_NOISESOFT, noisebasis); - wi = waveformFunctions[waveform].execute((float)Math.sqrt(x * x + y * y + z * z) * 20.0f + wi); - } - return wi; - } - - /* computes basic marble intensity at x,y,z */ - public float marbleInt(Structure tex, float x, float y, float z, DataRepository dataRepository) { - float noisesize = ((Number)tex.getFieldValue("noisesize")).floatValue(); - int noisebasis = ((Number)tex.getFieldValue("noisebasis")).intValue(); - int noisedepth = ((Number)tex.getFieldValue("noisedepth")).intValue(); - int stype = ((Number)tex.getFieldValue("stype")).intValue();/* marble type: TEX_SOFT=0, TEX_SHARP=1,TEX_SHAPER=2 */ - float turbul = ((Number)tex.getFieldValue("turbul")).floatValue(); - int noisetype = ((Number)tex.getFieldValue("noisetype")).intValue(); - int waveform = ((Number)tex.getFieldValue("noisebasis2")).intValue(); /* wave form: TEX_SIN=0, TEX_SAW=1, TEX_TRI=2 */ - - if(waveform > TEX_TRI || waveform < TEX_SIN) { - waveform = 0; /* check to be sure noisebasis2 isn't initialized ahead of time */ - } - - float n = 5.0f * (x + y + z); - float mi = n + turbul * this.bliGTurbulence(noisesize, x, y, z, noisedepth, noisetype != TEX_NOISESOFT, noisebasis); - - if(stype >= NoiseHelper.TEX_SOFT) { /* TEX_SOFT always true */ - mi = waveformFunctions[waveform].execute(mi); - if(stype == TEX_SHARP) { - mi = (float)Math.sqrt(mi); - } else if(stype == TEX_SHARPER) { - mi = (float)Math.sqrt(Math.sqrt(mi)); - } - } - return mi; - } - - public void voronoi(float x, float y, float z, float[] da, float[] pa, float me, int dtype) { - AbstractNoiseFunc.voronoi(x, y, z, da, pa, me, dtype); - } - - public void cellNoiseV(float x, float y, float z, float[] ca) { - AbstractNoiseFunc.cellNoiseV(x, y, z, ca); - } - - /** - * THE FOLLOWING METHODS HELP IN NOISE COMPUTATIONS - */ - - /** - * Separated from orgBlenderNoise above, with scaling. - * @param noisesize - * @param x - * @param y - * @param z - * @return - */ - private float bliHnoise(float noisesize, float x, float y, float z) { - if(noisesize == 0.0) { - return 0.0f; - } - x = (1.0f + x) / noisesize; - y = (1.0f + y) / noisesize; - z = (1.0f + z) / noisesize; - return noiseFunctions.get(0).execute(x, y, z); - } - - /** - * @param noisesize - * @param x - * @param y - * @param z - * @param nr - * @return - */ - public float bliTurbulence(float noisesize, float x, float y, float z, int nr) { - float d = 0.5f, div = 1.0f; - - float s = this.bliHnoise(noisesize, x, y, z); - while(nr > 0) { - s += d * this.bliHnoise(noisesize * d, x, y, z); - div += d; - d *= 0.5; - --nr; - } - return s / div; - } - - /** - * @param noisesize - * @param x - * @param y - * @param z - * @param nr - * @return - */ - public float bliTurbulence1(float noisesize, float x, float y, float z, int nr) { - float s, d = 0.5f, div = 1.0f; - - s = FastMath.abs((-1.0f + 2.0f * this.bliHnoise(noisesize, x, y, z))); - while(nr > 0) { - s += Math.abs(d * (-1.0f + 2.0f * this.bliHnoise(noisesize * d, x, y, z))); - div += d; - d *= 0.5; - --nr; - } - return s / div; - } - - /** - * @param noisesize - * @param x - * @param y - * @param z - * @return - */ - public float bliHnoisep(float noisesize, float x, float y, float z) { - return noiseFunctions.get(Integer.valueOf(0)).noise3Perlin(new float[] {x / noisesize, y / noisesize, z / noisesize}); - } - - /** - * @param point - * @param lofreq - * @param hifreq - * @return - */ - public float turbulencePerlin(float[] point, float lofreq, float hifreq) { - float freq, t = 0, p[] = new float[] {point[0] + 123.456f, point[1], point[2]}; - for(freq = lofreq; freq < hifreq; freq *= 2.) { - t += Math.abs(noiseFunctions.get(Integer.valueOf(0)).noise3Perlin(p)) / freq; - p[0] *= 2.0f; - p[1] *= 2.0f; - p[2] *= 2.0f; - } - return t - 0.3f; /* readjust to make mean value = 0.0 */ - } - - /** - * @param noisesize - * @param x - * @param y - * @param z - * @param nr - * @return - */ - public float turbulencep(float noisesize, float x, float y, float z, int nr) { - float[] vec = new float[] {x / noisesize, y / noisesize, z / noisesize}; - ++nr; - return this.turbulencePerlin(vec, 1.0f, (1 << nr)); - } - - /** - * Newnoise: generic noise function for use with different noisebases - * @param x - * @param y - * @param z - * @param oct - * @param isHard - * @param noisebasis - * @return - */ - public float bliGNoise(float noisesize, float x, float y, float z, boolean isHard, int noisebasis) { - AbstractNoiseFunc abstractNoiseFunc = noiseFunctions.get(Integer.valueOf(noisebasis)); - if(abstractNoiseFunc == null) { - abstractNoiseFunc = noiseFunctions.get(0); - noisebasis = 0; - } - if(noisebasis == 0) {// add one to make return value same as BLI_hnoise - x += 1; - y += 1; - z += 1; - } - - if(noisesize != 0.0) { - noisesize = 1.0f / noisesize; - x *= noisesize; - y *= noisesize; - z *= noisesize; - } - if(isHard) { - return Math.abs(2.0f * abstractNoiseFunc.execute(x, y, z) - 1.0f); - } - return abstractNoiseFunc.execute(x, y, z); - } - - /** - * Newnoise: generic turbulence function for use with different noisebasis - * @param x - * @param y - * @param z - * @param oct - * @param isHard - * @param noisebasis - * @return - */ - public float bliGTurbulence(float noisesize, float x, float y, float z, int oct, boolean isHard, int noisebasis) { - AbstractNoiseFunc abstractNoiseFunc = noiseFunctions.get(Integer.valueOf(noisebasis)); - if(abstractNoiseFunc == null) { - abstractNoiseFunc = noiseFunctions.get(0); - noisebasis = 0; - } - if(noisebasis == 0) {// add one to make return value same as BLI_hnoise - x += 1; - y += 1; - z += 1; - } - float sum = 0, t, amp = 1, fscale = 1; - - if(noisesize != 0.0) { - noisesize = 1.0f / noisesize; - x *= noisesize; - y *= noisesize; - z *= noisesize; - } - for(int i = 0; i <= oct; ++i, amp *= 0.5, fscale *= 2) { - t = abstractNoiseFunc.execute(fscale * x, fscale * y, fscale * z); - if(isHard) { - t = FastMath.abs(2.0f * t - 1.0f); - } - sum += t * amp; - } - - sum *= (float)(1 << oct) / (float)((1 << oct + 1) - 1); - return sum; - } - - /** - * "Variable Lacunarity Noise" A distorted variety of Perlin noise. This method is used to calculate distorted noise - * texture. - * @param x - * @param y - * @param z - * @param distortion - * @param nbas1 - * @param nbas2 - * @return - */ - public float mgVLNoise(float x, float y, float z, float distortion, int nbas1, int nbas2) { - AbstractNoiseFunc abstractNoiseFunc1 = noiseFunctions.get(Integer.valueOf(nbas1)); - if(abstractNoiseFunc1 == null) { - abstractNoiseFunc1 = noiseFunctions.get(Integer.valueOf(0)); - } - AbstractNoiseFunc abstractNoiseFunc2 = noiseFunctions.get(Integer.valueOf(nbas2)); - if(abstractNoiseFunc2 == null) { - abstractNoiseFunc2 = noiseFunctions.get(Integer.valueOf(0)); - } - // get a random vector and scale the randomization - float rx = abstractNoiseFunc1.execute(x + 13.5f, y + 13.5f, z + 13.5f) * distortion; - float ry = abstractNoiseFunc1.execute(x, y, z) * distortion; - float rz = abstractNoiseFunc1.execute(x - 13.5f, y - 13.5f, z - 13.5f) * distortion; - return abstractNoiseFunc2.executeS(x + rx, y + ry, z + rz); //distorted-domain noise - } - - public void mgMFractalOrfBmTex(Structure tex, float[] texvec, ColorBand colorBand, TexResult texres, DataRepository dataRepository) { - int stype = ((Number)tex.getFieldValue("stype")).intValue(); - float nsOutscale = ((Number)tex.getFieldValue("ns_outscale")).floatValue(); - float nabla = ((Number)tex.getFieldValue("nabla")).floatValue(); - float noisesize = ((Number)tex.getFieldValue("noisesize")).floatValue(); - float contrast = ((Number)tex.getFieldValue("contrast")).floatValue(); - float brightness = ((Number)tex.getFieldValue("bright")).floatValue(); - - IMusgraveFunction mgravefunc = stype == TEX_MFRACTAL ? musgraveFunctions.get(Integer.valueOf(stype)) : musgraveFunctions.get(Integer.valueOf(TEX_FBM)); - - texres.tin = nsOutscale * mgravefunc.execute(tex, texvec[0], texvec[1], texvec[2]); - if(texres.nor != null) { - float offs = nabla / noisesize; // also scaling of texvec - // calculate bumpnormal - texres.nor[0] = nsOutscale * mgravefunc.execute(tex, texvec[0] + offs, texvec[1], texvec[2]); - texres.nor[1] = nsOutscale * mgravefunc.execute(tex, texvec[0], texvec[1] + offs, texvec[2]); - texres.nor[2] = nsOutscale * mgravefunc.execute(tex, texvec[0], texvec[1], texvec[2] + offs); - this.texNormalDerivate(colorBand, texres, dataRepository); - } - this.brightnesAndContrast(texres, contrast, brightness); - } - - public void mgRidgedOrHybridMFTex(Structure tex, float[] texvec, ColorBand colorBand, TexResult texres, DataRepository dataRepository) { - int stype = ((Number)tex.getFieldValue("stype")).intValue(); - float nsOutscale = ((Number)tex.getFieldValue("ns_outscale")).floatValue(); - float nabla = ((Number)tex.getFieldValue("nabla")).floatValue(); - float noisesize = ((Number)tex.getFieldValue("noisesize")).floatValue(); - float contrast = ((Number)tex.getFieldValue("contrast")).floatValue(); - float brightness = ((Number)tex.getFieldValue("bright")).floatValue(); - - IMusgraveFunction mgravefunc = stype == TEX_RIDGEDMF ? musgraveFunctions.get(Integer.valueOf(stype)) : musgraveFunctions.get(Integer.valueOf(TEX_HYBRIDMF)); - - texres.tin = nsOutscale * mgravefunc.execute(tex, texvec[0], texvec[1], texvec[2]); - if(texres.nor != null) { - float offs = nabla / noisesize; // also scaling of texvec - // calculate bumpnormal - texres.nor[0] = nsOutscale * mgravefunc.execute(tex, texvec[0] + offs, texvec[1], texvec[2]); - texres.nor[1] = nsOutscale * mgravefunc.execute(tex, texvec[0], texvec[1] + offs, texvec[2]); - texres.nor[2] = nsOutscale * mgravefunc.execute(tex, texvec[0], texvec[1], texvec[2] + offs); - this.texNormalDerivate(colorBand, texres, dataRepository); - } - this.brightnesAndContrast(texres, contrast, brightness); - } - - public void mgHTerrainTex(Structure tex, float[] texvec, ColorBand colorBand, TexResult texres, DataRepository dataRepository) { - float nsOutscale = ((Number)tex.getFieldValue("ns_outscale")).floatValue(); - float nabla = ((Number)tex.getFieldValue("nabla")).floatValue(); - float noisesize = ((Number)tex.getFieldValue("noisesize")).floatValue(); - float contrast = ((Number)tex.getFieldValue("contrast")).floatValue(); - float brightness = ((Number)tex.getFieldValue("bright")).floatValue(); - - IMusgraveFunction musgraveFunction = musgraveFunctions.get(Integer.valueOf(TEX_HTERRAIN)); - texres.tin = nsOutscale * musgraveFunction.execute(tex, texvec[0], texvec[1], texvec[2]); - if(texres.nor != null) { - float offs = nabla / noisesize; // also scaling of texvec - // calculate bumpnormal - texres.nor[0] = nsOutscale * musgraveFunction.execute(tex, texvec[0] + offs, texvec[1], texvec[2]); - texres.nor[1] = nsOutscale * musgraveFunction.execute(tex, texvec[0], texvec[1] + offs, texvec[2]); - texres.nor[2] = nsOutscale * musgraveFunction.execute(tex, texvec[0], texvec[1], texvec[2] + offs); - this.texNormalDerivate(colorBand, texres, dataRepository); - } - this.brightnesAndContrast(texres, contrast, brightness); - } - - /** - * This class is abstract to the noise functions computations. It has two methods. One calculates the Signed (with - * 'S' at the end) and the other Unsigned value. - * @author Marcin Roguski (Kaelthas) - */ - protected static abstract class AbstractNoiseFunc { - /** - * This method calculates the unsigned value of the noise. - * @param x - * the x texture coordinate - * @param y - * the y texture coordinate - * @param z - * the z texture coordinate - * @return value of the noise - */ - public abstract float execute(float x, float y, float z); - - /** - * This method calculates the signed value of the noise. - * @param x - * the x texture coordinate - * @param y - * the y texture coordinate - * @param z - * the z texture coordinate - * @return value of the noise - */ - public abstract float executeS(float x, float y, float z); - - /* - * Not 'pure' Worley, but the results are virtually the same. Returns distances in da and point coords in pa - */ - protected static void voronoi(float x, float y, float z, float[] da, float[] pa, float me, int dtype) { - float xd, yd, zd, d, p[]; - - IDistanceFunc distanceFunc = distanceFunctions.get(Integer.valueOf(dtype)); - if(distanceFunc == null) { - distanceFunc = distanceFunctions.get(Integer.valueOf(0)); - } - - int xi = (int)FastMath.floor(x); - int yi = (int)FastMath.floor(y); - int zi = (int)FastMath.floor(z); - da[0] = da[1] = da[2] = da[3] = 1e10f; - for(int xx = xi - 1; xx <= xi + 1; ++xx) { - for(int yy = yi - 1; yy <= yi + 1; ++yy) { - for(int zz = zi - 1; zz <= zi + 1; ++zz) { - p = AbstractNoiseFunc.hashPoint(xx, yy, zz); - xd = x - (p[0] + xx); - yd = y - (p[1] + yy); - zd = z - (p[2] + zz); - d = distanceFunc.execute(xd, yd, zd, me); - if(d < da[0]) { - da[3] = da[2]; - da[2] = da[1]; - da[1] = da[0]; - da[0] = d; - pa[9] = pa[6]; - pa[10] = pa[7]; - pa[11] = pa[8]; - pa[6] = pa[3]; - pa[7] = pa[4]; - pa[8] = pa[5]; - pa[3] = pa[0]; - pa[4] = pa[1]; - pa[5] = pa[2]; - pa[0] = p[0] + xx; - pa[1] = p[1] + yy; - pa[2] = p[2] + zz; - } else if(d < da[1]) { - da[3] = da[2]; - da[2] = da[1]; - da[1] = d; - pa[9] = pa[6]; - pa[10] = pa[7]; - pa[11] = pa[8]; - pa[6] = pa[3]; - pa[7] = pa[4]; - pa[8] = pa[5]; - pa[3] = p[0] + xx; - pa[4] = p[1] + yy; - pa[5] = p[2] + zz; - } else if(d < da[2]) { - da[3] = da[2]; - da[2] = d; - pa[9] = pa[6]; - pa[10] = pa[7]; - pa[11] = pa[8]; - pa[6] = p[0] + xx; - pa[7] = p[1] + yy; - pa[8] = p[2] + zz; - } else if(d < da[3]) { - da[3] = d; - pa[9] = p[0] + xx; - pa[10] = p[1] + yy; - pa[11] = p[2] + zz; - } - } - } - } - } - - // #define HASHVEC(x,y,z) hashvectf+3*hash[ (hash[ (hash[(z) & 255]+(y)) & 255]+(x)) & 255] - - /* needed for voronoi */ - // #define HASHPNT(x,y,z) hashpntf+3*hash[ (hash[ (hash[(z) & 255]+(y)) & 255]+(x)) & 255] - protected static float[] hashPoint(int x, int y, int z) { - float[] result = new float[3]; - result[0] = hashpntf[3 * hash[hash[hash[z & 255] + y & 255] + x & 255]]; - result[1] = hashpntf[3 * hash[hash[hash[z & 255] + y & 255] + x & 255] + 1]; - result[2] = hashpntf[3 * hash[hash[hash[z & 255] + y & 255] + x & 255] + 2]; - return result; - } - - // #define setup(i,b0,b1,r0,r1) \ - // t = vec[i] + 10000.; \ - // b0 = ((int)t) & 255; \ - // b1 = (b0+1) & 255; \ - // r0 = t - (int)t; \ - // r1 = r0 - 1.; - - // vec[3] - public float noise3Perlin(float[] vec) { - int bx0, bx1, by0, by1, bz0, bz1, b00, b10, b01, b11; - float rx0, rx1, ry0, ry1, rz0, rz1, sx, sy, sz, a, b, c, d, t, u, v; - int i, j; - - // setup(0, bx0,bx1, rx0,rx1); - t = vec[0] + 10000.0f; - bx0 = (int)t & 255; - bx1 = bx0 + 1 & 255; - rx0 = t - (int)t; - rx1 = rx0 - 1.0f; - // setup(1, by0,by1, ry0,ry1); - t = vec[0] + 10000.0f; - by0 = (int)t & 255; - by1 = by0 + 1 & 255; - ry0 = t - (int)t; - ry1 = ry0 - 1.0f; - // setup(2, bz0,bz1, rz0,rz1); - t = vec[0] + 10000.0f; - bz0 = (int)t & 255; - bz1 = bz0 + 1 & 255; - rz0 = t - (int)t; - rz1 = rz0 - 1.0f; - - i = p[bx0]; - j = p[bx1]; - - b00 = p[i + by0]; - b10 = p[j + by0]; - b01 = p[i + by1]; - b11 = p[j + by1]; - - /* lerp moved to improved perlin above */ - - sx = this.surve(rx0); - sy = this.surve(ry0); - sz = this.surve(rz0); - - float[] q = new float[3]; - q = g[b00 + bz0]; - u = this.at(rx0, ry0, rz0, q); - q = g[b10 + bz0]; - v = this.at(rx1, ry0, rz0, q); - a = this.lerp(sx, u, v); - - q = g[b01 + bz0]; - u = this.at(rx0, ry1, rz0, q); - q = g[b11 + bz0]; - v = this.at(rx1, ry1, rz0, q); - b = this.lerp(sx, u, v); - - c = this.lerp(sy, a, b); /* interpolate in y at lo x */ - - q = g[b00 + bz1]; - u = this.at(rx0, ry0, rz1, q); - q = g[b10 + bz1]; - v = this.at(rx1, ry0, rz1, q); - a = this.lerp(sx, u, v); - - q = g[b01 + bz1]; - u = this.at(rx0, ry1, rz1, q); - q = g[b11 + bz1]; - v = this.at(rx1, ry1, rz1, q); - b = this.lerp(sx, u, v); - - d = this.lerp(sy, a, b); /* interpolate in y at hi x */ - - return 1.5f * this.lerp(sz, c, d); /* interpolate in z */ - } - - public float orgBlenderNoise(float x, float y, float z) { - float cn1, cn2, cn3, cn4, cn5, cn6, i; - float ox, oy, oz, jx, jy, jz; - float n = 0.5f; - int ix, iy, iz, b00, b01, b10, b11, b20, b21; - - ox = x - (ix = (int)Math.floor(x)); - oy = y - (iy = (int)Math.floor(y)); - oz = z - (iz = (int)Math.floor(z)); - - jx = ox - 1; - jy = oy - 1; - jz = oz - 1; - - cn1 = ox * ox; - cn2 = oy * oy; - cn3 = oz * oz; - cn4 = jx * jx; - cn5 = jy * jy; - cn6 = jz * jz; - - cn1 = 1.0f - 3.0f * cn1 + 2.0f * cn1 * ox; - cn2 = 1.0f - 3.0f * cn2 + 2.0f * cn2 * oy; - cn3 = 1.0f - 3.0f * cn3 + 2.0f * cn3 * oz; - cn4 = 1.0f - 3.0f * cn4 - 2.0f * cn4 * jx; - cn5 = 1.0f - 3.0f * cn5 - 2.0f * cn5 * jy; - cn6 = 1.0f - 3.0f * cn6 - 2.0f * cn6 * jz; - - b00 = hash[hash[ix & 255] + (iy & 255)]; - b10 = hash[hash[ix + 1 & 255] + (iy & 255)]; - b01 = hash[hash[ix & 255] + (iy + 1 & 255)]; - b11 = hash[hash[ix + 1 & 255] + (iy + 1 & 255)]; - - b20 = iz & 255; - b21 = iz + 1 & 255; - - /* 0 */ - i = cn1 * cn2 * cn3; - int hIndex = 3 * hash[b20 + b00]; - n += i * (hashvectf[hIndex] * ox + hashvectf[hIndex + 1] * oy + hashvectf[hIndex + 2] * oz); - /* 1 */ - i = cn1 * cn2 * cn6; - hIndex = 3 * hash[b21 + b00]; - n += i * (hashvectf[hIndex] * ox + hashvectf[hIndex + 1] * oy + hashvectf[hIndex + 2] * jz); - /* 2 */ - i = cn1 * cn5 * cn3; - hIndex = 3 * hash[b20 + b01]; - n += i * (hashvectf[hIndex] * ox + hashvectf[hIndex + 1] * jy + hashvectf[hIndex + 2] * oz); - /* 3 */ - i = cn1 * cn5 * cn6; - hIndex = 3 * hash[b21 + b01]; - n += i * (hashvectf[hIndex] * ox + hashvectf[hIndex + 1] * jy + hashvectf[hIndex + 2] * jz); - /* 4 */ - i = cn4 * cn2 * cn3; - hIndex = 3 * hash[b20 + b10]; - n += i * (hashvectf[hIndex] * jx + hashvectf[hIndex + 1] * oy + hashvectf[hIndex + 2] * oz); - /* 5 */ - i = cn4 * cn2 * cn6; - hIndex = 3 * hash[b21 + b10]; - n += i * (hashvectf[hIndex] * jx + hashvectf[hIndex + 1] * oy + hashvectf[hIndex + 2] * jz); - /* 6 */ - i = cn4 * cn5 * cn3; - hIndex = 3 * hash[b20 + b11]; - n += i * (hashvectf[hIndex] * jx + hashvectf[hIndex + 1] * jy + hashvectf[hIndex + 2] * oz); - /* 7 */ - i = cn4 * cn5 * cn6; - hIndex = 3 * hash[b21 + b11]; - n += i * (hashvectf[hIndex] * jx + hashvectf[hIndex + 1] * jy + hashvectf[hIndex + 2] * jz); - - if(n < 0.0f) { - n = 0.0f; - } else if(n > 1.0f) { - n = 1.0f; - } - return n; - } - - /* instead of adding another permutation array, just use hash table defined above */ - public float newPerlin(float x, float y, float z) { - int A, AA, AB, B, BA, BB; - float u = (float)Math.floor(x), v = (float)Math.floor(y), w = (float)Math.floor(z); - int X = (int)u & 255, Y = (int)v & 255, Z = (int)w & 255; // FIND UNIT CUBE THAT CONTAINS POINT - x -= u; // FIND RELATIVE X,Y,Z - y -= v; // OF POINT IN CUBE. - z -= w; - u = this.npfade(x); // COMPUTE FADE CURVES - v = this.npfade(y); // FOR EACH OF X,Y,Z. - w = this.npfade(z); - A = hash[X] + Y; - AA = hash[A] + Z; - AB = hash[A + 1] + Z; // HASH COORDINATES OF - B = hash[X + 1] + Y; - BA = hash[B] + Z; - BB = hash[B + 1] + Z; // THE 8 CUBE CORNERS, - return this.lerp(w, this.lerp(v, this.lerp(u, this.grad(hash[AA], x, y, z), // AND ADD - this.grad(hash[BA], x - 1, y, z)), // BLENDED - this.lerp(u, this.grad(hash[AB], x, y - 1, z), // RESULTS - this.grad(hash[BB], x - 1, y - 1, z))),// FROM 8 - this.lerp(v, this.lerp(u, this.grad(hash[AA + 1], x, y, z - 1), // CORNERS - this.grad(hash[BA + 1], x - 1, y, z - 1)), // OF CUBE - this.lerp(u, this.grad(hash[AB + 1], x, y - 1, z - 1), this.grad(hash[BB + 1], x - 1, y - 1, z - 1)))); - } - - /** - * Returns a vector/point/color in ca, using point hasharray directly - */ - protected static void cellNoiseV(float x, float y, float z, float[] ca) { - int xi = (int)Math.floor(x); - int yi = (int)Math.floor(y); - int zi = (int)Math.floor(z); - float[] p = AbstractNoiseFunc.hashPoint(xi, yi, zi); - ca[0] = p[0]; - ca[1] = p[1]; - ca[2] = p[2]; - } - - protected float lerp(float t, float a, float b) { - return a + t * (b - a); - } - - protected float npfade(float t) { - return t * t * t * (t * (t * 6.0f - 15.0f) + 10.0f); - } - - protected float grad(int hash, float x, float y, float z) { - int h = hash & 0x0F; // CONVERT LO 4 BITS OF HASH CODE - float u = h < 8 ? x : y, // INTO 12 GRADIENT DIRECTIONS. - v = h < 4 ? y : h == 12 || h == 14 ? x : z; - return ((h & 1) == 0 ? u : -u) + ((h & 2) == 0 ? v : -v); - } - - /** - * Dot product of two vectors. - * @param a - * the first vector - * @param b - * the second vector - * @return the dot product of two vectors - */ - protected float dot(float[] a, float[] b) { - return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; - } - - protected float surve(float t) { - return t * t * (3.0f - 2.0f * t); - } - - protected float at(float rx, float ry, float rz, float[] q) { - return rx * q[0] + ry * q[1] + rz * q[2]; - } - } - - /** - * This interface is used for distance calculation classes. Distance metrics for voronoi. e parameter only used in - * Minkovsky. - */ - interface IDistanceFunc { - /** - * This method calculates the distance for voronoi algorithms. - * @param x - * the x coordinate - * @param y - * the y coordinate - * @param z - * the z coordinate - * @param e - * this parameter used in Monkovsky (no idea what it really is ;) - * @return - */ - float execute(float x, float y, float z, float e); - } - - interface IMusgraveFunction { - float execute(Structure tex, float x, float y, float z); - } + + private static final Logger LOGGER = Logger.getLogger(NoiseHelper.class.getName()); + + /* return value */ + protected static final int TEX_INT = 0; + protected static final int TEX_RGB = 1; + protected static final int TEX_NOR = 2; + + /* noisetype */ + protected static final int TEX_NOISESOFT = 0; + protected static final int TEX_NOISEPERL = 1; + + /* tex->stype in texture.c - cloud types */ + protected static final int TEX_DEFAULT = 0; + protected static final int TEX_COLOR = 1; + + /* flag */ + protected static final int TEX_COLORBAND = 1; + protected static final int TEX_FLIPBLEND = 2; + protected static final int TEX_NEGALPHA = 4; + protected static final int TEX_CHECKER_ODD = 8; + protected static final int TEX_CHECKER_EVEN = 16; + protected static final int TEX_PRV_ALPHA = 32; + protected static final int TEX_PRV_NOR = 64; + protected static final int TEX_REPEAT_XMIR = 128; + protected static final int TEX_REPEAT_YMIR = 256; + 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; + + /* tex->noisebasis2 in texture.c - wood waveforms */ + protected static final int TEX_SIN = 0; + protected static final int TEX_SAW = 1; + protected static final int TEX_TRI = 2; + + /* tex->stype in texture.c - marble types */ + protected static final int TEX_SOFT = 0; + protected static final int TEX_SHARP = 1; + protected static final int TEX_SHARPER = 2; + + /* tex->stype in texture.c - wood types */ + protected static final int TEX_BAND = 0; + protected static final int TEX_RING = 1; + protected static final int TEX_BANDNOISE = 2; + protected static final int TEX_RINGNOISE = 3; + + /* tex->stype in texture.c - blend types */ + protected static final int TEX_LIN = 0; + protected static final int TEX_QUAD = 1; + protected static final int TEX_EASE = 2; + protected static final int TEX_DIAG = 3; + protected static final int TEX_SPHERE = 4; + protected static final int TEX_HALO = 5; + protected static final int TEX_RAD = 6; + + /* tex->stype in texture.c - stucci types */ + protected static final int TEX_PLASTIC = 0; + protected static final int TEX_WALLIN = 1; + protected static final int TEX_WALLOUT = 2; + + /* musgrave stype */ + protected static final int TEX_MFRACTAL = 0; + protected static final int TEX_RIDGEDMF = 1; + protected static final int TEX_HYBRIDMF = 2; + protected static final int TEX_FBM = 3; + protected static final int TEX_HTERRAIN = 4; + + /* keyblock->type */ + protected static final int KEY_LINEAR = 0; + protected static final int KEY_CARDINAL = 1; + protected static final int KEY_BSPLINE = 2; + + /* CONSTANTS (read from file) */ + protected static float[] hashpntf; + protected static short[] hash; + protected static float[] hashvectf; + protected static short[] p; + protected static float[][] g; + + /** + * Constructor. Stores the blender version number and loads the constants needed for computations. + * @param blenderVersion + * the number of blender version + */ + public NoiseHelper(String blenderVersion) { + super(blenderVersion); + this.loadConstants(); + } + + /** + * This method loads the constants needed for computations. They are exactly like the ones the blender uses. Each + * deriving class should override this method and load its own constraints. Be carefult with overriding though, if + * an exception will be thrown the class will not be instantiated. + */ + protected void loadConstants() { + InputStream is = NoiseHelper.class.getResourceAsStream("noiseconstants.dat"); + try { + ObjectInputStream ois = new ObjectInputStream(is); + hashpntf = (float[]) ois.readObject(); + hash = (short[]) ois.readObject(); + hashvectf = (float[]) ois.readObject(); + p = (short[]) ois.readObject(); + g = (float[][]) ois.readObject(); + } catch (IOException e) { + LOGGER.log(Level.SEVERE, e.getLocalizedMessage(), e); + } catch (ClassNotFoundException e) { + assert false : "Constants' classes should be arrays of primitive types, so they are ALWAYS known!"; + } finally { + if (is != null) { + try { + is.close(); + } catch (IOException e) { + LOGGER.log(Level.WARNING, e.getLocalizedMessage()); + } + } + } + } + protected static Map noiseFunctions = new HashMap(); + + static { + // orgBlenderNoise (*Was BLI_hnoise(), removed noisesize, so other functions can call it without scaling.*) + noiseFunctions.put(Integer.valueOf(0), new AbstractNoiseFunc() { + + @Override + public float execute(float x, float y, float z) { + return this.orgBlenderNoise(x, y, z); + } + + @Override + public float executeS(float x, float y, float z) { + return 2.0f * this.orgBlenderNoise(x, y, z) - 1.0f; + } + }); + // orgPerlinNoise (*For use with BLI_gNoise/gTurbulence, returns signed noise.*) + noiseFunctions.put(Integer.valueOf(1), new AbstractNoiseFunc() { + + @Override + public float execute(float x, float y, float z) { + return 0.5f + 0.5f * this.noise3Perlin(new float[]{x, y, z}); + } + + @Override + public float executeS(float x, float y, float z) { + return this.noise3Perlin(new float[]{x, y, z}); + } + }); + // newPerlin (* for use with BLI_gNoise()/BLI_gTurbulence(), returns unsigned improved perlin noise *) + noiseFunctions.put(Integer.valueOf(2), new AbstractNoiseFunc() { + + @Override + public float execute(float x, float y, float z) { + return 0.5f + 0.5f * this.newPerlin(x, y, z); + } + + @Override + public float executeS(float x, float y, float z) { + return this.execute(x, y, z); + } + }); + // voronoi_F1 + noiseFunctions.put(Integer.valueOf(3), new AbstractNoiseFunc() { + + @Override + public float execute(float x, float y, float z) { + float[] da = new float[4], pa = new float[12]; + AbstractNoiseFunc.voronoi(x, y, z, da, pa, 1, 0); + return da[0]; + } + + @Override + public float executeS(float x, float y, float z) { + float[] da = new float[4], pa = new float[12]; + AbstractNoiseFunc.voronoi(x, y, z, da, pa, 1, 0); + return 2.0f * da[0] - 1.0f; + } + }); + // voronoi_F2 + noiseFunctions.put(Integer.valueOf(4), new AbstractNoiseFunc() { + + @Override + public float execute(float x, float y, float z) { + float[] da = new float[4], pa = new float[12]; + AbstractNoiseFunc.voronoi(x, y, z, da, pa, 1, 0); + return da[1]; + } + + @Override + public float executeS(float x, float y, float z) { + float[] da = new float[4], pa = new float[12]; + AbstractNoiseFunc.voronoi(x, y, z, da, pa, 1, 0); + return 2.0f * da[1] - 1.0f; + } + }); + // voronoi_F3 + noiseFunctions.put(Integer.valueOf(5), new AbstractNoiseFunc() { + + @Override + public float execute(float x, float y, float z) { + float[] da = new float[4], pa = new float[12]; + AbstractNoiseFunc.voronoi(x, y, z, da, pa, 1, 0); + return da[2]; + } + + @Override + public float executeS(float x, float y, float z) { + float[] da = new float[4], pa = new float[12]; + AbstractNoiseFunc.voronoi(x, y, z, da, pa, 1, 0); + return 2.0f * da[2] - 1.0f; + } + }); + // voronoi_F4 + noiseFunctions.put(Integer.valueOf(6), new AbstractNoiseFunc() { + + @Override + public float execute(float x, float y, float z) { + float[] da = new float[4], pa = new float[12]; + AbstractNoiseFunc.voronoi(x, y, z, da, pa, 1, 0); + return da[3]; + } + + @Override + public float executeS(float x, float y, float z) { + float[] da = new float[4], pa = new float[12]; + AbstractNoiseFunc.voronoi(x, y, z, da, pa, 1, 0); + return 2.0f * da[3] - 1.0f; + } + }); + // voronoi_F1F2 + noiseFunctions.put(Integer.valueOf(7), new AbstractNoiseFunc() { + + @Override + public float execute(float x, float y, float z) { + float[] da = new float[4], pa = new float[12]; + AbstractNoiseFunc.voronoi(x, y, z, da, pa, 1, 0); + return da[1] - da[0]; + } + + @Override + public float executeS(float x, float y, float z) { + float[] da = new float[4], pa = new float[12]; + AbstractNoiseFunc.voronoi(x, y, z, da, pa, 1, 0); + return 2.0f * (da[1] - da[0]) - 1.0f; + } + }); + // voronoi_Cr + noiseFunctions.put(Integer.valueOf(8), new AbstractNoiseFunc() { + + @Override + public float execute(float x, float y, float z) { + float t = 10 * noiseFunctions.get(Integer.valueOf(7)).execute(x, y, z);// voronoi_F1F2 + return t > 1.0f ? 1.0f : t; + } + + @Override + public float executeS(float x, float y, float z) { + float t = 10.0f * noiseFunctions.get(Integer.valueOf(7)).execute(x, y, z);// voronoi_F1F2 + return t > 1.0f ? 1.0f : 2.0f * t - 1.0f; + } + }); + // cellNoise + noiseFunctions.put(Integer.valueOf(14), new AbstractNoiseFunc() { + + @Override + public float execute(float x, float y, float z) { + int xi = (int) Math.floor(x); + int yi = (int) Math.floor(y); + int zi = (int) Math.floor(z); + long n = xi + yi * 1301 + zi * 314159; + n ^= n << 13; + return (n * (n * n * 15731 + 789221) + 1376312589) / 4294967296.0f; + } + + @Override + public float executeS(float x, float y, float z) { + return 2.0f * this.execute(x, y, z) - 1.0f; + } + }); + } + /** Distance metrics for voronoi. e parameter only used in Minkovsky. */ + protected static Map distanceFunctions = new HashMap(); + + static { + // real distance + distanceFunctions.put(Integer.valueOf(0), new DistanceFunc() { + + @Override + public float execute(float x, float y, float z, float e) { + return (float) Math.sqrt(x * x + y * y + z * z); + } + }); + // distance squared + distanceFunctions.put(Integer.valueOf(1), new DistanceFunc() { + + @Override + public float execute(float x, float y, float z, float e) { + return x * x + y * y + z * z; + } + }); + // manhattan/taxicab/cityblock distance + distanceFunctions.put(Integer.valueOf(2), new DistanceFunc() { + + @Override + public float execute(float x, float y, float z, float e) { + return FastMath.abs(x) + FastMath.abs(y) + FastMath.abs(z); + } + }); + // Chebychev + distanceFunctions.put(Integer.valueOf(3), new DistanceFunc() { + + @Override + public float execute(float x, float y, float z, float e) { + x = FastMath.abs(x); + y = FastMath.abs(y); + z = FastMath.abs(z); + float t = x > y ? x : y; + return z > t ? z : t; + } + }); + // minkovsky preset exponent 0.5 (MinkovskyH) + distanceFunctions.put(Integer.valueOf(4), new DistanceFunc() { + + @Override + public float execute(float x, float y, float z, float e) { + float d = (float) (Math.sqrt(FastMath.abs(x)) + Math.sqrt(FastMath.abs(y)) + Math.sqrt(FastMath.abs(z))); + return d * d; + } + }); + // minkovsky preset exponent 4 (Minkovsky4) + distanceFunctions.put(Integer.valueOf(5), new DistanceFunc() { + + @Override + public float execute(float x, float y, float z, float e) { + x *= x; + y *= y; + z *= z; + return (float) Math.sqrt(Math.sqrt(x * x + y * y + z * z)); + } + }); + // Minkovsky, general case, slow, maybe too slow to be useful + distanceFunctions.put(Integer.valueOf(6), new DistanceFunc() { + + @Override + public float execute(float x, float y, float z, float e) { + 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); + } + }); + } + protected static Map musgraveFunctions = new HashMap(); + + static { + musgraveFunctions.put(Integer.valueOf(TEX_MFRACTAL), new MusgraveFunction() { + + @Override + public float execute(Structure tex, float x, float y, float z) { + float mg_H = ((Number) tex.getFieldValue("mg_H")).floatValue(); + float mg_lacunarity = ((Number) tex.getFieldValue("mg_lacunarity")).floatValue(); + float mg_octaves = ((Number) tex.getFieldValue("mg_octaves")).floatValue(); + int noisebasis = ((Number) tex.getFieldValue("noisebasis")).intValue(); + + float rmd, value = 1.0f, pwr = 1.0f, pwHL = (float) Math.pow(mg_lacunarity, -mg_H); + AbstractNoiseFunc abstractNoiseFunc = noiseFunctions.get(Integer.valueOf(noisebasis)); + if (abstractNoiseFunc == null) { + abstractNoiseFunc = noiseFunctions.get(Integer.valueOf(0)); + } + + for (int i = 0; i < (int) mg_octaves; ++i) { + value *= pwr * abstractNoiseFunc.executeS(x, y, z) + 1.0f; + pwr *= pwHL; + x *= mg_lacunarity; + y *= mg_lacunarity; + z *= mg_lacunarity; + } + rmd = (float) (mg_octaves - Math.floor(mg_octaves)); + if (rmd != 0.0f) { + value *= rmd * abstractNoiseFunc.executeS(x, y, z) * pwr + 1.0f; + } + return value; + } + }); + musgraveFunctions.put(Integer.valueOf(TEX_RIDGEDMF), new MusgraveFunction() { + + @Override + public float execute(Structure tex, float x, float y, float z) { + float mg_H = ((Number) tex.getFieldValue("mg_H")).floatValue(); + float mg_lacunarity = ((Number) tex.getFieldValue("mg_lacunarity")).floatValue(); + float mg_octaves = ((Number) tex.getFieldValue("mg_octaves")).floatValue(); + float mg_offset = ((Number) tex.getFieldValue("mg_offset")).floatValue(); + int noisebasis = ((Number) tex.getFieldValue("noisebasis")).intValue(); + float mg_gain = ((Number) tex.getFieldValue("mg_gain")).floatValue(); + float result, signal, weight; + float pwHL = (float) Math.pow(mg_lacunarity, -mg_H); + float pwr = pwHL; /* starts with i=1 instead of 0 */ + + AbstractNoiseFunc abstractNoiseFunc = noiseFunctions.get(Integer.valueOf(noisebasis)); + if (abstractNoiseFunc == null) { + abstractNoiseFunc = noiseFunctions.get(Integer.valueOf(0)); + } + + signal = mg_offset - FastMath.abs(abstractNoiseFunc.executeS(x, y, z)); + signal *= signal; + result = signal; + weight = 1.0f; + + for (int i = 1; i < (int) mg_octaves; ++i) { + x *= mg_lacunarity; + y *= mg_lacunarity; + z *= mg_lacunarity; + weight = signal * mg_gain; + if (weight > 1.0f) { + weight = 1.0f; + } else if (weight < 0.0) { + weight = 0.0f; + } + signal = mg_offset - FastMath.abs(abstractNoiseFunc.executeS(x, y, z)); + signal *= signal; + signal *= weight; + result += signal * pwr; + pwr *= pwHL; + } + return result; + } + }); + musgraveFunctions.put(Integer.valueOf(TEX_HYBRIDMF), new MusgraveFunction() { + + @Override + public float execute(Structure tex, float x, float y, float z) { + float mg_H = ((Number) tex.getFieldValue("mg_H")).floatValue(); + float mg_lacunarity = ((Number) tex.getFieldValue("mg_lacunarity")).floatValue(); + float mg_octaves = ((Number) tex.getFieldValue("mg_octaves")).floatValue(); + float mg_offset = ((Number) tex.getFieldValue("mg_offset")).floatValue(); + int noisebasis = ((Number) tex.getFieldValue("noisebasis")).intValue(); + float mg_gain = ((Number) tex.getFieldValue("mg_gain")).floatValue(); + float result, signal, weight, rmd; + float pwHL = (float) Math.pow(mg_lacunarity, -mg_H); + float pwr = pwHL; /* starts with i=1 instead of 0 */ + AbstractNoiseFunc abstractNoiseFunc = noiseFunctions.get(Integer.valueOf(noisebasis)); + if (abstractNoiseFunc == null) { + abstractNoiseFunc = noiseFunctions.get(Integer.valueOf(0)); + } + + result = abstractNoiseFunc.executeS(x, y, z) + mg_offset; + weight = mg_gain * result; + x *= mg_lacunarity; + y *= mg_lacunarity; + z *= mg_lacunarity; + + for (int i = 1; weight > 0.001f && i < (int) mg_octaves; ++i) { + if (weight > 1.0f) { + weight = 1.0f; + } + signal = (abstractNoiseFunc.executeS(x, y, z) + mg_offset) * pwr; + pwr *= pwHL; + result += weight * signal; + weight *= mg_gain * signal; + x *= mg_lacunarity; + y *= mg_lacunarity; + z *= mg_lacunarity; + } + + rmd = mg_octaves - (float) Math.floor(mg_octaves); + if (rmd != 0.0f) { + result += rmd * (abstractNoiseFunc.executeS(x, y, z) + mg_offset) * pwr; + } + return result; + } + }); + musgraveFunctions.put(Integer.valueOf(TEX_FBM), new MusgraveFunction() { + + @Override + public float execute(Structure tex, float x, float y, float z) { + float mg_H = ((Number) tex.getFieldValue("mg_H")).floatValue(); + float mg_lacunarity = ((Number) tex.getFieldValue("mg_lacunarity")).floatValue(); + float mg_octaves = ((Number) tex.getFieldValue("mg_octaves")).floatValue(); + int noisebasis = ((Number) tex.getFieldValue("noisebasis")).intValue(); + float rmd, value = 0.0f, pwr = 1.0f, pwHL = (float) Math.pow(mg_lacunarity, -mg_H); + + AbstractNoiseFunc abstractNoiseFunc = noiseFunctions.get(Integer.valueOf(noisebasis)); + if (abstractNoiseFunc == null) { + abstractNoiseFunc = noiseFunctions.get(Integer.valueOf(0)); + } + + for (int i = 0; i < (int) mg_octaves; ++i) { + value += abstractNoiseFunc.executeS(x, y, z) * pwr; + pwr *= pwHL; + x *= mg_lacunarity; + y *= mg_lacunarity; + z *= mg_lacunarity; + } + + rmd = (float) (mg_octaves - Math.floor(mg_octaves)); + if (rmd != 0.f) { + value += rmd * abstractNoiseFunc.executeS(x, y, z) * pwr; + } + return value; + } + }); + musgraveFunctions.put(Integer.valueOf(TEX_HTERRAIN), new MusgraveFunction() { + + @Override + public float execute(Structure tex, float x, float y, float z) { + float mg_H = ((Number) tex.getFieldValue("mg_H")).floatValue(); + float mg_lacunarity = ((Number) tex.getFieldValue("mg_lacunarity")).floatValue(); + float mg_octaves = ((Number) tex.getFieldValue("mg_octaves")).floatValue(); + int noisebasis = ((Number) tex.getFieldValue("noisebasis")).intValue(); + float mg_offset = ((Number) tex.getFieldValue("mg_offset")).floatValue(); + float value, increment, rmd; + float pwHL = (float) Math.pow(mg_lacunarity, -mg_H); + float pwr = pwHL; /* starts with i=1 instead of 0 */ + AbstractNoiseFunc abstractNoiseFunc = noiseFunctions.get(Integer.valueOf(noisebasis)); + if (abstractNoiseFunc == null) { + abstractNoiseFunc = noiseFunctions.get(Integer.valueOf(0)); + } + + /* first unscaled octave of function; later octaves are scaled */ + value = mg_offset + abstractNoiseFunc.executeS(x, y, z); + x *= mg_lacunarity; + y *= mg_lacunarity; + z *= mg_lacunarity; + + for (int i = 1; i < (int) mg_octaves; ++i) { + increment = (abstractNoiseFunc.executeS(x, y, z) + mg_offset) * pwr * value; + value += increment; + pwr *= pwHL; + x *= mg_lacunarity; + y *= mg_lacunarity; + z *= mg_lacunarity; + } + + rmd = mg_octaves - (float) Math.floor(mg_octaves); + if (rmd != 0.0) { + increment = (abstractNoiseFunc.executeS(x, y, z) + mg_offset) * pwr * value; + value += rmd * increment; + } + return value; + } + }); + } + + /** + * THE FOLLOWING METHODS HELP IN COMPUTATION OF THE TEXTURES. + */ + protected void brightnesAndContrast(TexResult texres, float contrast, float brightness) { + texres.tin = (texres.tin - 0.5f) * contrast + brightness - 0.5f; + if (texres.tin < 0.0f) { + texres.tin = 0.0f; + } else if (texres.tin > 1.0f) { + texres.tin = 1.0f; + } + } + + protected void brightnesAndContrastRGB(Structure tex, TexResult texres) { + float contrast = ((Number) tex.getFieldValue("contrast")).floatValue(); + float bright = ((Number) tex.getFieldValue("bright")).floatValue(); + float rfac = ((Number) tex.getFieldValue("rfac")).floatValue(); + float gfac = ((Number) tex.getFieldValue("gfac")).floatValue(); + float bfac = ((Number) tex.getFieldValue("bfac")).floatValue(); + + texres.tr = rfac * ((texres.tr - 0.5f) * contrast + bright - 0.5f); + if (texres.tr < 0.0f) { + texres.tr = 0.0f; + } + texres.tg = gfac * ((texres.tg - 0.5f) * contrast + bright - 0.5f); + if (texres.tg < 0.0f) { + texres.tg = 0.0f; + } + texres.tb = bfac * ((texres.tb - 0.5f) * contrast + bright - 0.5f); + if (texres.tb < 0.0f) { + texres.tb = 0.0f; + } + } + + /* this allows colorbanded textures to control normals as well */ + public void texNormalDerivate(ColorBand colorBand, TexResult texres, DataRepository dataRepository) { + if (texres.nor != null) { + TexResult fakeTexresult; + try { + fakeTexresult = (TexResult) texres.clone(); + } catch (CloneNotSupportedException e) { + throw new IllegalStateException("Texture result class MUST support cloning!", e); + } + + float fac0 = fakeTexresult.tr + fakeTexresult.tg + fakeTexresult.tb; + fakeTexresult.tin = texres.nor[0]; + this.doColorband(colorBand, fakeTexresult, dataRepository); + + float fac1 = fakeTexresult.tr + fakeTexresult.tg + fakeTexresult.tb; + fakeTexresult.tin = texres.nor[1]; + this.doColorband(colorBand, fakeTexresult, dataRepository); + + float fac2 = fakeTexresult.tr + fakeTexresult.tg + fakeTexresult.tb; + fakeTexresult.tin = texres.nor[2]; + this.doColorband(colorBand, fakeTexresult, dataRepository); + + float fac3 = fakeTexresult.tr + fakeTexresult.tg + fakeTexresult.tb; + + texres.nor[0] = 0.3333f * (fac0 - fac1); + texres.nor[1] = 0.3333f * (fac0 - fac2); + texres.nor[2] = 0.3333f * (fac0 - fac3); + + texres.nor[0] = texres.tin - texres.nor[0]; + texres.nor[1] = texres.tin - texres.nor[1]; + texres.nor[2] = texres.tin - texres.nor[2]; + } + } + + /** + * This method calculates the colorband for the texture. + * @param colorBand + * the colorband data + * @param texres + * the texture pixel result + * @param dataRepository + * the data repository + * @return true if calculation suceedess and false otherwise + */ + public boolean doColorband(ColorBand colorBand, TexResult texres, DataRepository dataRepository) { + CBData cbd1, cbd2, cbd0, cbd3; + int i1 = 0, i2 = 0, a; + float fac, mfac; + float[] t = new float[4]; + + if (colorBand == null || colorBand.tot == 0) { + return true; + } + + cbd1 = colorBand.data[0]; + if (colorBand.tot == 1) { + texres.tr = cbd1.r; + texres.tg = cbd1.g; + texres.tb = cbd1.b; + texres.ta = cbd1.a; + } else { + if (texres.tin <= cbd1.pos && colorBand.ipotype < 2) { + texres.tr = cbd1.r; + texres.tg = cbd1.g; + texres.tb = cbd1.b; + texres.ta = cbd1.a; + } else { + /* we're looking for first pos > in */ + for (a = 0; a < colorBand.tot; ++a, ++i1) { + cbd1 = colorBand.data[i1]; + if (cbd1.pos > texres.tin) { + break; + } + } + + if (a == colorBand.tot) { + cbd2 = colorBand.data[i1 - 1]; + try { + cbd1 = (CBData) cbd2.clone(); + } catch (CloneNotSupportedException e) { + throw new IllegalStateException("Clone not supported for " + CBData.class.getName() + " class! Fix that!"); + } + cbd1.pos = 1.0f; + } else if (a == 0) { + try { + cbd2 = (CBData) cbd1.clone(); + } catch (CloneNotSupportedException e) { + throw new IllegalStateException("Clone not supported for " + CBData.class.getName() + " class! Fix that!"); + } + cbd2.pos = 0.0f; + } else { + cbd2 = colorBand.data[i1 - 1]; + } + + if (texres.tin >= cbd1.pos && colorBand.ipotype < 2) { + texres.tr = cbd1.r; + texres.tg = cbd1.g; + texres.tb = cbd1.b; + texres.ta = cbd1.a; + } else { + + if (cbd2.pos != cbd1.pos) { + fac = (texres.tin - cbd1.pos) / (cbd2.pos - cbd1.pos); + } else { + fac = 0.0f; + } + + if (colorBand.ipotype == 4) { + /* constant */ + texres.tr = cbd2.r; + texres.tg = cbd2.g; + texres.tb = cbd2.b; + texres.ta = cbd2.a; + return true; + } + + if (colorBand.ipotype >= 2) { + /* ipo from right to left: 3 2 1 0 */ + + if (a >= colorBand.tot - 1) { + cbd0 = cbd1; + } else { + cbd0 = colorBand.data[i1 + 1]; + } + if (a < 2) { + cbd3 = cbd2; + } else { + cbd3 = colorBand.data[i2 - 1]; + } + + fac = FastMath.clamp(fac, 0.0f, 1.0f); + + if (colorBand.ipotype == 3) { + this.setFourIpo(fac, t, KEY_CARDINAL); + } else { + this.setFourIpo(fac, t, KEY_BSPLINE); + } + + texres.tr = t[3] * cbd3.r + t[2] * cbd2.r + t[1] * cbd1.r + t[0] * cbd0.r; + texres.tg = t[3] * cbd3.g + t[2] * cbd2.g + t[1] * cbd1.g + t[0] * cbd0.g; + texres.tb = t[3] * cbd3.b + t[2] * cbd2.b + t[1] * cbd1.b + t[0] * cbd0.b; + texres.ta = t[3] * cbd3.a + t[2] * cbd2.a + t[1] * cbd1.a + t[0] * cbd0.a; + texres.tr = FastMath.clamp(texres.tr, 0.0f, 1.0f); + texres.tg = FastMath.clamp(texres.tg, 0.0f, 1.0f); + texres.tb = FastMath.clamp(texres.tb, 0.0f, 1.0f); + texres.ta = FastMath.clamp(texres.ta, 0.0f, 1.0f); + } else { + + if (colorBand.ipotype == 1) { /* EASE */ + mfac = fac * fac; + fac = 3.0f * mfac - 2.0f * mfac * fac; + } + mfac = 1.0f - fac; + + texres.tr = mfac * cbd1.r + fac * cbd2.r; + texres.tg = mfac * cbd1.g + fac * cbd2.g; + texres.tb = mfac * cbd1.b + fac * cbd2.b; + texres.ta = mfac * cbd1.a + fac * cbd2.a; + } + } + } + } + return true; + } + + protected void setFourIpo(float d, float[] data, int type) { + if (type == KEY_LINEAR) { + data[0] = 0.0f; + data[1] = 1.0f - d; + data[2] = d; + data[3] = 0.0f; + } else { + float d2 = d * d; + float d3 = d2 * d; + if (type == KEY_CARDINAL) { + float fc = 0.71f; + data[0] = -fc * d3 + 2.0f * fc * d2 - fc * d; + data[1] = (2.0f - fc) * d3 + (fc - 3.0f) * d2 + 1.0f; + data[2] = (fc - 2.0f) * d3 + (3.0f - 2.0f * fc) * d2 + fc * d; + data[3] = fc * d3 - fc * d2; + } else if (type == KEY_BSPLINE) { + data[0] = -0.16666666f * d3 + 0.5f * d2 - 0.5f * d + 0.16666666f; + data[1] = 0.5f * d3 - d2 + 0.6666666f; + data[2] = -0.5f * d3 + 0.5f * d2 + 0.5f * d + 0.16666666f; + data[3] = 0.16666666f * d3; + } + } + } + + interface IWaveForm { + + float execute(float x); + } + protected static IWaveForm[] waveformFunctions = new IWaveForm[3]; + + static { + waveformFunctions[0] = new IWaveForm() {// tex_sin + + @Override + public float execute(float x) { + return 0.5f + 0.5f * (float) Math.sin(x); + } + }; + waveformFunctions[1] = new IWaveForm() {// tex_saw + + @Override + public float execute(float x) { + int n = (int) (x / FastMath.TWO_PI); + x -= n * FastMath.TWO_PI; + if (x < 0.0f) { + x += FastMath.TWO_PI; + } + return x / FastMath.TWO_PI; + } + }; + waveformFunctions[2] = new IWaveForm() {// tex_tri + + @Override + public float execute(float x) { + return 1.0f - 2.0f * FastMath.abs((float) Math.floor(x * 1.0f / FastMath.TWO_PI + 0.5f) - x * 1.0f / FastMath.TWO_PI); + } + }; + } + + /* computes basic wood intensity value at x,y,z */ + public float woodInt(Structure tex, float x, float y, float z, DataRepository dataRepository) { + int noisebasis2 = ((Number) tex.getFieldValue("noisebasis2")).intValue(); + int noisebasis = ((Number) tex.getFieldValue("noisebasis")).intValue(); + int stype = ((Number) tex.getFieldValue("stype")).intValue(); + float noisesize = ((Number) tex.getFieldValue("noisesize")).floatValue(); + float turbul = ((Number) tex.getFieldValue("turbul")).floatValue(); + int noiseType = ((Number) tex.getFieldValue("noisetype")).intValue(); + float wi = 0; + int waveform = noisebasis2; /* wave form: TEX_SIN=0, TEX_SAW=1, TEX_TRI=2 */ + int wt = stype; /* wood type: TEX_BAND=0, TEX_RING=1, TEX_BANDNOISE=2, TEX_RINGNOISE=3 */ + + if (waveform > TEX_TRI || waveform < TEX_SIN) { + waveform = 0; /* check to be sure noisebasis2 is initialized ahead of time */ + } + + if (wt == TEX_BAND) { + wi = waveformFunctions[waveform].execute((x + y + z) * 10.0f); + } else if (wt == TEX_RING) { + wi = waveformFunctions[waveform].execute((float) Math.sqrt(x * x + y * y + z * z) * 20.0f); + } else if (wt == TEX_BANDNOISE) { + wi = turbul * this.bliGNoise(noisesize, x, y, z, noiseType != TEX_NOISESOFT, noisebasis); + wi = waveformFunctions[waveform].execute((x + y + z) * 10.0f + wi); + } else if (wt == TEX_RINGNOISE) { + wi = turbul * this.bliGNoise(noisesize, x, y, z, noiseType != TEX_NOISESOFT, noisebasis); + wi = waveformFunctions[waveform].execute((float) Math.sqrt(x * x + y * y + z * z) * 20.0f + wi); + } + return wi; + } + + /* computes basic marble intensity at x,y,z */ + public float marbleInt(Structure tex, float x, float y, float z, DataRepository dataRepository) { + float noisesize = ((Number) tex.getFieldValue("noisesize")).floatValue(); + int noisebasis = ((Number) tex.getFieldValue("noisebasis")).intValue(); + int noisedepth = ((Number) tex.getFieldValue("noisedepth")).intValue(); + int stype = ((Number) tex.getFieldValue("stype")).intValue();/* marble type: TEX_SOFT=0, TEX_SHARP=1,TEX_SHAPER=2 */ + float turbul = ((Number) tex.getFieldValue("turbul")).floatValue(); + int noisetype = ((Number) tex.getFieldValue("noisetype")).intValue(); + int waveform = ((Number) tex.getFieldValue("noisebasis2")).intValue(); /* wave form: TEX_SIN=0, TEX_SAW=1, TEX_TRI=2 */ + + if (waveform > TEX_TRI || waveform < TEX_SIN) { + waveform = 0; /* check to be sure noisebasis2 isn't initialized ahead of time */ + } + + float n = 5.0f * (x + y + z); + float mi = n + turbul * this.bliGTurbulence(noisesize, x, y, z, noisedepth, noisetype != TEX_NOISESOFT, noisebasis); + + if (stype >= NoiseHelper.TEX_SOFT) { /* TEX_SOFT always true */ + mi = waveformFunctions[waveform].execute(mi); + if (stype == TEX_SHARP) { + mi = (float) Math.sqrt(mi); + } else if (stype == TEX_SHARPER) { + mi = (float) Math.sqrt(Math.sqrt(mi)); + } + } + return mi; + } + + public void voronoi(float x, float y, float z, float[] da, float[] pa, float me, int dtype) { + AbstractNoiseFunc.voronoi(x, y, z, da, pa, me, dtype); + } + + public void cellNoiseV(float x, float y, float z, float[] ca) { + AbstractNoiseFunc.cellNoiseV(x, y, z, ca); + } + + /** + * THE FOLLOWING METHODS HELP IN NOISE COMPUTATIONS + */ + /** + * Separated from orgBlenderNoise above, with scaling. + * @param noisesize + * @param x + * @param y + * @param z + * @return + */ + private float bliHnoise(float noisesize, float x, float y, float z) { + if (noisesize == 0.0) { + return 0.0f; + } + x = (1.0f + x) / noisesize; + y = (1.0f + y) / noisesize; + z = (1.0f + z) / noisesize; + return noiseFunctions.get(0).execute(x, y, z); + } + + /** + * @param noisesize + * @param x + * @param y + * @param z + * @param nr + * @return + */ + public float bliTurbulence(float noisesize, float x, float y, float z, int nr) { + float d = 0.5f, div = 1.0f; + + float s = this.bliHnoise(noisesize, x, y, z); + while (nr > 0) { + s += d * this.bliHnoise(noisesize * d, x, y, z); + div += d; + d *= 0.5; + --nr; + } + return s / div; + } + + /** + * @param noisesize + * @param x + * @param y + * @param z + * @param nr + * @return + */ + public float bliTurbulence1(float noisesize, float x, float y, float z, int nr) { + float s, d = 0.5f, div = 1.0f; + + s = FastMath.abs((-1.0f + 2.0f * this.bliHnoise(noisesize, x, y, z))); + while (nr > 0) { + s += Math.abs(d * (-1.0f + 2.0f * this.bliHnoise(noisesize * d, x, y, z))); + div += d; + d *= 0.5; + --nr; + } + return s / div; + } + + /** + * @param noisesize + * @param x + * @param y + * @param z + * @return + */ + public float bliHnoisep(float noisesize, float x, float y, float z) { + return noiseFunctions.get(Integer.valueOf(0)).noise3Perlin(new float[]{x / noisesize, y / noisesize, z / noisesize}); + } + + /** + * @param point + * @param lofreq + * @param hifreq + * @return + */ + public float turbulencePerlin(float[] point, float lofreq, float hifreq) { + float freq, t = 0, p[] = new float[]{point[0] + 123.456f, point[1], point[2]}; + for (freq = lofreq; freq < hifreq; freq *= 2.) { + t += Math.abs(noiseFunctions.get(Integer.valueOf(0)).noise3Perlin(p)) / freq; + p[0] *= 2.0f; + p[1] *= 2.0f; + p[2] *= 2.0f; + } + return t - 0.3f; /* readjust to make mean value = 0.0 */ + } + + /** + * @param noisesize + * @param x + * @param y + * @param z + * @param nr + * @return + */ + public float turbulencep(float noisesize, float x, float y, float z, int nr) { + float[] vec = new float[]{x / noisesize, y / noisesize, z / noisesize}; + ++nr; + return this.turbulencePerlin(vec, 1.0f, (1 << nr)); + } + + /** + * Newnoise: generic noise function for use with different noisebases + * @param x + * @param y + * @param z + * @param oct + * @param isHard + * @param noisebasis + * @return + */ + public float bliGNoise(float noisesize, float x, float y, float z, boolean isHard, int noisebasis) { + AbstractNoiseFunc abstractNoiseFunc = noiseFunctions.get(Integer.valueOf(noisebasis)); + if (abstractNoiseFunc == null) { + abstractNoiseFunc = noiseFunctions.get(0); + noisebasis = 0; + } + if (noisebasis == 0) {// add one to make return value same as BLI_hnoise + x += 1; + y += 1; + z += 1; + } + + if (noisesize != 0.0) { + noisesize = 1.0f / noisesize; + x *= noisesize; + y *= noisesize; + z *= noisesize; + } + if (isHard) { + return Math.abs(2.0f * abstractNoiseFunc.execute(x, y, z) - 1.0f); + } + return abstractNoiseFunc.execute(x, y, z); + } + + /** + * Newnoise: generic turbulence function for use with different noisebasis + * @param x + * @param y + * @param z + * @param oct + * @param isHard + * @param noisebasis + * @return + */ + public float bliGTurbulence(float noisesize, float x, float y, float z, int oct, boolean isHard, int noisebasis) { + AbstractNoiseFunc abstractNoiseFunc = noiseFunctions.get(Integer.valueOf(noisebasis)); + if (abstractNoiseFunc == null) { + abstractNoiseFunc = noiseFunctions.get(0); + noisebasis = 0; + } + if (noisebasis == 0) {// add one to make return value same as BLI_hnoise + x += 1; + y += 1; + z += 1; + } + float sum = 0, t, amp = 1, fscale = 1; + + if (noisesize != 0.0) { + noisesize = 1.0f / noisesize; + x *= noisesize; + y *= noisesize; + z *= noisesize; + } + for (int i = 0; i <= oct; ++i, amp *= 0.5, fscale *= 2) { + t = abstractNoiseFunc.execute(fscale * x, fscale * y, fscale * z); + if (isHard) { + t = FastMath.abs(2.0f * t - 1.0f); + } + sum += t * amp; + } + + sum *= (float) (1 << oct) / (float) ((1 << oct + 1) - 1); + return sum; + } + + /** + * "Variable Lacunarity Noise" A distorted variety of Perlin noise. This method is used to calculate distorted noise + * texture. + * @param x + * @param y + * @param z + * @param distortion + * @param nbas1 + * @param nbas2 + * @return + */ + public float mgVLNoise(float x, float y, float z, float distortion, int nbas1, int nbas2) { + AbstractNoiseFunc abstractNoiseFunc1 = noiseFunctions.get(Integer.valueOf(nbas1)); + if (abstractNoiseFunc1 == null) { + abstractNoiseFunc1 = noiseFunctions.get(Integer.valueOf(0)); + } + AbstractNoiseFunc abstractNoiseFunc2 = noiseFunctions.get(Integer.valueOf(nbas2)); + if (abstractNoiseFunc2 == null) { + abstractNoiseFunc2 = noiseFunctions.get(Integer.valueOf(0)); + } + // get a random vector and scale the randomization + float rx = abstractNoiseFunc1.execute(x + 13.5f, y + 13.5f, z + 13.5f) * distortion; + float ry = abstractNoiseFunc1.execute(x, y, z) * distortion; + float rz = abstractNoiseFunc1.execute(x - 13.5f, y - 13.5f, z - 13.5f) * distortion; + return abstractNoiseFunc2.executeS(x + rx, y + ry, z + rz); //distorted-domain noise + } + + public void mgMFractalOrfBmTex(Structure tex, float[] texvec, ColorBand colorBand, TexResult texres, DataRepository dataRepository) { + int stype = ((Number) tex.getFieldValue("stype")).intValue(); + float nsOutscale = ((Number) tex.getFieldValue("ns_outscale")).floatValue(); + float nabla = ((Number) tex.getFieldValue("nabla")).floatValue(); + float noisesize = ((Number) tex.getFieldValue("noisesize")).floatValue(); + float contrast = ((Number) tex.getFieldValue("contrast")).floatValue(); + float brightness = ((Number) tex.getFieldValue("bright")).floatValue(); + + MusgraveFunction mgravefunc = stype == TEX_MFRACTAL ? musgraveFunctions.get(Integer.valueOf(stype)) : musgraveFunctions.get(Integer.valueOf(TEX_FBM)); + + texres.tin = nsOutscale * mgravefunc.execute(tex, texvec[0], texvec[1], texvec[2]); + if (texres.nor != null) { + float offs = nabla / noisesize; // also scaling of texvec + // calculate bumpnormal + texres.nor[0] = nsOutscale * mgravefunc.execute(tex, texvec[0] + offs, texvec[1], texvec[2]); + texres.nor[1] = nsOutscale * mgravefunc.execute(tex, texvec[0], texvec[1] + offs, texvec[2]); + texres.nor[2] = nsOutscale * mgravefunc.execute(tex, texvec[0], texvec[1], texvec[2] + offs); + this.texNormalDerivate(colorBand, texres, dataRepository); + } + this.brightnesAndContrast(texres, contrast, brightness); + } + + public void mgRidgedOrHybridMFTex(Structure tex, float[] texvec, ColorBand colorBand, TexResult texres, DataRepository dataRepository) { + int stype = ((Number) tex.getFieldValue("stype")).intValue(); + float nsOutscale = ((Number) tex.getFieldValue("ns_outscale")).floatValue(); + float nabla = ((Number) tex.getFieldValue("nabla")).floatValue(); + float noisesize = ((Number) tex.getFieldValue("noisesize")).floatValue(); + float contrast = ((Number) tex.getFieldValue("contrast")).floatValue(); + float brightness = ((Number) tex.getFieldValue("bright")).floatValue(); + + MusgraveFunction mgravefunc = stype == TEX_RIDGEDMF ? musgraveFunctions.get(Integer.valueOf(stype)) : musgraveFunctions.get(Integer.valueOf(TEX_HYBRIDMF)); + + texres.tin = nsOutscale * mgravefunc.execute(tex, texvec[0], texvec[1], texvec[2]); + if (texres.nor != null) { + float offs = nabla / noisesize; // also scaling of texvec + // calculate bumpnormal + texres.nor[0] = nsOutscale * mgravefunc.execute(tex, texvec[0] + offs, texvec[1], texvec[2]); + texres.nor[1] = nsOutscale * mgravefunc.execute(tex, texvec[0], texvec[1] + offs, texvec[2]); + texres.nor[2] = nsOutscale * mgravefunc.execute(tex, texvec[0], texvec[1], texvec[2] + offs); + this.texNormalDerivate(colorBand, texres, dataRepository); + } + this.brightnesAndContrast(texres, contrast, brightness); + } + + public void mgHTerrainTex(Structure tex, float[] texvec, ColorBand colorBand, TexResult texres, DataRepository dataRepository) { + float nsOutscale = ((Number) tex.getFieldValue("ns_outscale")).floatValue(); + float nabla = ((Number) tex.getFieldValue("nabla")).floatValue(); + float noisesize = ((Number) tex.getFieldValue("noisesize")).floatValue(); + float contrast = ((Number) tex.getFieldValue("contrast")).floatValue(); + float brightness = ((Number) tex.getFieldValue("bright")).floatValue(); + + MusgraveFunction musgraveFunction = musgraveFunctions.get(Integer.valueOf(TEX_HTERRAIN)); + texres.tin = nsOutscale * musgraveFunction.execute(tex, texvec[0], texvec[1], texvec[2]); + if (texres.nor != null) { + float offs = nabla / noisesize; // also scaling of texvec + // calculate bumpnormal + texres.nor[0] = nsOutscale * musgraveFunction.execute(tex, texvec[0] + offs, texvec[1], texvec[2]); + texres.nor[1] = nsOutscale * musgraveFunction.execute(tex, texvec[0], texvec[1] + offs, texvec[2]); + texres.nor[2] = nsOutscale * musgraveFunction.execute(tex, texvec[0], texvec[1], texvec[2] + offs); + this.texNormalDerivate(colorBand, texres, dataRepository); + } + this.brightnesAndContrast(texres, contrast, brightness); + } + + /** + * This class is abstract to the noise functions computations. It has two methods. One calculates the Signed (with + * 'S' at the end) and the other Unsigned value. + * @author Marcin Roguski (Kaelthas) + */ + protected static abstract class AbstractNoiseFunc { + + /** + * This method calculates the unsigned value of the noise. + * @param x + * the x texture coordinate + * @param y + * the y texture coordinate + * @param z + * the z texture coordinate + * @return value of the noise + */ + public abstract float execute(float x, float y, float z); + + /** + * This method calculates the signed value of the noise. + * @param x + * the x texture coordinate + * @param y + * the y texture coordinate + * @param z + * the z texture coordinate + * @return value of the noise + */ + public abstract float executeS(float x, float y, float z); + + /* + * Not 'pure' Worley, but the results are virtually the same. Returns distances in da and point coords in pa + */ + protected static void voronoi(float x, float y, float z, float[] da, float[] pa, float me, int dtype) { + float xd, yd, zd, d, p[]; + + DistanceFunc distanceFunc = distanceFunctions.get(Integer.valueOf(dtype)); + if (distanceFunc == null) { + distanceFunc = distanceFunctions.get(Integer.valueOf(0)); + } + + int xi = (int) FastMath.floor(x); + int yi = (int) FastMath.floor(y); + int zi = (int) FastMath.floor(z); + da[0] = da[1] = da[2] = da[3] = 1e10f; + for (int xx = xi - 1; xx <= xi + 1; ++xx) { + for (int yy = yi - 1; yy <= yi + 1; ++yy) { + for (int zz = zi - 1; zz <= zi + 1; ++zz) { + p = AbstractNoiseFunc.hashPoint(xx, yy, zz); + xd = x - (p[0] + xx); + yd = y - (p[1] + yy); + zd = z - (p[2] + zz); + d = distanceFunc.execute(xd, yd, zd, me); + if (d < da[0]) { + da[3] = da[2]; + da[2] = da[1]; + da[1] = da[0]; + da[0] = d; + pa[9] = pa[6]; + pa[10] = pa[7]; + pa[11] = pa[8]; + pa[6] = pa[3]; + pa[7] = pa[4]; + pa[8] = pa[5]; + pa[3] = pa[0]; + pa[4] = pa[1]; + pa[5] = pa[2]; + pa[0] = p[0] + xx; + pa[1] = p[1] + yy; + pa[2] = p[2] + zz; + } else if (d < da[1]) { + da[3] = da[2]; + da[2] = da[1]; + da[1] = d; + pa[9] = pa[6]; + pa[10] = pa[7]; + pa[11] = pa[8]; + pa[6] = pa[3]; + pa[7] = pa[4]; + pa[8] = pa[5]; + pa[3] = p[0] + xx; + pa[4] = p[1] + yy; + pa[5] = p[2] + zz; + } else if (d < da[2]) { + da[3] = da[2]; + da[2] = d; + pa[9] = pa[6]; + pa[10] = pa[7]; + pa[11] = pa[8]; + pa[6] = p[0] + xx; + pa[7] = p[1] + yy; + pa[8] = p[2] + zz; + } else if (d < da[3]) { + da[3] = d; + pa[9] = p[0] + xx; + pa[10] = p[1] + yy; + pa[11] = p[2] + zz; + } + } + } + } + } + + // #define HASHVEC(x,y,z) hashvectf+3*hash[ (hash[ (hash[(z) & 255]+(y)) & 255]+(x)) & 255] + + /* needed for voronoi */ + // #define HASHPNT(x,y,z) hashpntf+3*hash[ (hash[ (hash[(z) & 255]+(y)) & 255]+(x)) & 255] + protected static float[] hashPoint(int x, int y, int z) { + float[] result = new float[3]; + result[0] = hashpntf[3 * hash[hash[hash[z & 255] + y & 255] + x & 255]]; + result[1] = hashpntf[3 * hash[hash[hash[z & 255] + y & 255] + x & 255] + 1]; + result[2] = hashpntf[3 * hash[hash[hash[z & 255] + y & 255] + x & 255] + 2]; + return result; + } + + // #define setup(i,b0,b1,r0,r1) \ + // t = vec[i] + 10000.; \ + // b0 = ((int)t) & 255; \ + // b1 = (b0+1) & 255; \ + // r0 = t - (int)t; \ + // r1 = r0 - 1.; + // vec[3] + public float noise3Perlin(float[] vec) { + int bx0, bx1, by0, by1, bz0, bz1, b00, b10, b01, b11; + float rx0, rx1, ry0, ry1, rz0, rz1, sx, sy, sz, a, b, c, d, t, u, v; + int i, j; + + // setup(0, bx0,bx1, rx0,rx1); + t = vec[0] + 10000.0f; + bx0 = (int) t & 255; + bx1 = bx0 + 1 & 255; + rx0 = t - (int) t; + rx1 = rx0 - 1.0f; + // setup(1, by0,by1, ry0,ry1); + t = vec[0] + 10000.0f; + by0 = (int) t & 255; + by1 = by0 + 1 & 255; + ry0 = t - (int) t; + ry1 = ry0 - 1.0f; + // setup(2, bz0,bz1, rz0,rz1); + t = vec[0] + 10000.0f; + bz0 = (int) t & 255; + bz1 = bz0 + 1 & 255; + rz0 = t - (int) t; + rz1 = rz0 - 1.0f; + + i = p[bx0]; + j = p[bx1]; + + b00 = p[i + by0]; + b10 = p[j + by0]; + b01 = p[i + by1]; + b11 = p[j + by1]; + + /* lerp moved to improved perlin above */ + + sx = this.surve(rx0); + sy = this.surve(ry0); + sz = this.surve(rz0); + + float[] q = new float[3]; + q = g[b00 + bz0]; + u = this.at(rx0, ry0, rz0, q); + q = g[b10 + bz0]; + v = this.at(rx1, ry0, rz0, q); + a = this.lerp(sx, u, v); + + q = g[b01 + bz0]; + u = this.at(rx0, ry1, rz0, q); + q = g[b11 + bz0]; + v = this.at(rx1, ry1, rz0, q); + b = this.lerp(sx, u, v); + + c = this.lerp(sy, a, b); /* interpolate in y at lo x */ + + q = g[b00 + bz1]; + u = this.at(rx0, ry0, rz1, q); + q = g[b10 + bz1]; + v = this.at(rx1, ry0, rz1, q); + a = this.lerp(sx, u, v); + + q = g[b01 + bz1]; + u = this.at(rx0, ry1, rz1, q); + q = g[b11 + bz1]; + v = this.at(rx1, ry1, rz1, q); + b = this.lerp(sx, u, v); + + d = this.lerp(sy, a, b); /* interpolate in y at hi x */ + + return 1.5f * this.lerp(sz, c, d); /* interpolate in z */ + } + + public float orgBlenderNoise(float x, float y, float z) { + float cn1, cn2, cn3, cn4, cn5, cn6, i; + float ox, oy, oz, jx, jy, jz; + float n = 0.5f; + int ix, iy, iz, b00, b01, b10, b11, b20, b21; + + ox = x - (ix = (int) Math.floor(x)); + oy = y - (iy = (int) Math.floor(y)); + oz = z - (iz = (int) Math.floor(z)); + + jx = ox - 1; + jy = oy - 1; + jz = oz - 1; + + cn1 = ox * ox; + cn2 = oy * oy; + cn3 = oz * oz; + cn4 = jx * jx; + cn5 = jy * jy; + cn6 = jz * jz; + + cn1 = 1.0f - 3.0f * cn1 + 2.0f * cn1 * ox; + cn2 = 1.0f - 3.0f * cn2 + 2.0f * cn2 * oy; + cn3 = 1.0f - 3.0f * cn3 + 2.0f * cn3 * oz; + cn4 = 1.0f - 3.0f * cn4 - 2.0f * cn4 * jx; + cn5 = 1.0f - 3.0f * cn5 - 2.0f * cn5 * jy; + cn6 = 1.0f - 3.0f * cn6 - 2.0f * cn6 * jz; + + b00 = hash[hash[ix & 255] + (iy & 255)]; + b10 = hash[hash[ix + 1 & 255] + (iy & 255)]; + b01 = hash[hash[ix & 255] + (iy + 1 & 255)]; + b11 = hash[hash[ix + 1 & 255] + (iy + 1 & 255)]; + + b20 = iz & 255; + b21 = iz + 1 & 255; + + /* 0 */ + i = cn1 * cn2 * cn3; + int hIndex = 3 * hash[b20 + b00]; + n += i * (hashvectf[hIndex] * ox + hashvectf[hIndex + 1] * oy + hashvectf[hIndex + 2] * oz); + /* 1 */ + i = cn1 * cn2 * cn6; + hIndex = 3 * hash[b21 + b00]; + n += i * (hashvectf[hIndex] * ox + hashvectf[hIndex + 1] * oy + hashvectf[hIndex + 2] * jz); + /* 2 */ + i = cn1 * cn5 * cn3; + hIndex = 3 * hash[b20 + b01]; + n += i * (hashvectf[hIndex] * ox + hashvectf[hIndex + 1] * jy + hashvectf[hIndex + 2] * oz); + /* 3 */ + i = cn1 * cn5 * cn6; + hIndex = 3 * hash[b21 + b01]; + n += i * (hashvectf[hIndex] * ox + hashvectf[hIndex + 1] * jy + hashvectf[hIndex + 2] * jz); + /* 4 */ + i = cn4 * cn2 * cn3; + hIndex = 3 * hash[b20 + b10]; + n += i * (hashvectf[hIndex] * jx + hashvectf[hIndex + 1] * oy + hashvectf[hIndex + 2] * oz); + /* 5 */ + i = cn4 * cn2 * cn6; + hIndex = 3 * hash[b21 + b10]; + n += i * (hashvectf[hIndex] * jx + hashvectf[hIndex + 1] * oy + hashvectf[hIndex + 2] * jz); + /* 6 */ + i = cn4 * cn5 * cn3; + hIndex = 3 * hash[b20 + b11]; + n += i * (hashvectf[hIndex] * jx + hashvectf[hIndex + 1] * jy + hashvectf[hIndex + 2] * oz); + /* 7 */ + i = cn4 * cn5 * cn6; + hIndex = 3 * hash[b21 + b11]; + n += i * (hashvectf[hIndex] * jx + hashvectf[hIndex + 1] * jy + hashvectf[hIndex + 2] * jz); + + if (n < 0.0f) { + n = 0.0f; + } else if (n > 1.0f) { + n = 1.0f; + } + return n; + } + + /* instead of adding another permutation array, just use hash table defined above */ + public float newPerlin(float x, float y, float z) { + int A, AA, AB, B, BA, BB; + float u = (float) Math.floor(x), v = (float) Math.floor(y), w = (float) Math.floor(z); + int X = (int) u & 255, Y = (int) v & 255, Z = (int) w & 255; // FIND UNIT CUBE THAT CONTAINS POINT + x -= u; // FIND RELATIVE X,Y,Z + y -= v; // OF POINT IN CUBE. + z -= w; + u = this.npfade(x); // COMPUTE FADE CURVES + v = this.npfade(y); // FOR EACH OF X,Y,Z. + w = this.npfade(z); + A = hash[X] + Y; + AA = hash[A] + Z; + AB = hash[A + 1] + Z; // HASH COORDINATES OF + B = hash[X + 1] + Y; + BA = hash[B] + Z; + BB = hash[B + 1] + Z; // THE 8 CUBE CORNERS, + return this.lerp(w, this.lerp(v, this.lerp(u, this.grad(hash[AA], x, y, z), // AND ADD + this.grad(hash[BA], x - 1, y, z)), // BLENDED + this.lerp(u, this.grad(hash[AB], x, y - 1, z), // RESULTS + this.grad(hash[BB], x - 1, y - 1, z))),// FROM 8 + this.lerp(v, this.lerp(u, this.grad(hash[AA + 1], x, y, z - 1), // CORNERS + this.grad(hash[BA + 1], x - 1, y, z - 1)), // OF CUBE + this.lerp(u, this.grad(hash[AB + 1], x, y - 1, z - 1), this.grad(hash[BB + 1], x - 1, y - 1, z - 1)))); + } + + /** + * Returns a vector/point/color in ca, using point hasharray directly + */ + protected static void cellNoiseV(float x, float y, float z, float[] ca) { + int xi = (int) Math.floor(x); + int yi = (int) Math.floor(y); + int zi = (int) Math.floor(z); + float[] p = AbstractNoiseFunc.hashPoint(xi, yi, zi); + ca[0] = p[0]; + ca[1] = p[1]; + ca[2] = p[2]; + } + + protected float lerp(float t, float a, float b) { + return a + t * (b - a); + } + + protected float npfade(float t) { + return t * t * t * (t * (t * 6.0f - 15.0f) + 10.0f); + } + + protected float grad(int hash, float x, float y, float z) { + int h = hash & 0x0F; // CONVERT LO 4 BITS OF HASH CODE + float u = h < 8 ? x : y, // INTO 12 GRADIENT DIRECTIONS. + v = h < 4 ? y : h == 12 || h == 14 ? x : z; + return ((h & 1) == 0 ? u : -u) + ((h & 2) == 0 ? v : -v); + } + + /** + * Dot product of two vectors. + * @param a + * the first vector + * @param b + * the second vector + * @return the dot product of two vectors + */ + protected float dot(float[] a, float[] b) { + return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; + } + + protected float surve(float t) { + return t * t * (3.0f - 2.0f * t); + } + + protected float at(float rx, float ry, float rz, float[] q) { + return rx * q[0] + ry * q[1] + rz * q[2]; + } + } + + /** + * This interface is used for distance calculation classes. Distance metrics for voronoi. e parameter only used in + * Minkovsky. + */ + interface DistanceFunc { + + /** + * This method calculates the distance for voronoi algorithms. + * @param x + * the x coordinate + * @param y + * the y coordinate + * @param z + * the z coordinate + * @param e + * this parameter used in Monkovsky (no idea what it really is ;) + * @return + */ + float execute(float x, float y, float z, float e); + } + + interface MusgraveFunction { + + float execute(Structure tex, float x, float y, float z); + } } diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/structures/AbstractInfluenceFunction.java b/engine/src/blender/com/jme3/scene/plugins/blender/structures/AbstractInfluenceFunction.java index 5216cf354..4fbe0feb2 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/structures/AbstractInfluenceFunction.java +++ b/engine/src/blender/com/jme3/scene/plugins/blender/structures/AbstractInfluenceFunction.java @@ -21,205 +21,199 @@ import com.jme3.scene.plugins.blender.utils.Pointer; * @author Marcin Roguski */ public abstract class AbstractInfluenceFunction { - protected static final Logger LOGGER = Logger.getLogger(AbstractInfluenceFunction.class.getName()); - protected static final float IK_SOLVER_ERROR = 0.5f; - - //DISTLIMIT - protected static final int LIMITDIST_INSIDE = 0; - protected static final int LIMITDIST_OUTSIDE = 1; - protected static final int LIMITDIST_ONSURFACE = 2; - - //CONSTRAINT_TYPE_LOCLIKE - protected static final int LOCLIKE_X = 0x01; - protected static final int LOCLIKE_Y = 0x02; - protected static final int LOCLIKE_Z = 0x04; - - //ROTLIKE - protected static final int ROTLIKE_X = 0x01; - protected static final int ROTLIKE_Y = 0x02; - protected static final int ROTLIKE_Z = 0x04; - protected static final int ROTLIKE_X_INVERT = 0x10; - protected static final int ROTLIKE_Y_INVERT = 0x20; - protected static final int ROTLIKE_Z_INVERT = 0x40; - protected static final int ROTLIKE_OFFSET = 0x80; - - //SIZELIKE - protected static final int SIZELIKE_X = 0x01; - protected static final int SIZELIKE_Y = 0x02; - protected static final int SIZELIKE_Z = 0x04; - protected static final int SIZELIKE_OFFSET = 0x80; - - /* LOCLIKE_TIP is a depreceated option... use headtail=1.0f instead */ - //protected static final int LOCLIKE_TIP = 0x08; - protected static final int LOCLIKE_X_INVERT = 0x10; - protected static final int LOCLIKE_Y_INVERT = 0x20; - protected static final int LOCLIKE_Z_INVERT = 0x40; - protected static final int LOCLIKE_OFFSET = 0x80; - - //LOCLIMIT, SIZELIMIT - protected static final int LIMIT_XMIN = 0x01; - protected static final int LIMIT_XMAX = 0x02; - protected static final int LIMIT_YMIN = 0x04; - protected static final int LIMIT_YMAX = 0x08; - protected static final int LIMIT_ZMIN = 0x10; - protected static final int LIMIT_ZMAX = 0x20; - - //ROTLIMIT - protected static final int LIMIT_XROT = 0x01; - protected static final int LIMIT_YROT = 0x02; - protected static final int LIMIT_ZROT = 0x04; - - /** The type of the constraint. */ - protected ConstraintType constraintType; - /** The data repository. */ - protected DataRepository dataRepository; - - /** - * Constructor. - * @param constraintType - * the type of the current constraint - * @param dataRepository - * the data repository - */ - public AbstractInfluenceFunction(ConstraintType constraintType, DataRepository dataRepository) { - this.constraintType = constraintType; - this.dataRepository = dataRepository; - } - - /** - * This method validates the constraint type. It throws an IllegalArgumentException if the constraint type of the - * given structure is invalid. - * @param constraintStructure - * the structure with constraint data - */ - protected void validateConstraintType(Structure constraintStructure) { - if(!constraintType.getClassName().equalsIgnoreCase(constraintStructure.getType())) { - throw new IllegalArgumentException("Invalud structure type (" + constraintStructure.getType() + ") for the constraint: " + constraintType.getClassName() + '!'); - } - } - - /** - * This method affects the bone animation tracks for the given skeleton. - * @param skeleton - * the skeleton containing the affected bones by constraint - * @param boneAnimation - * the bone animation baked traces - * @param constraint - * the constraint - */ - public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation, Constraint constraint) {} - - /** - * This method returns the bone traces for the bone that is affected by the given constraint. - * @param skeleton - * the skeleton containing bones - * @param boneAnimation - * the bone animation that affects the skeleton - * @param constraint - * the affecting constraint - * @return the bone track for the bone that is being affected by the constraint - */ - protected BoneTrack getBoneTrack(Skeleton skeleton, BoneAnimation boneAnimation, Constraint constraint) { - Long boneOMA = constraint.getBoneOMA(); - Bone bone = (Bone)dataRepository.getLoadedFeature(boneOMA, LoadedFeatureDataType.LOADED_FEATURE); - int boneIndex = skeleton.getBoneIndex(bone); - if(boneIndex != -1) { - //searching for track for this bone - for(BoneTrack boneTrack : boneAnimation.getTracks()) { - if(boneTrack.getTargetBoneIndex() == boneIndex) { - return boneTrack; - } - } - } - return null; - } - - /** - * This method returns the target or subtarget object (if specified). - * @param constraint - * the constraint instance - * @return target or subtarget feature - */ - protected Object getTarget(Constraint constraint, LoadedFeatureDataType loadedFeatureDataType) { - Long targetOMA = ((Pointer)constraint.getData().getFieldValue("tar")).getOldMemoryAddress(); - Object targetObject = dataRepository.getLoadedFeature(targetOMA, loadedFeatureDataType); - String subtargetName = constraint.getData().getFieldValue("subtarget").toString(); - if(subtargetName.length() > 0) { - return dataRepository.getLoadedFeature(subtargetName, loadedFeatureDataType); - } - return targetObject; - } - - /** - * This method returns target's object location. - * @param constraint - * the constraint instance - * @return target's object location - */ - protected Vector3f getTargetLocation(Constraint constraint) { - Long targetOMA = ((Pointer)constraint.getData().getFieldValue("tar")).getOldMemoryAddress(); - Space targetSpace = constraint.getTargetSpace(); - Node targetObject = (Node)dataRepository.getLoadedFeature(targetOMA, LoadedFeatureDataType.LOADED_FEATURE); - switch(targetSpace) { - case CONSTRAINT_SPACE_LOCAL: - return targetObject.getLocalTranslation(); - case CONSTRAINT_SPACE_WORLD: - return targetObject.getWorldTranslation(); - default: - throw new IllegalStateException("Invalid space type for target object: " + targetSpace.toString()); - } - } - - /** - * This method returns target's object location in the specified frame. - * @param constraint - * the constraint instance - * @param frame - * the frame number - * @return target's object location - */ - protected Vector3f getTargetLocation(Constraint constraint, int frame) { - return this.getTargetLocation(constraint);//TODO: implement getting location in a specified frame - } - - /** - * This method returns target's object rotation. - * @param constraint - * the constraint instance - * @return target's object rotation - */ - protected Quaternion getTargetRotation(Constraint constraint) { - Long targetOMA = ((Pointer)constraint.getData().getFieldValue("tar")).getOldMemoryAddress(); - Space targetSpace = constraint.getTargetSpace(); - Node targetObject = (Node)dataRepository.getLoadedFeature(targetOMA, LoadedFeatureDataType.LOADED_FEATURE); - switch(targetSpace) { - case CONSTRAINT_SPACE_LOCAL: - return targetObject.getLocalRotation(); - case CONSTRAINT_SPACE_WORLD: - return targetObject.getWorldRotation(); - default: - throw new IllegalStateException("Invalid space type for target object: " + targetSpace.toString()); - } - } - - /** - * This method returns target's object scale. - * @param constraint - * the constraint instance - * @return target's object scale - */ - protected Vector3f getTargetScale(Constraint constraint) { - Long targetOMA = ((Pointer)constraint.getData().getFieldValue("tar")).getOldMemoryAddress(); - Space targetSpace = constraint.getTargetSpace(); - Node targetObject = (Node)dataRepository.getLoadedFeature(targetOMA, LoadedFeatureDataType.LOADED_FEATURE); - switch(targetSpace) { - case CONSTRAINT_SPACE_LOCAL: - return targetObject.getLocalScale(); - case CONSTRAINT_SPACE_WORLD: - return targetObject.getWorldScale(); - default: - throw new IllegalStateException("Invalid space type for target object: " + targetSpace.toString()); - } - } + protected static final Logger LOGGER = Logger.getLogger(AbstractInfluenceFunction.class.getName()); + protected static final float IK_SOLVER_ERROR = 0.5f; + //DISTLIMIT + protected static final int LIMITDIST_INSIDE = 0; + protected static final int LIMITDIST_OUTSIDE = 1; + protected static final int LIMITDIST_ONSURFACE = 2; + //CONSTRAINT_TYPE_LOCLIKE + protected static final int LOCLIKE_X = 0x01; + protected static final int LOCLIKE_Y = 0x02; + protected static final int LOCLIKE_Z = 0x04; + //ROTLIKE + protected static final int ROTLIKE_X = 0x01; + protected static final int ROTLIKE_Y = 0x02; + protected static final int ROTLIKE_Z = 0x04; + protected static final int ROTLIKE_X_INVERT = 0x10; + protected static final int ROTLIKE_Y_INVERT = 0x20; + protected static final int ROTLIKE_Z_INVERT = 0x40; + protected static final int ROTLIKE_OFFSET = 0x80; + //SIZELIKE + protected static final int SIZELIKE_X = 0x01; + protected static final int SIZELIKE_Y = 0x02; + protected static final int SIZELIKE_Z = 0x04; + protected static final int SIZELIKE_OFFSET = 0x80; + + /* LOCLIKE_TIP is a depreceated option... use headtail=1.0f instead */ + //protected static final int LOCLIKE_TIP = 0x08; + protected static final int LOCLIKE_X_INVERT = 0x10; + protected static final int LOCLIKE_Y_INVERT = 0x20; + protected static final int LOCLIKE_Z_INVERT = 0x40; + protected static final int LOCLIKE_OFFSET = 0x80; + //LOCLIMIT, SIZELIMIT + protected static final int LIMIT_XMIN = 0x01; + protected static final int LIMIT_XMAX = 0x02; + protected static final int LIMIT_YMIN = 0x04; + protected static final int LIMIT_YMAX = 0x08; + protected static final int LIMIT_ZMIN = 0x10; + protected static final int LIMIT_ZMAX = 0x20; + //ROTLIMIT + protected static final int LIMIT_XROT = 0x01; + protected static final int LIMIT_YROT = 0x02; + protected static final int LIMIT_ZROT = 0x04; + /** The type of the constraint. */ + protected ConstraintType constraintType; + /** The data repository. */ + protected DataRepository dataRepository; + + /** + * Constructor. + * @param constraintType + * the type of the current constraint + * @param dataRepository + * the data repository + */ + public AbstractInfluenceFunction(ConstraintType constraintType, DataRepository dataRepository) { + this.constraintType = constraintType; + this.dataRepository = dataRepository; + } + + /** + * This method validates the constraint type. It throws an IllegalArgumentException if the constraint type of the + * given structure is invalid. + * @param constraintStructure + * the structure with constraint data + */ + protected void validateConstraintType(Structure constraintStructure) { + if (!constraintType.getClassName().equalsIgnoreCase(constraintStructure.getType())) { + throw new IllegalArgumentException("Invalud structure type (" + constraintStructure.getType() + ") for the constraint: " + constraintType.getClassName() + '!'); + } + } + + /** + * This method affects the bone animation tracks for the given skeleton. + * @param skeleton + * the skeleton containing the affected bones by constraint + * @param boneAnimation + * the bone animation baked traces + * @param constraint + * the constraint + */ + public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation, Constraint constraint) { + } + + /** + * This method returns the bone traces for the bone that is affected by the given constraint. + * @param skeleton + * the skeleton containing bones + * @param boneAnimation + * the bone animation that affects the skeleton + * @param constraint + * the affecting constraint + * @return the bone track for the bone that is being affected by the constraint + */ + protected BoneTrack getBoneTrack(Skeleton skeleton, BoneAnimation boneAnimation, Constraint constraint) { + Long boneOMA = constraint.getBoneOMA(); + Bone bone = (Bone) dataRepository.getLoadedFeature(boneOMA, LoadedFeatureDataType.LOADED_FEATURE); + int boneIndex = skeleton.getBoneIndex(bone); + if (boneIndex != -1) { + //searching for track for this bone + for (BoneTrack boneTrack : boneAnimation.getTracks()) { + if (boneTrack.getTargetBoneIndex() == boneIndex) { + return boneTrack; + } + } + } + return null; + } + + /** + * This method returns the target or subtarget object (if specified). + * @param constraint + * the constraint instance + * @return target or subtarget feature + */ + protected Object getTarget(Constraint constraint, LoadedFeatureDataType loadedFeatureDataType) { + Long targetOMA = ((Pointer) constraint.getData().getFieldValue("tar")).getOldMemoryAddress(); + Object targetObject = dataRepository.getLoadedFeature(targetOMA, loadedFeatureDataType); + String subtargetName = constraint.getData().getFieldValue("subtarget").toString(); + if (subtargetName.length() > 0) { + return dataRepository.getLoadedFeature(subtargetName, loadedFeatureDataType); + } + return targetObject; + } + + /** + * This method returns target's object location. + * @param constraint + * the constraint instance + * @return target's object location + */ + protected Vector3f getTargetLocation(Constraint constraint) { + Long targetOMA = ((Pointer) constraint.getData().getFieldValue("tar")).getOldMemoryAddress(); + Space targetSpace = constraint.getTargetSpace(); + Node targetObject = (Node) dataRepository.getLoadedFeature(targetOMA, LoadedFeatureDataType.LOADED_FEATURE); + switch (targetSpace) { + case CONSTRAINT_SPACE_LOCAL: + return targetObject.getLocalTranslation(); + case CONSTRAINT_SPACE_WORLD: + return targetObject.getWorldTranslation(); + default: + throw new IllegalStateException("Invalid space type for target object: " + targetSpace.toString()); + } + } + + /** + * This method returns target's object location in the specified frame. + * @param constraint + * the constraint instance + * @param frame + * the frame number + * @return target's object location + */ + protected Vector3f getTargetLocation(Constraint constraint, int frame) { + return this.getTargetLocation(constraint);//TODO: implement getting location in a specified frame + } + + /** + * This method returns target's object rotation. + * @param constraint + * the constraint instance + * @return target's object rotation + */ + protected Quaternion getTargetRotation(Constraint constraint) { + Long targetOMA = ((Pointer) constraint.getData().getFieldValue("tar")).getOldMemoryAddress(); + Space targetSpace = constraint.getTargetSpace(); + Node targetObject = (Node) dataRepository.getLoadedFeature(targetOMA, LoadedFeatureDataType.LOADED_FEATURE); + switch (targetSpace) { + case CONSTRAINT_SPACE_LOCAL: + return targetObject.getLocalRotation(); + case CONSTRAINT_SPACE_WORLD: + return targetObject.getWorldRotation(); + default: + throw new IllegalStateException("Invalid space type for target object: " + targetSpace.toString()); + } + } + + /** + * This method returns target's object scale. + * @param constraint + * the constraint instance + * @return target's object scale + */ + protected Vector3f getTargetScale(Constraint constraint) { + Long targetOMA = ((Pointer) constraint.getData().getFieldValue("tar")).getOldMemoryAddress(); + Space targetSpace = constraint.getTargetSpace(); + Node targetObject = (Node) dataRepository.getLoadedFeature(targetOMA, LoadedFeatureDataType.LOADED_FEATURE); + switch (targetSpace) { + case CONSTRAINT_SPACE_LOCAL: + return targetObject.getLocalScale(); + case CONSTRAINT_SPACE_WORLD: + return targetObject.getWorldScale(); + default: + throw new IllegalStateException("Invalid space type for target object: " + targetSpace.toString()); + } + } } diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/structures/BezierCurve.java b/engine/src/blender/com/jme3/scene/plugins/blender/structures/BezierCurve.java index bf7b43c77..9826d9a32 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/structures/BezierCurve.java +++ b/engine/src/blender/com/jme3/scene/plugins/blender/structures/BezierCurve.java @@ -13,125 +13,125 @@ import com.jme3.scene.plugins.blender.utils.DynamicArray; * @author Marcin Roguski */ public class BezierCurve { - public static final int X_VALUE = 0; - public static final int Y_VALUE = 1; - public static final int Z_VALUE = 2; - /** - * The type of the curve. Describes the data it modifies. - * Used in ipos calculations. - */ - private int type; - /** The dimension of the curve. */ - private int dimension; - /** A table of the bezier points. */ - private float[][][] bezierPoints; + public static final int X_VALUE = 0; + public static final int Y_VALUE = 1; + public static final int Z_VALUE = 2; + /** + * The type of the curve. Describes the data it modifies. + * Used in ipos calculations. + */ + private int type; + /** The dimension of the curve. */ + private int dimension; + /** A table of the bezier points. */ + private float[][][] bezierPoints; - @SuppressWarnings("unchecked") - public BezierCurve(final int type, final List bezTriples, final int dimension) { - if(dimension != 2 && dimension != 3) { - throw new IllegalArgumentException("The dimension of the curve should be 2 or 3!"); - } - this.type = type; - this.dimension = dimension; - //first index of the bezierPoints table has the length of triples amount - //the second index points to a table od three points of a bezier triple (handle, point, handle) - //the third index specifies the coordinates of the specific point in a bezier triple - bezierPoints = new float[bezTriples.size()][3][dimension]; - int i = 0, j, k; - for(Structure bezTriple : bezTriples) { - DynamicArray vec = (DynamicArray)bezTriple.getFieldValue("vec"); - for(j = 0; j < 3; ++j) { - for(k = 0; k < dimension; ++k) { - bezierPoints[i][j][k] = vec.get(j, k).floatValue(); - } - } - ++i; - } - } + @SuppressWarnings("unchecked") + public BezierCurve(final int type, final List bezTriples, final int dimension) { + if (dimension != 2 && dimension != 3) { + throw new IllegalArgumentException("The dimension of the curve should be 2 or 3!"); + } + this.type = type; + this.dimension = dimension; + //first index of the bezierPoints table has the length of triples amount + //the second index points to a table od three points of a bezier triple (handle, point, handle) + //the third index specifies the coordinates of the specific point in a bezier triple + bezierPoints = new float[bezTriples.size()][3][dimension]; + int i = 0, j, k; + for (Structure bezTriple : bezTriples) { + DynamicArray vec = (DynamicArray) bezTriple.getFieldValue("vec"); + for (j = 0; j < 3; ++j) { + for (k = 0; k < dimension; ++k) { + bezierPoints[i][j][k] = vec.get(j, k).floatValue(); + } + } + ++i; + } + } - /** - * This method evaluates the data for the specified frame. The Y value is returned. - * @param frame - * the frame for which the value is being calculated - * @param valuePart - * this param specifies wheather we should return the X, Y or Z part of the result value; it should have - * one of the following values: X_VALUE - the X factor of the result Y_VALUE - the Y factor of the result - * Z_VALUE - the Z factor of the result - * @return the value of the curve - */ - public float evaluate(int frame, int valuePart) { - for(int i = 0; i < bezierPoints.length - 1; ++i) { - if(frame >= bezierPoints[i][1][0] && frame <= bezierPoints[i + 1][1][0]) { - float t = (frame - bezierPoints[i][1][0]) / (bezierPoints[i + 1][1][0] - bezierPoints[i][1][0]); - float oneMinusT = 1.0f - t; - float oneMinusT2 = oneMinusT * oneMinusT; - float t2 = t * t; - return bezierPoints[i][1][valuePart] * oneMinusT2 * oneMinusT + 3.0f * bezierPoints[i][2][valuePart] * t * oneMinusT2 + 3.0f * bezierPoints[i + 1][0][valuePart] * t2 * oneMinusT + bezierPoints[i + 1][1][valuePart] * t2 * t; - } - } - if(frame < bezierPoints[0][1][0]) { - return bezierPoints[0][1][1]; - } else { //frame>bezierPoints[bezierPoints.length-1][1][0] - return bezierPoints[bezierPoints.length - 1][1][1]; - } - } + /** + * This method evaluates the data for the specified frame. The Y value is returned. + * @param frame + * the frame for which the value is being calculated + * @param valuePart + * this param specifies wheather we should return the X, Y or Z part of the result value; it should have + * one of the following values: X_VALUE - the X factor of the result Y_VALUE - the Y factor of the result + * Z_VALUE - the Z factor of the result + * @return the value of the curve + */ + public float evaluate(int frame, int valuePart) { + for (int i = 0; i < bezierPoints.length - 1; ++i) { + if (frame >= bezierPoints[i][1][0] && frame <= bezierPoints[i + 1][1][0]) { + float t = (frame - bezierPoints[i][1][0]) / (bezierPoints[i + 1][1][0] - bezierPoints[i][1][0]); + float oneMinusT = 1.0f - t; + float oneMinusT2 = oneMinusT * oneMinusT; + float t2 = t * t; + return bezierPoints[i][1][valuePart] * oneMinusT2 * oneMinusT + 3.0f * bezierPoints[i][2][valuePart] * t * oneMinusT2 + 3.0f * bezierPoints[i + 1][0][valuePart] * t2 * oneMinusT + bezierPoints[i + 1][1][valuePart] * t2 * t; + } + } + if (frame < bezierPoints[0][1][0]) { + return bezierPoints[0][1][1]; + } else { //frame>bezierPoints[bezierPoints.length-1][1][0] + return bezierPoints[bezierPoints.length - 1][1][1]; + } + } - /** - * This method returns the frame where last bezier triple center point of the bezier curve is located. - * @return the frame number of the last defined bezier triple point for the curve - */ - public int getLastFrame() { - return (int)bezierPoints[bezierPoints.length - 1][1][0]; - } - - /** - * This method returns the type of the bezier curve. The type describes the parameter that this curve modifies - * (ie. LocationX or rotationW of the feature). - * @return the type of the bezier curve - */ - public int getType() { - return type; - } - - /** - * This method returns a list of control points for this curve. - * @return a list of control points for this curve. - */ - public List getControlPoints() { - List controlPoints = new ArrayList(bezierPoints.length * 3); - for(int i = 0;i getControlPoints() { + List controlPoints = new ArrayList(bezierPoints.length * 3); + for (int i = 0; i < bezierPoints.length; ++i) { + controlPoints.add(new Vector3f(bezierPoints[i][0][0], bezierPoints[i][0][1], bezierPoints[i][0][2])); + controlPoints.add(new Vector3f(bezierPoints[i][1][0], bezierPoints[i][1][1], bezierPoints[i][1][2])); + controlPoints.add(new Vector3f(bezierPoints[i][2][0], bezierPoints[i][2][1], bezierPoints[i][2][2])); + } + return controlPoints; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder("Bezier curve: ").append(type).append('\n'); + for (int i = 0; i < bezierPoints.length; ++i) { + sb.append(this.toStringBezTriple(i)).append('\n'); + } + return sb.toString(); + } + + /** + * This method converts the bezier triple of a specified index into text. + * @param tripleIndex + * index of the triple + * @return text representation of the triple + */ + private String toStringBezTriple(int tripleIndex) { + if (this.dimension == 2) { + return "[(" + bezierPoints[tripleIndex][0][0] + ", " + bezierPoints[tripleIndex][0][1] + ") (" + + bezierPoints[tripleIndex][1][0] + ", " + bezierPoints[tripleIndex][1][1] + ") (" + + bezierPoints[tripleIndex][2][0] + ", " + bezierPoints[tripleIndex][2][1] + ")]"; + } else { + return "[(" + bezierPoints[tripleIndex][0][0] + ", " + bezierPoints[tripleIndex][0][1] + ", " + bezierPoints[tripleIndex][0][2] + ") (" + + bezierPoints[tripleIndex][1][0] + ", " + bezierPoints[tripleIndex][1][1] + ", " + bezierPoints[tripleIndex][1][2] + ") (" + + bezierPoints[tripleIndex][2][0] + ", " + bezierPoints[tripleIndex][2][1] + ", " + bezierPoints[tripleIndex][2][2] + ")]"; + } + } } \ No newline at end of file diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/structures/Constraint.java b/engine/src/blender/com/jme3/scene/plugins/blender/structures/Constraint.java index 6aa7ba8a2..6f2c645c3 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/structures/Constraint.java +++ b/engine/src/blender/com/jme3/scene/plugins/blender/structures/Constraint.java @@ -12,151 +12,151 @@ import com.jme3.scene.plugins.blender.utils.Pointer; * @author Marcin Roguski */ public class Constraint { - /** The type of this constraint. */ - private final ConstraintType type; - /** The name of this constraint. */ - private final String name; - /** The old memory address of the constraint's owner. */ - private final Long boneOMA; - private final Space ownerSpace; + /** The type of this constraint. */ + private final ConstraintType type; + /** The name of this constraint. */ + private final String name; + /** The old memory address of the constraint's owner. */ + private final Long boneOMA; + private final Space ownerSpace; + private final Space targetSpace; + /** The structure with constraint's data. */ + private final Structure data; + /** The ipo object defining influence. */ + private final Ipo ipo; + /** The influence function of this constraint. */ + private final AbstractInfluenceFunction influenceFunction; - private final Space targetSpace; - /** The structure with constraint's data. */ - private final Structure data; - /** The ipo object defining influence. */ - private final Ipo ipo; - /** The influence function of this constraint. */ - private final AbstractInfluenceFunction influenceFunction; + /** + * This constructor creates the constraint instance. + * @param constraintStructure + * the constraint's structure (bConstraint clss in blender 2.49). + * @param influenceFunction + * the constraint's influence function (taken from ConstraintHelper) + * @param boneOMA + * the old memory address of the constraint owner + * @param influenceIpo + * the ipo curve of the influence factor + * @param dataRepository + * the data repository + * @throws BlenderFileException + */ + public Constraint(Structure constraintStructure, AbstractInfluenceFunction influenceFunction, Long boneOMA, Space ownerSpace, Space targetSpace, Ipo influenceIpo, DataRepository dataRepository) throws BlenderFileException { + if (influenceFunction == null) { + throw new IllegalArgumentException("Influence function is not defined!"); + } + Pointer pData = (Pointer) constraintStructure.getFieldValue("data"); + if (!pData.isNull()) { + data = pData.fetchData(dataRepository.getInputStream()).get(0); + } else { + throw new BlenderFileException("The constraint has no data specified!"); + } + this.boneOMA = boneOMA; + this.type = ConstraintType.valueOf(((Number) constraintStructure.getFieldValue("type")).intValue()); + this.name = constraintStructure.getFieldValue("name").toString(); + this.ownerSpace = ownerSpace; + this.targetSpace = targetSpace; + this.ipo = influenceIpo; + this.influenceFunction = influenceFunction; + } - /** - * This constructor creates the constraint instance. - * @param constraintStructure - * the constraint's structure (bConstraint clss in blender 2.49). - * @param influenceFunction - * the constraint's influence function (taken from ConstraintHelper) - * @param boneOMA - * the old memory address of the constraint owner - * @param influenceIpo - * the ipo curve of the influence factor - * @param dataRepository - * the data repository - * @throws BlenderFileException - */ - public Constraint(Structure constraintStructure, AbstractInfluenceFunction influenceFunction, Long boneOMA, Space ownerSpace, Space targetSpace, Ipo influenceIpo, DataRepository dataRepository) throws BlenderFileException { - if(influenceFunction == null) { - throw new IllegalArgumentException("Influence function is not defined!"); - } - Pointer pData = (Pointer)constraintStructure.getFieldValue("data"); - if(!pData.isNull()) { - data = pData.fetchData(dataRepository.getInputStream()).get(0); - } else { - throw new BlenderFileException("The constraint has no data specified!"); - } - this.boneOMA = boneOMA; - this.type = ConstraintType.valueOf(((Number)constraintStructure.getFieldValue("type")).intValue()); - this.name = constraintStructure.getFieldValue("name").toString(); - this.ownerSpace = ownerSpace; - this.targetSpace = targetSpace; - this.ipo = influenceIpo; - this.influenceFunction = influenceFunction; - } + /** + * This method returns the name of the constraint. + * @return the name of the constraint + */ + public String getName() { + return name; + } - /** - * This method returns the name of the constraint. - * @return the name of the constraint - */ - public String getName() { - return name; - } + /** + * This method returns the old memoty address of the bone this constraint affects. + * @return the old memory address of the bone this constraint affects + */ + public Long getBoneOMA() { + return boneOMA; + } - /** - * This method returns the old memoty address of the bone this constraint affects. - * @return the old memory address of the bone this constraint affects - */ - public Long getBoneOMA() { - return boneOMA; - } + /** + * This method returns owner's transform space. + * @return owner's transform space + */ + public Space getOwnerSpace() { + return ownerSpace; + } - /** - * This method returns owner's transform space. - * @return owner's transform space - */ - public Space getOwnerSpace() { - return ownerSpace; - } + /** + * This method returns target's transform space. + * @return target's transform space + */ + public Space getTargetSpace() { + return targetSpace; + } - /** - * This method returns target's transform space. - * @return target's transform space - */ - public Space getTargetSpace() { - return targetSpace; - } + /** + * This method returns the type of the constraint. + * @return the type of the constraint + */ + public ConstraintType getType() { + return type; + } - /** - * This method returns the type of the constraint. - * @return the type of the constraint - */ - public ConstraintType getType() { - return type; - } + /** + * This method returns the constraint's data structure. + * @return the constraint's data structure + */ + public Structure getData() { + return data; + } - /** - * This method returns the constraint's data structure. - * @return the constraint's data structure - */ - public Structure getData() { - return data; - } + /** + * This method returns the constraint's influcence curve. + * @return the constraint's influcence curve + */ + public Ipo getIpo() { + return ipo; + } - /** - * This method returns the constraint's influcence curve. - * @return the constraint's influcence curve - */ - public Ipo getIpo() { - return ipo; - } + /** + * This method affects the bone animation tracks for the given skeleton. + * @param skeleton + * the skeleton containing the affected bones by constraint + * @param boneAnimation + * the bone animation baked traces + * @param constraint + * the constraint + */ + public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation) { + influenceFunction.affectAnimation(skeleton, boneAnimation, this); + } - /** - * This method affects the bone animation tracks for the given skeleton. - * @param skeleton - * the skeleton containing the affected bones by constraint - * @param boneAnimation - * the bone animation baked traces - * @param constraint - * the constraint - */ - public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation) { - influenceFunction.affectAnimation(skeleton, boneAnimation, this); - } + /** + * The space of target or owner transformation. + * @author Marcin Roguski + */ + public static enum Space { - /** - * The space of target or owner transformation. - * @author Marcin Roguski - */ - public static enum Space { - CONSTRAINT_SPACE_WORLD, CONSTRAINT_SPACE_LOCAL, CONSTRAINT_SPACE_POSE, CONSTRAINT_SPACE_PARLOCAL, CONSTRAINT_SPACE_INVALID; + CONSTRAINT_SPACE_WORLD, CONSTRAINT_SPACE_LOCAL, CONSTRAINT_SPACE_POSE, CONSTRAINT_SPACE_PARLOCAL, CONSTRAINT_SPACE_INVALID; - /** - * This method returns the enum instance when given the appropriate value from the blend file. - * @param c - * the blender's value of the space modifier - * @return the scape enum instance - */ - public static Space valueOf(byte c) { - switch(c) { - case 0: - return CONSTRAINT_SPACE_WORLD; - case 1: - return CONSTRAINT_SPACE_LOCAL; - case 2: - return CONSTRAINT_SPACE_POSE; - case 3: - return CONSTRAINT_SPACE_PARLOCAL; - default: - return CONSTRAINT_SPACE_INVALID; - } - } - } + /** + * This method returns the enum instance when given the appropriate value from the blend file. + * @param c + * the blender's value of the space modifier + * @return the scape enum instance + */ + public static Space valueOf(byte c) { + switch (c) { + case 0: + return CONSTRAINT_SPACE_WORLD; + case 1: + return CONSTRAINT_SPACE_LOCAL; + case 2: + return CONSTRAINT_SPACE_POSE; + case 3: + return CONSTRAINT_SPACE_PARLOCAL; + default: + return CONSTRAINT_SPACE_INVALID; + } + } + } } \ No newline at end of file diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/structures/ConstraintType.java b/engine/src/blender/com/jme3/scene/plugins/blender/structures/ConstraintType.java index 0fd23137d..db486b52e 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/structures/ConstraintType.java +++ b/engine/src/blender/com/jme3/scene/plugins/blender/structures/ConstraintType.java @@ -10,134 +10,136 @@ import java.util.Map; * @author Marcin Roguski */ public enum ConstraintType { - /* Invalid/legacy constraint */ - CONSTRAINT_TYPE_NULL(0, "bNullConstraint"), - /* Unimplemented non longer :) - during constraints recode, Aligorith */ - CONSTRAINT_TYPE_CHILDOF(1, "bChildOfConstraint"), - CONSTRAINT_TYPE_KINEMATIC(3, "bKinematicConstraint"), - CONSTRAINT_TYPE_FOLLOWPATH(4, "bFollowPathConstraint"), - /* Unimplemented no longer :) - Aligorith */ - CONSTRAINT_TYPE_ROTLIMIT(5, "bRotLimitConstraint"), - /* Unimplemented no longer :) - Aligorith */ - CONSTRAINT_TYPE_LOCLIMIT(6, "bLocLimitConstraint"), - /* Unimplemented no longer :) - Aligorith */ - CONSTRAINT_TYPE_SIZELIMIT(7, "bSizeLimitConstraint"), - CONSTRAINT_TYPE_ROTLIKE(8, "bRotateLikeConstraint"), - CONSTRAINT_TYPE_LOCLIKE(9, "bLocateLikeConstraint"), - CONSTRAINT_TYPE_SIZELIKE(10, "bSizeLikeConstraint"), - /* Unimplemented no longer :) - Aligorith. Scripts */ - CONSTRAINT_TYPE_PYTHON(11, "bPythonConstraint"), - CONSTRAINT_TYPE_ACTION(12, "bActionConstraint"), - /* New Tracking constraint that locks an axis in place - theeth */ - CONSTRAINT_TYPE_LOCKTRACK(13, "bLockTrackConstraint"), - /* limit distance */ - CONSTRAINT_TYPE_DISTLIMIT(14, "bDistLimitConstraint"), - /* claiming this to be mine :) is in tuhopuu bjornmose */ - CONSTRAINT_TYPE_STRETCHTO(15, "bStretchToConstraint"), - /* floor constraint */ - CONSTRAINT_TYPE_MINMAX(16, "bMinMaxConstraint"), - /* rigidbody constraint */ - CONSTRAINT_TYPE_RIGIDBODYJOINT(17, "bRigidBodyConstraint"), - /* clampto constraint */ - CONSTRAINT_TYPE_CLAMPTO(18, "bClampToConstraint"), - /* transformation (loc/rot/size -> loc/rot/size) constraint */ - CONSTRAINT_TYPE_TRANSFORM(19, "bTransformConstraint"), - /* shrinkwrap (loc/rot) constraint */ - CONSTRAINT_TYPE_SHRINKWRAP(20, "bShrinkwrapConstraint"); + /* Invalid/legacy constraint */ - /** The constraint's id (in blender known as 'type'). */ - private int constraintId; - /** The name of constraint class used by blender. */ - private String className; - /** The map containing class names and types of constraints. */ - private static Map typesMap = new HashMap(ConstraintType.values().length); - /** The map containing class names and types of constraints. */ - private static Map idsMap = new HashMap(ConstraintType.values().length); - static { - idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_NULL.constraintId), CONSTRAINT_TYPE_NULL); - idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_CHILDOF.constraintId), CONSTRAINT_TYPE_CHILDOF); - idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_KINEMATIC.constraintId), CONSTRAINT_TYPE_KINEMATIC); - idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_FOLLOWPATH.constraintId), CONSTRAINT_TYPE_FOLLOWPATH); - idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_ROTLIMIT.constraintId), CONSTRAINT_TYPE_ROTLIMIT); - idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_LOCLIMIT.constraintId), CONSTRAINT_TYPE_LOCLIMIT); - idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_SIZELIMIT.constraintId), CONSTRAINT_TYPE_SIZELIMIT); - idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_ROTLIKE.constraintId), CONSTRAINT_TYPE_ROTLIKE); - idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_LOCLIKE.constraintId), CONSTRAINT_TYPE_LOCLIKE); - idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_SIZELIKE.constraintId), CONSTRAINT_TYPE_SIZELIKE); - idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_PYTHON.constraintId), CONSTRAINT_TYPE_PYTHON); - idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_ACTION.constraintId), CONSTRAINT_TYPE_ACTION); - idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_LOCKTRACK.constraintId), CONSTRAINT_TYPE_LOCKTRACK); - idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_DISTLIMIT.constraintId), CONSTRAINT_TYPE_DISTLIMIT); - idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_STRETCHTO.constraintId), CONSTRAINT_TYPE_STRETCHTO); - idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_MINMAX.constraintId), CONSTRAINT_TYPE_MINMAX); - idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_RIGIDBODYJOINT.constraintId), CONSTRAINT_TYPE_RIGIDBODYJOINT); - idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_CLAMPTO.constraintId), CONSTRAINT_TYPE_CLAMPTO); - idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_TRANSFORM.constraintId), CONSTRAINT_TYPE_TRANSFORM); - idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_SHRINKWRAP.constraintId), CONSTRAINT_TYPE_SHRINKWRAP); - } - /** - * Constructor. Stores constraint type and class name. - * @param constraintId - * the constraint's type - * @param className - * the constraint's type name - */ - private ConstraintType(int constraintId, String className) { - this.constraintId = constraintId; - this.className = className; - } + CONSTRAINT_TYPE_NULL(0, "bNullConstraint"), + /* Unimplemented non longer :) - during constraints recode, Aligorith */ + CONSTRAINT_TYPE_CHILDOF(1, "bChildOfConstraint"), + CONSTRAINT_TYPE_KINEMATIC(3, "bKinematicConstraint"), + CONSTRAINT_TYPE_FOLLOWPATH(4, "bFollowPathConstraint"), + /* Unimplemented no longer :) - Aligorith */ + CONSTRAINT_TYPE_ROTLIMIT(5, "bRotLimitConstraint"), + /* Unimplemented no longer :) - Aligorith */ + CONSTRAINT_TYPE_LOCLIMIT(6, "bLocLimitConstraint"), + /* Unimplemented no longer :) - Aligorith */ + CONSTRAINT_TYPE_SIZELIMIT(7, "bSizeLimitConstraint"), + CONSTRAINT_TYPE_ROTLIKE(8, "bRotateLikeConstraint"), + CONSTRAINT_TYPE_LOCLIKE(9, "bLocateLikeConstraint"), + CONSTRAINT_TYPE_SIZELIKE(10, "bSizeLikeConstraint"), + /* Unimplemented no longer :) - Aligorith. Scripts */ + CONSTRAINT_TYPE_PYTHON(11, "bPythonConstraint"), + CONSTRAINT_TYPE_ACTION(12, "bActionConstraint"), + /* New Tracking constraint that locks an axis in place - theeth */ + CONSTRAINT_TYPE_LOCKTRACK(13, "bLockTrackConstraint"), + /* limit distance */ + CONSTRAINT_TYPE_DISTLIMIT(14, "bDistLimitConstraint"), + /* claiming this to be mine :) is in tuhopuu bjornmose */ + CONSTRAINT_TYPE_STRETCHTO(15, "bStretchToConstraint"), + /* floor constraint */ + CONSTRAINT_TYPE_MINMAX(16, "bMinMaxConstraint"), + /* rigidbody constraint */ + CONSTRAINT_TYPE_RIGIDBODYJOINT(17, "bRigidBodyConstraint"), + /* clampto constraint */ + CONSTRAINT_TYPE_CLAMPTO(18, "bClampToConstraint"), + /* transformation (loc/rot/size -> loc/rot/size) constraint */ + CONSTRAINT_TYPE_TRANSFORM(19, "bTransformConstraint"), + /* shrinkwrap (loc/rot) constraint */ + CONSTRAINT_TYPE_SHRINKWRAP(20, "bShrinkwrapConstraint"); + /** The constraint's id (in blender known as 'type'). */ + private int constraintId; + /** The name of constraint class used by blender. */ + private String className; + /** The map containing class names and types of constraints. */ + private static final Map typesMap = new HashMap(ConstraintType.values().length); + /** The map containing class names and types of constraints. */ + private static final Map idsMap = new HashMap(ConstraintType.values().length); - /** - * This method returns the type by given constraint id. - * @param constraintId - * the id of the constraint - * @return the constraint type enum value - */ - public static ConstraintType valueOf(int constraintId) { - return idsMap.get(Integer.valueOf(constraintId)); - } + static { + idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_NULL.constraintId), CONSTRAINT_TYPE_NULL); + idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_CHILDOF.constraintId), CONSTRAINT_TYPE_CHILDOF); + idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_KINEMATIC.constraintId), CONSTRAINT_TYPE_KINEMATIC); + idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_FOLLOWPATH.constraintId), CONSTRAINT_TYPE_FOLLOWPATH); + idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_ROTLIMIT.constraintId), CONSTRAINT_TYPE_ROTLIMIT); + idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_LOCLIMIT.constraintId), CONSTRAINT_TYPE_LOCLIMIT); + idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_SIZELIMIT.constraintId), CONSTRAINT_TYPE_SIZELIMIT); + idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_ROTLIKE.constraintId), CONSTRAINT_TYPE_ROTLIKE); + idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_LOCLIKE.constraintId), CONSTRAINT_TYPE_LOCLIKE); + idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_SIZELIKE.constraintId), CONSTRAINT_TYPE_SIZELIKE); + idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_PYTHON.constraintId), CONSTRAINT_TYPE_PYTHON); + idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_ACTION.constraintId), CONSTRAINT_TYPE_ACTION); + idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_LOCKTRACK.constraintId), CONSTRAINT_TYPE_LOCKTRACK); + idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_DISTLIMIT.constraintId), CONSTRAINT_TYPE_DISTLIMIT); + idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_STRETCHTO.constraintId), CONSTRAINT_TYPE_STRETCHTO); + idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_MINMAX.constraintId), CONSTRAINT_TYPE_MINMAX); + idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_RIGIDBODYJOINT.constraintId), CONSTRAINT_TYPE_RIGIDBODYJOINT); + idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_CLAMPTO.constraintId), CONSTRAINT_TYPE_CLAMPTO); + idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_TRANSFORM.constraintId), CONSTRAINT_TYPE_TRANSFORM); + idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_SHRINKWRAP.constraintId), CONSTRAINT_TYPE_SHRINKWRAP); + } - /** - * This method returns the constraint's id (type). - * @return the constraint's id (type) - */ - public int getConstraintId() { - return constraintId; - } + /** + * Constructor. Stores constraint type and class name. + * @param constraintId + * the constraint's type + * @param className + * the constraint's type name + */ + private ConstraintType(int constraintId, String className) { + this.constraintId = constraintId; + this.className = className; + } - /** - * This method returns the constraint's class name. - * @return the constraint's class name - */ - public String getClassName() { - return className; - } + /** + * This method returns the type by given constraint id. + * @param constraintId + * the id of the constraint + * @return the constraint type enum value + */ + public static ConstraintType valueOf(int constraintId) { + return idsMap.get(Integer.valueOf(constraintId)); + } - /** - * This method returns constraint enum type by the given class name. - * @param className - * the blender's constraint class name - * @return the constraint enum type of the specified class name - */ - public static ConstraintType getByBlenderClassName(String className) { - ConstraintType result = typesMap.get(className); - if(result == null) { - ConstraintType[] constraints = ConstraintType.values(); - for(ConstraintType constraint : constraints) { - if(constraint.className.equals(className)) { - return constraint; - } - } - } - return result; - } + /** + * This method returns the constraint's id (type). + * @return the constraint's id (type) + */ + public int getConstraintId() { + return constraintId; + } - /** - * This method returns the type value of the last defined constraint. It can be used for allocating tables for - * storing constraint procedures since not all type values from 0 to the last value are used. - * @return the type value of the last defined constraint - */ - public static int getLastDefinedTypeValue() { - return CONSTRAINT_TYPE_SHRINKWRAP.getConstraintId(); - } + /** + * This method returns the constraint's class name. + * @return the constraint's class name + */ + public String getClassName() { + return className; + } + + /** + * This method returns constraint enum type by the given class name. + * @param className + * the blender's constraint class name + * @return the constraint enum type of the specified class name + */ + public static ConstraintType getByBlenderClassName(String className) { + ConstraintType result = typesMap.get(className); + if (result == null) { + ConstraintType[] constraints = ConstraintType.values(); + for (ConstraintType constraint : constraints) { + if (constraint.className.equals(className)) { + return constraint; + } + } + } + return result; + } + + /** + * This method returns the type value of the last defined constraint. It can be used for allocating tables for + * storing constraint procedures since not all type values from 0 to the last value are used. + * @return the type value of the last defined constraint + */ + public static int getLastDefinedTypeValue() { + return CONSTRAINT_TYPE_SHRINKWRAP.getConstraintId(); + } } diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/structures/Ipo.java b/engine/src/blender/com/jme3/scene/plugins/blender/structures/Ipo.java index d75e5d79a..d098774d1 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/structures/Ipo.java +++ b/engine/src/blender/com/jme3/scene/plugins/blender/structures/Ipo.java @@ -10,167 +10,167 @@ import com.jme3.math.Vector3f; * @author Marcin Roguski */ public class Ipo { - public static final int AC_LOC_X = 1; - public static final int AC_LOC_Y = 2; - public static final int AC_LOC_Z = 3; - public static final int OB_ROT_X = 7; - public static final int OB_ROT_Y = 8; - public static final int OB_ROT_Z = 9; - public static final int AC_SIZE_X = 13; - public static final int AC_SIZE_Y = 14; - public static final int AC_SIZE_Z = 15; - public static final int AC_QUAT_W = 25; - public static final int AC_QUAT_X = 26; - public static final int AC_QUAT_Y = 27; - public static final int AC_QUAT_Z = 28; - - /** A list of bezier curves for this interpolation object. */ - private BezierCurve[] bezierCurves; - /** Each ipo contains one bone track. */ - private BoneTrack calculatedTrack; - /** - * Constructor. Stores the bezier curves. - * @param bezierCurves - * a table of bezier curves - */ - public Ipo(BezierCurve[] bezierCurves) { - this.bezierCurves = bezierCurves; - } + public static final int AC_LOC_X = 1; + public static final int AC_LOC_Y = 2; + public static final int AC_LOC_Z = 3; + public static final int OB_ROT_X = 7; + public static final int OB_ROT_Y = 8; + public static final int OB_ROT_Z = 9; + public static final int AC_SIZE_X = 13; + public static final int AC_SIZE_Y = 14; + public static final int AC_SIZE_Z = 15; + public static final int AC_QUAT_W = 25; + public static final int AC_QUAT_X = 26; + public static final int AC_QUAT_Y = 27; + public static final int AC_QUAT_Z = 28; + /** A list of bezier curves for this interpolation object. */ + private BezierCurve[] bezierCurves; + /** Each ipo contains one bone track. */ + private BoneTrack calculatedTrack; - /** - * This method calculates the ipo value for the first curve. - * @param frame - * the frame for which the value is calculated - * @return calculated ipo value - */ - public float calculateValue(int frame) { - return this.calculateValue(frame, 0); - } + /** + * Constructor. Stores the bezier curves. + * @param bezierCurves + * a table of bezier curves + */ + public Ipo(BezierCurve[] bezierCurves) { + this.bezierCurves = bezierCurves; + } - /** - * This method calculates the ipo value for the curve of the specified index. Make sure you do not exceed the - * curves amount. Alway chech the amount of curves before calling this method. - * @param frame - * the frame for which the value is calculated - * @param curveIndex - * the index of the curve - * @return calculated ipo value - */ - public float calculateValue(int frame, int curveIndex) { - return bezierCurves[curveIndex].evaluate(frame, BezierCurve.Y_VALUE); - } + /** + * This method calculates the ipo value for the first curve. + * @param frame + * the frame for which the value is calculated + * @return calculated ipo value + */ + public float calculateValue(int frame) { + return this.calculateValue(frame, 0); + } - /** - * This method returns the curves amount. - * @return the curves amount - */ - public int getCurvesAmount() { - return bezierCurves.length; - } + /** + * This method calculates the ipo value for the curve of the specified index. Make sure you do not exceed the + * curves amount. Alway chech the amount of curves before calling this method. + * @param frame + * the frame for which the value is calculated + * @param curveIndex + * the index of the curve + * @return calculated ipo value + */ + public float calculateValue(int frame, int curveIndex) { + return bezierCurves[curveIndex].evaluate(frame, BezierCurve.Y_VALUE); + } - /** - * This method returns the frame where last bezier triple center point of the specified bezier curve is located. - * @return the frame number of the last defined bezier triple point for the specified ipo - */ - public int getLastFrame() { - int result = 1; - for(int i = 0; i < bezierCurves.length; ++i) { - int tempResult = bezierCurves[i].getLastFrame(); - if(tempResult > result) { - result = tempResult; - } - } - return result; - } - - public void modifyTranslation(int frame, Vector3f translation) { - if(calculatedTrack!=null) { - calculatedTrack.getTranslations()[frame].set(translation); - } - } - - public void modifyRotation(int frame, Quaternion rotation) { - if(calculatedTrack!=null) { - calculatedTrack.getRotations()[frame].set(rotation); - } - } - - public void modifyScale(int frame, Vector3f scale) { - if(calculatedTrack!=null) { - calculatedTrack.getScales()[frame].set(scale); - } - } - - /** - * This method calculates the value of the curves as a bone track between the specified frames. - * @param boneIndex - * the index of the bone for which the method calculates the tracks - * @param startFrame - * the firs frame of tracks (inclusive) - * @param stopFrame - * the last frame of the tracks (inclusive) - * @param fps - * frame rate (frames per second) - * @return bone track for the specified bone - */ - public BoneTrack calculateTrack(int boneIndex, int startFrame, int stopFrame, int fps) { - //preparing data for track - int framesAmount = stopFrame - startFrame; - float start = (startFrame - 1.0f) / fps; - float timeBetweenFrames = 1.0f / fps; + /** + * This method returns the curves amount. + * @return the curves amount + */ + public int getCurvesAmount() { + return bezierCurves.length; + } - float[] times = new float[framesAmount + 1]; - Vector3f[] translations = new Vector3f[framesAmount + 1]; - float[] translation = new float[3]; - Quaternion[] rotations = new Quaternion[framesAmount + 1]; - float[] quaternionRotation = new float[4]; - float[] objectRotation = new float[3]; - boolean bObjectRotation = false; - Vector3f[] scales = new Vector3f[framesAmount + 1]; - float[] scale = new float[3]; + /** + * This method returns the frame where last bezier triple center point of the specified bezier curve is located. + * @return the frame number of the last defined bezier triple point for the specified ipo + */ + public int getLastFrame() { + int result = 1; + for (int i = 0; i < bezierCurves.length; ++i) { + int tempResult = bezierCurves[i].getLastFrame(); + if (tempResult > result) { + result = tempResult; + } + } + return result; + } - //calculating track data - for(int frame = startFrame; frame <= stopFrame; ++frame) { - int index = frame - startFrame; - times[index] = start + (frame - 1) * timeBetweenFrames; - for(int j = 0; j < bezierCurves.length; ++j) { - double value = bezierCurves[j].evaluate(frame, BezierCurve.Y_VALUE); - switch(bezierCurves[j].getType()) { - case AC_LOC_X: - case AC_LOC_Y: - case AC_LOC_Z: - translation[bezierCurves[j].getType() - 1] = (float)value; - break; - case OB_ROT_X: - case OB_ROT_Y: - case OB_ROT_Z: - objectRotation[bezierCurves[j].getType() - 7] = (float)value; - bObjectRotation = true; - break; - case AC_SIZE_X: - case AC_SIZE_Y: - case AC_SIZE_Z: - scale[bezierCurves[j].getType() - 13] = (float)value; - break; - case AC_QUAT_W: - quaternionRotation[3] = (float)value; - break; - case AC_QUAT_X: - case AC_QUAT_Y: - case AC_QUAT_Z: - quaternionRotation[bezierCurves[j].getType() - 26] = (float)value; - break; - default: - //TODO: error? info? warning? - } - } - translations[index] = new Vector3f(translation[0], translation[1], translation[2]); - rotations[index] = bObjectRotation ? new Quaternion().fromAngles(objectRotation) : - new Quaternion(quaternionRotation[0], quaternionRotation[1], quaternionRotation[2], quaternionRotation[3]); - scales[index] = new Vector3f(scale[0], scale[1], scale[2]); - } - calculatedTrack = new BoneTrack(boneIndex, times, translations, rotations, scales); - return calculatedTrack; - } + public void modifyTranslation(int frame, Vector3f translation) { + if (calculatedTrack != null) { + calculatedTrack.getTranslations()[frame].set(translation); + } + } + + public void modifyRotation(int frame, Quaternion rotation) { + if (calculatedTrack != null) { + calculatedTrack.getRotations()[frame].set(rotation); + } + } + + public void modifyScale(int frame, Vector3f scale) { + if (calculatedTrack != null) { + calculatedTrack.getScales()[frame].set(scale); + } + } + + /** + * This method calculates the value of the curves as a bone track between the specified frames. + * @param boneIndex + * the index of the bone for which the method calculates the tracks + * @param startFrame + * the firs frame of tracks (inclusive) + * @param stopFrame + * the last frame of the tracks (inclusive) + * @param fps + * frame rate (frames per second) + * @return bone track for the specified bone + */ + public BoneTrack calculateTrack(int boneIndex, int startFrame, int stopFrame, int fps) { + //preparing data for track + int framesAmount = stopFrame - startFrame; + float start = (startFrame - 1.0f) / fps; + float timeBetweenFrames = 1.0f / fps; + + float[] times = new float[framesAmount + 1]; + Vector3f[] translations = new Vector3f[framesAmount + 1]; + float[] translation = new float[3]; + Quaternion[] rotations = new Quaternion[framesAmount + 1]; + float[] quaternionRotation = new float[4]; + float[] objectRotation = new float[3]; + boolean bObjectRotation = false; + Vector3f[] scales = new Vector3f[framesAmount + 1]; + float[] scale = new float[3]; + + //calculating track data + for (int frame = startFrame; frame <= stopFrame; ++frame) { + int index = frame - startFrame; + times[index] = start + (frame - 1) * timeBetweenFrames; + for (int j = 0; j < bezierCurves.length; ++j) { + double value = bezierCurves[j].evaluate(frame, BezierCurve.Y_VALUE); + switch (bezierCurves[j].getType()) { + case AC_LOC_X: + case AC_LOC_Y: + case AC_LOC_Z: + translation[bezierCurves[j].getType() - 1] = (float) value; + break; + case OB_ROT_X: + case OB_ROT_Y: + case OB_ROT_Z: + objectRotation[bezierCurves[j].getType() - 7] = (float) value; + bObjectRotation = true; + break; + case AC_SIZE_X: + case AC_SIZE_Y: + case AC_SIZE_Z: + scale[bezierCurves[j].getType() - 13] = (float) value; + break; + case AC_QUAT_W: + quaternionRotation[3] = (float) value; + break; + case AC_QUAT_X: + case AC_QUAT_Y: + case AC_QUAT_Z: + quaternionRotation[bezierCurves[j].getType() - 26] = (float) value; + break; + default: + //TODO: error? info? warning? + } + } + translations[index] = new Vector3f(translation[0], translation[1], translation[2]); + rotations[index] = bObjectRotation ? new Quaternion().fromAngles(objectRotation) + : new Quaternion(quaternionRotation[0], quaternionRotation[1], quaternionRotation[2], quaternionRotation[3]); + scales[index] = new Vector3f(scale[0], scale[1], scale[2]); + } + calculatedTrack = new BoneTrack(boneIndex, times, translations, rotations, scales); + return calculatedTrack; + } } \ No newline at end of file diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/structures/Modifier.java b/engine/src/blender/com/jme3/scene/plugins/blender/structures/Modifier.java index f53fddef3..a3184ab64 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/structures/Modifier.java +++ b/engine/src/blender/com/jme3/scene/plugins/blender/structures/Modifier.java @@ -7,50 +7,51 @@ package com.jme3.scene.plugins.blender.structures; * @author Marcin Roguski (Kaelthas) */ public class Modifier { - public static final String ARRAY_MODIFIER_DATA = "ArrayModifierData"; - public static final String ARMATURE_MODIFIER_DATA = "ArmatureModifierData"; - public static final String PARTICLE_MODIFIER_DATA = "ParticleSystemModifierData"; - /** Blender's type of modifier. */ - private String type; - /** JME modifier representation object. */ - private Object jmeModifierRepresentation; - /** Various additional data used by modifiers.*/ - private Object additionalData; - /** - * Constructor. Creates modifier object. - * @param type - * blender's type of modifier - * @param modifier - * JME modifier representation object - */ - public Modifier(String type, Object modifier, Object additionalData) { - this.type = type; - this.jmeModifierRepresentation = modifier; - this.additionalData = additionalData; - } + public static final String ARRAY_MODIFIER_DATA = "ArrayModifierData"; + public static final String ARMATURE_MODIFIER_DATA = "ArmatureModifierData"; + public static final String PARTICLE_MODIFIER_DATA = "ParticleSystemModifierData"; + /** Blender's type of modifier. */ + private String type; + /** JME modifier representation object. */ + private Object jmeModifierRepresentation; + /** Various additional data used by modifiers.*/ + private Object additionalData; - /** - * This method returns JME modifier representation object. - * @return JME modifier representation object - */ - public Object getJmeModifierRepresentation() { - return jmeModifierRepresentation; - } + /** + * Constructor. Creates modifier object. + * @param type + * blender's type of modifier + * @param modifier + * JME modifier representation object + */ + public Modifier(String type, Object modifier, Object additionalData) { + this.type = type; + this.jmeModifierRepresentation = modifier; + this.additionalData = additionalData; + } - /** - * This method returns blender's type of modifier. - * @return blender's type of modifier - */ - public String getType() { - return type; - } - - /** - * This method returns additional data stored in the modifier. - * @return the additional data stored in the modifier - */ - public Object getAdditionalData() { - return additionalData; - } + /** + * This method returns JME modifier representation object. + * @return JME modifier representation object + */ + public Object getJmeModifierRepresentation() { + return jmeModifierRepresentation; + } + + /** + * This method returns blender's type of modifier. + * @return blender's type of modifier + */ + public String getType() { + return type; + } + + /** + * This method returns additional data stored in the modifier. + * @return the additional data stored in the modifier + */ + public Object getAdditionalData() { + return additionalData; + } } \ No newline at end of file diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/utils/AbstractBlenderHelper.java b/engine/src/blender/com/jme3/scene/plugins/blender/utils/AbstractBlenderHelper.java index efa507deb..3bf0f08ab 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/utils/AbstractBlenderHelper.java +++ b/engine/src/blender/com/jme3/scene/plugins/blender/utils/AbstractBlenderHelper.java @@ -42,61 +42,63 @@ import com.jme3.util.BufferUtils; * @author Marcin Roguski */ public abstract class AbstractBlenderHelper { - /** The version of the blend file. */ - protected final int blenderVersion; - /** - * This constructor parses the given blender version and stores the result. Some functionalities may differ in different blender - * versions. - * @param blenderVersion - * the version read from the blend file - */ - public AbstractBlenderHelper(String blenderVersion) { - this.blenderVersion = Integer.parseInt(blenderVersion); - } - - /** - * This method clears the state of the helper so that it can be used for different calculations of another feature. - */ - public void clearState() { } + /** The version of the blend file. */ + protected final int blenderVersion; - /** - * This method should be used to check if the text is blank. Avoid using text.trim().length()==0. This causes that more strings are - * being created and stored in the memory. It can be unwise especially inside loops. - * @param text - * the text to be checked - * @return true if the text is blank and false otherwise - */ - protected boolean isBlank(String text) { - if (text != null) { - for (int i = 0; i < text.length(); ++i) { - if (!Character.isWhitespace(text.charAt(i))) { - return false; - } - } - } - return true; - } - - /** - * Generate a new FloatBuffer using the given array of float[4] objects. The FloatBuffer will be 4 * data.length - * long and contain the vector data as data[0][0], data[0][1], data[0][2], data[0][3], data[1][0]... etc. - * @param data - * list of float[4] objects to place into a new FloatBuffer - */ - protected FloatBuffer createFloatBuffer(List data) { - if(data == null) { - return null; - } - FloatBuffer buff = BufferUtils.createFloatBuffer(4 * data.size()); - for(float[] v : data) { - if(v != null) { - buff.put(v[0]).put(v[1]).put(v[2]).put(v[3]); - } else { - buff.put(0).put(0).put(0).put(0); - } - } - buff.flip(); - return buff; - } + /** + * This constructor parses the given blender version and stores the result. Some functionalities may differ in different blender + * versions. + * @param blenderVersion + * the version read from the blend file + */ + public AbstractBlenderHelper(String blenderVersion) { + this.blenderVersion = Integer.parseInt(blenderVersion); + } + + /** + * This method clears the state of the helper so that it can be used for different calculations of another feature. + */ + public void clearState() { + } + + /** + * This method should be used to check if the text is blank. Avoid using text.trim().length()==0. This causes that more strings are + * being created and stored in the memory. It can be unwise especially inside loops. + * @param text + * the text to be checked + * @return true if the text is blank and false otherwise + */ + protected boolean isBlank(String text) { + if (text != null) { + for (int i = 0; i < text.length(); ++i) { + if (!Character.isWhitespace(text.charAt(i))) { + return false; + } + } + } + return true; + } + + /** + * Generate a new FloatBuffer using the given array of float[4] objects. The FloatBuffer will be 4 * data.length + * long and contain the vector data as data[0][0], data[0][1], data[0][2], data[0][3], data[1][0]... etc. + * @param data + * list of float[4] objects to place into a new FloatBuffer + */ + protected FloatBuffer createFloatBuffer(List data) { + if (data == null) { + return null; + } + FloatBuffer buff = BufferUtils.createFloatBuffer(4 * data.size()); + for (float[] v : data) { + if (v != null) { + buff.put(v[0]).put(v[1]).put(v[2]).put(v[3]); + } else { + buff.put(0).put(0).put(0).put(0); + } + } + buff.flip(); + return buff; + } } diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/utils/IBlenderConverter.java b/engine/src/blender/com/jme3/scene/plugins/blender/utils/BlenderConverter.java similarity index 53% rename from engine/src/blender/com/jme3/scene/plugins/blender/utils/IBlenderConverter.java rename to engine/src/blender/com/jme3/scene/plugins/blender/utils/BlenderConverter.java index 59c5d9a8b..0c3180b86 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/utils/IBlenderConverter.java +++ b/engine/src/blender/com/jme3/scene/plugins/blender/utils/BlenderConverter.java @@ -52,58 +52,59 @@ import com.jme3.scene.plugins.blender.exception.BlenderFileException; * the type of material element */ //TODO: ujednolicić wyrzucane wyjątki -public interface IBlenderConverter { - /** - * This method reads converts the given structure into scene. The given structure needs to be filled with the - * appropriate data. - * @param structure - * the structure we read the scene from - * @return the scene feature - */ - NodeType toScene(Structure structure); +public interface BlenderConverter { - /** - * This method reads converts the given structure into camera. The given structure needs to be filled with the - * appropriate data. - * @param structure - * the structure we read the camera from - * @return the camera feature - */ - CameraType toCamera(Structure structure) throws BlenderFileException; + /** + * This method reads converts the given structure into scene. The given structure needs to be filled with the + * appropriate data. + * @param structure + * the structure we read the scene from + * @return the scene feature + */ + NodeType toScene(Structure structure); - /** - * This method reads converts the given structure into light. The given structure needs to be filled with the - * appropriate data. - * @param structure - * the structure we read the light from - * @return the light feature - */ - LightType toLight(Structure structure) throws BlenderFileException; + /** + * This method reads converts the given structure into camera. The given structure needs to be filled with the + * appropriate data. + * @param structure + * the structure we read the camera from + * @return the camera feature + */ + CameraType toCamera(Structure structure) throws BlenderFileException; - /** - * This method reads converts the given structure into objct. The given structure needs to be filled with the - * appropriate data. - * @param structure - * the structure we read the object from - * @return the object feature - */ - ObjectType toObject(Structure structure) throws BlenderFileException; + /** + * This method reads converts the given structure into light. The given structure needs to be filled with the + * appropriate data. + * @param structure + * the structure we read the light from + * @return the light feature + */ + LightType toLight(Structure structure) throws BlenderFileException; - /** - * This method reads converts the given structure into mesh. The given structure needs to be filled with the - * appropriate data. - * @param structure - * the structure we read the mesh from - * @return the mesh feature - */ - MeshType toMesh(Structure structure) throws BlenderFileException; + /** + * This method reads converts the given structure into objct. The given structure needs to be filled with the + * appropriate data. + * @param structure + * the structure we read the object from + * @return the object feature + */ + ObjectType toObject(Structure structure) throws BlenderFileException; - /** - * This method reads converts the given structure into material. The given structure needs to be filled with the - * appropriate data. - * @param structure - * the structure we read the material from - * @return the material feature - */ - MaterialType toMaterial(Structure structure) throws BlenderFileException; + /** + * This method reads converts the given structure into mesh. The given structure needs to be filled with the + * appropriate data. + * @param structure + * the structure we read the mesh from + * @return the mesh feature + */ + MeshType toMesh(Structure structure) throws BlenderFileException; + + /** + * This method reads converts the given structure into material. The given structure needs to be filled with the + * appropriate data. + * @param structure + * the structure we read the material from + * @return the material feature + */ + MaterialType toMaterial(Structure structure) throws BlenderFileException; } \ No newline at end of file diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/utils/BlenderInputStream.java b/engine/src/blender/com/jme3/scene/plugins/blender/utils/BlenderInputStream.java index 73b74daeb..e5fe0a818 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/utils/BlenderInputStream.java +++ b/engine/src/blender/com/jme3/scene/plugins/blender/utils/BlenderInputStream.java @@ -46,337 +46,337 @@ import com.jme3.scene.plugins.blender.exception.BlenderFileException; * @author Marcin Roguski */ public class BlenderInputStream extends InputStream { - private static final Logger LOGGER = Logger.getLogger(BlenderInputStream.class.getName()); - /** The default size of the blender buffer. */ - private static final int DEFAULT_BUFFER_SIZE = 1048576; //1MB - /** The application's asset manager. */ - private AssetManager assetManager; - /** - * Size of a pointer; all pointers in the file are stored in this format. '_' means 4 bytes and '-' means 8 bytes. - */ - private int pointerSize; - /** - * Type of byte ordering used; 'v' means little endian and 'V' means big endian. - */ - private char endianess; - /** Version of Blender the file was created in; '248' means version 2.48. */ - private String versionNumber; - /** The buffer we store the read data to. */ - protected byte[] cachedBuffer; - /** The total size of the stored data. */ - protected int size; - /** The current position of the read cursor. */ - protected int position; + private static final Logger LOGGER = Logger.getLogger(BlenderInputStream.class.getName()); + /** The default size of the blender buffer. */ + private static final int DEFAULT_BUFFER_SIZE = 1048576; //1MB + /** The application's asset manager. */ + private AssetManager assetManager; + /** + * Size of a pointer; all pointers in the file are stored in this format. '_' means 4 bytes and '-' means 8 bytes. + */ + private int pointerSize; + /** + * Type of byte ordering used; 'v' means little endian and 'V' means big endian. + */ + private char endianess; + /** Version of Blender the file was created in; '248' means version 2.48. */ + private String versionNumber; + /** The buffer we store the read data to. */ + protected byte[] cachedBuffer; + /** The total size of the stored data. */ + protected int size; + /** The current position of the read cursor. */ + protected int position; - /** - * Constructor. The input stream is stored and used to read data. - * @param inputStream - * the stream we read data from - * @param assetManager - * the application's asset manager - * @param endianess - * type of byte ordering used; 'v' means little endian and 'V' means big endian - * @throws BlenderFileException - * this exception is thrown if the file header has some invalid data - */ - public BlenderInputStream(InputStream inputStream, AssetManager assetManager) throws BlenderFileException { - this.assetManager = assetManager; - //the size value will canche while reading the file; the available() method cannot be counted on - try { - size = inputStream.available(); - } catch (IOException e) { - size = 0; - } - if(size <= 0) { - size = BlenderInputStream.DEFAULT_BUFFER_SIZE; - } + /** + * Constructor. The input stream is stored and used to read data. + * @param inputStream + * the stream we read data from + * @param assetManager + * the application's asset manager + * @param endianess + * type of byte ordering used; 'v' means little endian and 'V' means big endian + * @throws BlenderFileException + * this exception is thrown if the file header has some invalid data + */ + public BlenderInputStream(InputStream inputStream, AssetManager assetManager) throws BlenderFileException { + this.assetManager = assetManager; + //the size value will canche while reading the file; the available() method cannot be counted on + try { + size = inputStream.available(); + } catch (IOException e) { + size = 0; + } + if (size <= 0) { + size = BlenderInputStream.DEFAULT_BUFFER_SIZE; + } - //buffered input stream is used here for much faster file reading - BufferedInputStream bufferedInputStream; - if(inputStream instanceof BufferedInputStream) { - bufferedInputStream = (BufferedInputStream)inputStream; - } else { - bufferedInputStream = new BufferedInputStream(inputStream); - } + //buffered input stream is used here for much faster file reading + BufferedInputStream bufferedInputStream; + if (inputStream instanceof BufferedInputStream) { + bufferedInputStream = (BufferedInputStream) inputStream; + } else { + bufferedInputStream = new BufferedInputStream(inputStream); + } - try { - this.readStreamToCache(bufferedInputStream); - } catch (IOException e) { - throw new BlenderFileException("Problems occured while caching the file!", e); - } + try { + this.readStreamToCache(bufferedInputStream); + } catch (IOException e) { + throw new BlenderFileException("Problems occured while caching the file!", e); + } - try { - this.readFileHeader(); - } catch(BlenderFileException e) {//the file might be packed, don't panic, try one more time ;) - this.decompressFile(); - this.position = 0; - this.readFileHeader(); - } - } + try { + this.readFileHeader(); + } catch (BlenderFileException e) {//the file might be packed, don't panic, try one more time ;) + this.decompressFile(); + this.position = 0; + this.readFileHeader(); + } + } - /** - * This method reads the whole stream into a buffer. - * @param inputStream - * the stream to read the file data from - * @throws IOException - * an exception is thrown when data read from the stream is invalid or there are problems with i/o - * operations - */ - private void readStreamToCache(InputStream inputStream) throws IOException { - int data = inputStream.read(); - cachedBuffer = new byte[size]; - size = 0;//this will count the actual size - while(data != -1) { - cachedBuffer[size++] = (byte)data; - if(size >= cachedBuffer.length) {//widen the cached array - byte[] newBuffer = new byte[cachedBuffer.length + (cachedBuffer.length >> 1)]; - System.arraycopy(cachedBuffer, 0, newBuffer, 0, cachedBuffer.length); - cachedBuffer = newBuffer; - } - data = inputStream.read(); - } - } + /** + * This method reads the whole stream into a buffer. + * @param inputStream + * the stream to read the file data from + * @throws IOException + * an exception is thrown when data read from the stream is invalid or there are problems with i/o + * operations + */ + private void readStreamToCache(InputStream inputStream) throws IOException { + int data = inputStream.read(); + cachedBuffer = new byte[size]; + size = 0;//this will count the actual size + while (data != -1) { + cachedBuffer[size++] = (byte) data; + if (size >= cachedBuffer.length) {//widen the cached array + byte[] newBuffer = new byte[cachedBuffer.length + (cachedBuffer.length >> 1)]; + System.arraycopy(cachedBuffer, 0, newBuffer, 0, cachedBuffer.length); + cachedBuffer = newBuffer; + } + data = inputStream.read(); + } + } - /** - * This method is used when the blender file is gzipped. It decompresses the data and stores it back into the - * cachedBuffer field. - */ - private void decompressFile() { - GZIPInputStream gis = null; - try { - gis = new GZIPInputStream(new ByteArrayInputStream(cachedBuffer)); - this.readStreamToCache(gis); - } catch (IOException e) { - throw new IllegalStateException("IO errors occured where they should NOT! " + - "The data is already buffered at this point!", e); - } finally { - try { - if(gis!=null) { - gis.close(); - } - } catch(IOException e) { - LOGGER.warning(e.getMessage()); - } - } - } + /** + * This method is used when the blender file is gzipped. It decompresses the data and stores it back into the + * cachedBuffer field. + */ + private void decompressFile() { + GZIPInputStream gis = null; + try { + gis = new GZIPInputStream(new ByteArrayInputStream(cachedBuffer)); + this.readStreamToCache(gis); + } catch (IOException e) { + throw new IllegalStateException("IO errors occured where they should NOT! " + + "The data is already buffered at this point!", e); + } finally { + try { + if (gis != null) { + gis.close(); + } + } catch (IOException e) { + LOGGER.warning(e.getMessage()); + } + } + } - /** - * This method loads the header from the given stream during instance creation. - * @param inputStream - * the stream we read the header from - * @throws BlenderFileException - * this exception is thrown if the file header has some invalid data - */ - private void readFileHeader() throws BlenderFileException { - byte[] identifier = new byte[7]; - int bytesRead = this.readBytes(identifier); - if(bytesRead != 7) { - throw new BlenderFileException("Error reading header identifier. Only " + bytesRead + " bytes read and there should be 7!"); - } - String strIdentifier = new String(identifier); - if(!"BLENDER".equals(strIdentifier)) { - throw new BlenderFileException("Wrong file identifier: " + strIdentifier + "! Should be 'BLENDER'!"); - } - char pointerSizeSign = (char)this.readByte(); - if(pointerSizeSign == '-') { - pointerSize = 8; - } else if(pointerSizeSign == '_') { - pointerSize = 4; - } else { - throw new BlenderFileException("Invalid pointer size character! Should be '_' or '-' and there is: " + pointerSizeSign); - } - endianess = (char)this.readByte(); - if(endianess != 'v' && endianess != 'V') { - throw new BlenderFileException("Unknown endianess value! 'v' or 'V' expected and found: " + endianess); - } - byte[] versionNumber = new byte[3]; - bytesRead = this.readBytes(versionNumber); - if(bytesRead != 3) { - throw new BlenderFileException("Error reading version numberr. Only " + bytesRead + " bytes read and there should be 3!"); - } - this.versionNumber = new String(versionNumber); - } + /** + * This method loads the header from the given stream during instance creation. + * @param inputStream + * the stream we read the header from + * @throws BlenderFileException + * this exception is thrown if the file header has some invalid data + */ + private void readFileHeader() throws BlenderFileException { + byte[] identifier = new byte[7]; + int bytesRead = this.readBytes(identifier); + if (bytesRead != 7) { + throw new BlenderFileException("Error reading header identifier. Only " + bytesRead + " bytes read and there should be 7!"); + } + String strIdentifier = new String(identifier); + if (!"BLENDER".equals(strIdentifier)) { + throw new BlenderFileException("Wrong file identifier: " + strIdentifier + "! Should be 'BLENDER'!"); + } + char pointerSizeSign = (char) this.readByte(); + if (pointerSizeSign == '-') { + pointerSize = 8; + } else if (pointerSizeSign == '_') { + pointerSize = 4; + } else { + throw new BlenderFileException("Invalid pointer size character! Should be '_' or '-' and there is: " + pointerSizeSign); + } + endianess = (char) this.readByte(); + if (endianess != 'v' && endianess != 'V') { + throw new BlenderFileException("Unknown endianess value! 'v' or 'V' expected and found: " + endianess); + } + byte[] versionNumber = new byte[3]; + bytesRead = this.readBytes(versionNumber); + if (bytesRead != 3) { + throw new BlenderFileException("Error reading version numberr. Only " + bytesRead + " bytes read and there should be 3!"); + } + this.versionNumber = new String(versionNumber); + } - @Override - public int read() throws IOException { - return this.readByte(); - } - - /** - * This method reads 1 byte from the stream. - * It works just in the way the read method does. - * It just not throw an exception because at this moment the whole file - * is loaded into buffer, so no need for IOException to be thrown. - * @return a byte from the stream (1 bytes read) - */ - public int readByte() { - return cachedBuffer[position++] & 0xFF; - } - - /** - * This method reads a bytes number big enough to fill the table. - * It does not throw exceptions so it is for internal use only. - * @param bytes - * an array to be filled with data - * @return number of read bytes (a length of array actually) - */ - private int readBytes(byte[] bytes) { - for(int i=0;i 0) { - position += bytesAmount - move; - } - } + /** + * This method returns the blender version number where the file was created. + * @return blender version number + */ + public String getVersionNumber() { + return versionNumber; + } - @Override - public void close() throws IOException { + /** + * This method returns the size of the pointer. + * @return the size of the pointer + */ + public int getPointerSize() { + return pointerSize; + } + + /** + * This method returns the application's asset manager. + * @return the application's asset manager + */ + public AssetManager getAssetManager() { + return assetManager; + } + + /** + * This method aligns cursor position forward to a given amount of bytes. + * @param bytesAmount + * the byte amount to which we aligh the cursor + */ + public void alignPosition(int bytesAmount) { + if (bytesAmount <= 0) { + throw new IllegalArgumentException("Alignment byte number shoulf be positivbe!"); + } + long move = position % bytesAmount; + if (move > 0) { + position += bytesAmount - move; + } + } + + @Override + public void close() throws IOException { // cachedBuffer = null; // size = position = 0; - } + } } diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/utils/DataRepository.java b/engine/src/blender/com/jme3/scene/plugins/blender/utils/DataRepository.java index 83fefab3d..31db11fe6 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/utils/DataRepository.java +++ b/engine/src/blender/com/jme3/scene/plugins/blender/utils/DataRepository.java @@ -54,347 +54,347 @@ import com.jme3.scene.plugins.blender.structures.Modifier; * @author Marcin Roguski */ public class DataRepository { - /** The blender key. */ - private BlenderKey blenderKey; - /** The header of the file block. */ - private DnaBlockData dnaBlockData; - /** The input stream of the blend file. */ - private BlenderInputStream inputStream; - /** The asset manager. */ - private AssetManager assetManager; - /** A map containing the file block headers. The key is the old pointer address. */ - private Map fileBlockHeadersByOma = new HashMap(); - /** A map containing the file block headers. The key is the block code. */ - private Map> fileBlockHeadersByCode = new HashMap>(); - /** - * This map stores the loaded features by their old memory address. The first object in the value table is the - * loaded structure and the second - the structure already converted into proper data. - */ - private Map loadedFeatures = new HashMap(); - /** - * This map stores the loaded features by their name. Only features with ID structure can be stored here. - * The first object in the value table is the - * loaded structure and the second - the structure already converted into proper data. - */ - private Map loadedFeaturesByName = new HashMap(); - /** A stack that hold the parent structure of currently loaded feature. */ - private Stack parentStack = new Stack(); - /** A map storing loaded ipos. The key is the ipo's owner old memory address and the value is the ipo. */ - private Map loadedIpos = new HashMap(); - /** A list of modifiers for the specified object. */ - protected Map> modifiers = new HashMap>(); - /** A map og helpers that perform loading. */ - private Map helpers = new HashMap(); - - /** - * This method sets the blender key. - * @param blenderKey - * the blender key - */ - public void setBlenderKey(BlenderKey blenderKey) { - this.blenderKey = blenderKey; - } - - /** - * This method returns the blender key. - * @return the blender key - */ - public BlenderKey getBlenderKey() { - return blenderKey; - } - - /** - * This method sets the dna block data. - * @param dnaBlockData - * the dna block data - */ - public void setBlockData(DnaBlockData dnaBlockData) { - this.dnaBlockData = dnaBlockData; - } - - /** - * This method returns the dna block data. - * @return the dna block data - */ - public DnaBlockData getDnaBlockData() { - return dnaBlockData; - } - - /** - * This method returns the asset manager. - * @return the asset manager - */ - public AssetManager getAssetManager() { - return assetManager; - } - - /** - * This method sets the asset manager. - * @param assetManager - * the asset manager - */ - public void setAssetManager(AssetManager assetManager) { - this.assetManager = assetManager; - } - - /** - * This method returns the input stream of the blend file. - * @return the input stream of the blend file - */ - public BlenderInputStream getInputStream() { - return inputStream; - } - - /** - * This method sets the input stream of the blend file. - * @param inputStream - * the input stream of the blend file - */ - public void setInputStream(BlenderInputStream inputStream) { - this.inputStream = inputStream; - } - - /** - * This method adds a file block header to the map. Its old memory address is the key. - * @param oldMemoryAddress - * the address of the block header - * @param fileBlockHeader - * the block header to store - */ - public void addFileBlockHeader(Long oldMemoryAddress, FileBlockHeader fileBlockHeader) { - fileBlockHeadersByOma.put(oldMemoryAddress, fileBlockHeader); - List headers = fileBlockHeadersByCode.get(Integer.valueOf(fileBlockHeader.getCode())); - if(headers == null) { - headers = new ArrayList(); - fileBlockHeadersByCode.put(Integer.valueOf(fileBlockHeader.getCode()), headers); - } - headers.add(fileBlockHeader); - } - - /** - * This method returns the block header of a given memory address. If the header is not present then null is - * returned. - * @param oldMemoryAddress - * the address of the block header - * @return loaded header or null if it was not yet loaded - */ - public FileBlockHeader getFileBlock(Long oldMemoryAddress) { - return fileBlockHeadersByOma.get(oldMemoryAddress); - } - - /** - * This method returns a list of file blocks' headers of a specified code. - * @param code - * the code of file blocks - * @return a list of file blocks' headers of a specified code - */ - public List getFileBlocks(Integer code) { - return fileBlockHeadersByCode.get(code); - } - - /** - * This method clears the saved block headers stored in the features map. - */ - public void clearFileBlocks() { - fileBlockHeadersByOma.clear(); - fileBlockHeadersByCode.clear(); - } - - /** - * This method adds a helper instance to the helpers' map. - * @param - * the type of the helper - * @param clazz - * helper's class definition - * @param helper - * the helper instance - */ - public void putHelper(Class clazz, AbstractBlenderHelper helper) { - helpers.put(clazz.getSimpleName(), helper); - } - - @SuppressWarnings("unchecked") - public T getHelper(Class clazz) { - return (T)helpers.get(clazz.getSimpleName()); - } - - - /** - * This method adds a loaded feature to the map. The key is its unique old memory address. - * @param oldMemoryAddress - * the address of the feature - * @param featureName the name of the feature - * @param structure - * the filled structure of the feature - * @param feature - * the feature we want to store - */ - public void addLoadedFeatures(Long oldMemoryAddress, String featureName, Structure structure, Object feature) { - if(oldMemoryAddress == null || structure == null || feature == null) { - throw new IllegalArgumentException("One of the given arguments is null!"); - } - Object[] storedData = new Object[] {structure, feature}; - loadedFeatures.put(oldMemoryAddress, storedData); - if(featureName!=null) { - loadedFeaturesByName.put(featureName, storedData); - } - } - - /** - * This method returns the feature of a given memory address. If the feature is not yet loaded then null is - * returned. - * @param oldMemoryAddress - * the address of the feature - * @param loadedFeatureDataType - * the type of data we want to retreive it can be either filled structure or already converted feature - * @return loaded feature or null if it was not yet loaded - */ - public Object getLoadedFeature(Long oldMemoryAddress, LoadedFeatureDataType loadedFeatureDataType) { - Object[] result = loadedFeatures.get(oldMemoryAddress); - if(result != null) { - return result[loadedFeatureDataType.getIndex()]; - } - return null; - } - - /** - * This method returns the feature of a given name. If the feature is not yet loaded then null is - * returned. - * @param featureName - * the name of the feature - * @param loadedFeatureDataType - * the type of data we want to retreive it can be either filled structure or already converted feature - * @return loaded feature or null if it was not yet loaded - */ - public Object getLoadedFeature(String featureName, LoadedFeatureDataType loadedFeatureDataType) { - Object[] result = loadedFeaturesByName.get(featureName); - if(result != null) { - return result[loadedFeatureDataType.getIndex()]; - } - return null; - } - - /** - * This method clears the saved features stored in the features map. - */ - public void clearLoadedFeatures() { - loadedFeatures.clear(); - } - - /** - * This method adds the structure to the parent stack. - * @param parent - * the structure to be added to the stack - */ - public void pushParent(Structure parent) { - parentStack.push(parent); - } - - /** - * This method removes the structure from the top of the parent's stack. - * @return the structure that was removed from the stack - */ - public Structure popParent() { - try { - return parentStack.pop(); - } catch(EmptyStackException e) { - return null; - } - } - - /** - * This method retreives the structure at the top of the parent's stack but does not remove it. - * @return the structure from the top of the stack - */ - public Structure peekParent() { - try { - return parentStack.peek(); - } catch(EmptyStackException e) { - return null; - } - } - - public void addIpo(Long ownerOMA, Ipo ipo) { - loadedIpos.put(ownerOMA, ipo); - } - - public Ipo removeIpo(Long ownerOma) { - return loadedIpos.remove(ownerOma); - } - - public Ipo getIpo(Long ownerOMA) { - return loadedIpos.get(ownerOMA); - } - - /** - * This method adds a new modifier to the list. - * @param ownerOMA - * the owner's old memory address - * @param modifierType - * the type of the modifier - * @param loadedModifier - * the loaded modifier object - */ - public void addModifier(Long ownerOMA, String modifierType, Object loadedModifier, Object additionalModifierData) { - List objectModifiers = this.modifiers.get(ownerOMA); - if(objectModifiers == null) { - objectModifiers = new ArrayList(); - this.modifiers.put(ownerOMA, objectModifiers); - } - objectModifiers.add(new Modifier(modifierType, loadedModifier, additionalModifierData)); - } - - /** - * This method returns modifiers for the object specified by its old memory address and the modifier type. If no - * modifiers are found - empty list is returned. If the type is null - all modifiers for the object are returned. - * @param objectOMA - * object's old memory address - * @param type - * the type of the modifier - * @return the list of object's modifiers - */ - public List getModifiers(Long objectOMA, String type) { - List result = new ArrayList(); - List readModifiers = modifiers.get(objectOMA); - if(readModifiers != null && readModifiers.size() > 0) { - for(Modifier modifier : readModifiers) { - if(type==null || type.isEmpty() || modifier.getType().equals(type)) { - result.add(modifier); - } - } - } - return result; - } - - /** - * This metod returns the default material. - * @return the default material - */ - public synchronized Material getDefaultMaterial() { - if(blenderKey.getDefaultMaterial() == null) { - Material defaultMaterial = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); - defaultMaterial.setColor("Color", ColorRGBA.DarkGray); - blenderKey.setDefaultMaterial(defaultMaterial); - } - return blenderKey.getDefaultMaterial(); - } - - /** - * This enum defines what loaded data type user wants to retreive. It can be either filled structure or already - * converted data. - * @author Marcin Roguski - */ - public static enum LoadedFeatureDataType { - LOADED_STRUCTURE(0), LOADED_FEATURE(1); - - private int index; - - private LoadedFeatureDataType(int index) { - this.index = index; - } - - public int getIndex() { - return index; - } - } + + /** The blender key. */ + private BlenderKey blenderKey; + /** The header of the file block. */ + private DnaBlockData dnaBlockData; + /** The input stream of the blend file. */ + private BlenderInputStream inputStream; + /** The asset manager. */ + private AssetManager assetManager; + /** A map containing the file block headers. The key is the old pointer address. */ + private Map fileBlockHeadersByOma = new HashMap(); + /** A map containing the file block headers. The key is the block code. */ + private Map> fileBlockHeadersByCode = new HashMap>(); + /** + * This map stores the loaded features by their old memory address. The first object in the value table is the + * loaded structure and the second - the structure already converted into proper data. + */ + private Map loadedFeatures = new HashMap(); + /** + * This map stores the loaded features by their name. Only features with ID structure can be stored here. + * The first object in the value table is the + * loaded structure and the second - the structure already converted into proper data. + */ + private Map loadedFeaturesByName = new HashMap(); + /** A stack that hold the parent structure of currently loaded feature. */ + private Stack parentStack = new Stack(); + /** A map storing loaded ipos. The key is the ipo's owner old memory address and the value is the ipo. */ + private Map loadedIpos = new HashMap(); + /** A list of modifiers for the specified object. */ + protected Map> modifiers = new HashMap>(); + /** A map og helpers that perform loading. */ + private Map helpers = new HashMap(); + + /** + * This method sets the blender key. + * @param blenderKey + * the blender key + */ + public void setBlenderKey(BlenderKey blenderKey) { + this.blenderKey = blenderKey; + } + + /** + * This method returns the blender key. + * @return the blender key + */ + public BlenderKey getBlenderKey() { + return blenderKey; + } + + /** + * This method sets the dna block data. + * @param dnaBlockData + * the dna block data + */ + public void setBlockData(DnaBlockData dnaBlockData) { + this.dnaBlockData = dnaBlockData; + } + + /** + * This method returns the dna block data. + * @return the dna block data + */ + public DnaBlockData getDnaBlockData() { + return dnaBlockData; + } + + /** + * This method returns the asset manager. + * @return the asset manager + */ + public AssetManager getAssetManager() { + return assetManager; + } + + /** + * This method sets the asset manager. + * @param assetManager + * the asset manager + */ + public void setAssetManager(AssetManager assetManager) { + this.assetManager = assetManager; + } + + /** + * This method returns the input stream of the blend file. + * @return the input stream of the blend file + */ + public BlenderInputStream getInputStream() { + return inputStream; + } + + /** + * This method sets the input stream of the blend file. + * @param inputStream + * the input stream of the blend file + */ + public void setInputStream(BlenderInputStream inputStream) { + this.inputStream = inputStream; + } + + /** + * This method adds a file block header to the map. Its old memory address is the key. + * @param oldMemoryAddress + * the address of the block header + * @param fileBlockHeader + * the block header to store + */ + public void addFileBlockHeader(Long oldMemoryAddress, FileBlockHeader fileBlockHeader) { + fileBlockHeadersByOma.put(oldMemoryAddress, fileBlockHeader); + List headers = fileBlockHeadersByCode.get(Integer.valueOf(fileBlockHeader.getCode())); + if (headers == null) { + headers = new ArrayList(); + fileBlockHeadersByCode.put(Integer.valueOf(fileBlockHeader.getCode()), headers); + } + headers.add(fileBlockHeader); + } + + /** + * This method returns the block header of a given memory address. If the header is not present then null is + * returned. + * @param oldMemoryAddress + * the address of the block header + * @return loaded header or null if it was not yet loaded + */ + public FileBlockHeader getFileBlock(Long oldMemoryAddress) { + return fileBlockHeadersByOma.get(oldMemoryAddress); + } + + /** + * This method returns a list of file blocks' headers of a specified code. + * @param code + * the code of file blocks + * @return a list of file blocks' headers of a specified code + */ + public List getFileBlocks(Integer code) { + return fileBlockHeadersByCode.get(code); + } + + /** + * This method clears the saved block headers stored in the features map. + */ + public void clearFileBlocks() { + fileBlockHeadersByOma.clear(); + fileBlockHeadersByCode.clear(); + } + + /** + * This method adds a helper instance to the helpers' map. + * @param + * the type of the helper + * @param clazz + * helper's class definition + * @param helper + * the helper instance + */ + public void putHelper(Class clazz, AbstractBlenderHelper helper) { + helpers.put(clazz.getSimpleName(), helper); + } + + @SuppressWarnings("unchecked") + public T getHelper(Class clazz) { + return (T) helpers.get(clazz.getSimpleName()); + } + + /** + * This method adds a loaded feature to the map. The key is its unique old memory address. + * @param oldMemoryAddress + * the address of the feature + * @param featureName the name of the feature + * @param structure + * the filled structure of the feature + * @param feature + * the feature we want to store + */ + public void addLoadedFeatures(Long oldMemoryAddress, String featureName, Structure structure, Object feature) { + if (oldMemoryAddress == null || structure == null || feature == null) { + throw new IllegalArgumentException("One of the given arguments is null!"); + } + Object[] storedData = new Object[]{structure, feature}; + loadedFeatures.put(oldMemoryAddress, storedData); + if (featureName != null) { + loadedFeaturesByName.put(featureName, storedData); + } + } + + /** + * This method returns the feature of a given memory address. If the feature is not yet loaded then null is + * returned. + * @param oldMemoryAddress + * the address of the feature + * @param loadedFeatureDataType + * the type of data we want to retreive it can be either filled structure or already converted feature + * @return loaded feature or null if it was not yet loaded + */ + public Object getLoadedFeature(Long oldMemoryAddress, LoadedFeatureDataType loadedFeatureDataType) { + Object[] result = loadedFeatures.get(oldMemoryAddress); + if (result != null) { + return result[loadedFeatureDataType.getIndex()]; + } + return null; + } + + /** + * This method returns the feature of a given name. If the feature is not yet loaded then null is + * returned. + * @param featureName + * the name of the feature + * @param loadedFeatureDataType + * the type of data we want to retreive it can be either filled structure or already converted feature + * @return loaded feature or null if it was not yet loaded + */ + public Object getLoadedFeature(String featureName, LoadedFeatureDataType loadedFeatureDataType) { + Object[] result = loadedFeaturesByName.get(featureName); + if (result != null) { + return result[loadedFeatureDataType.getIndex()]; + } + return null; + } + + /** + * This method clears the saved features stored in the features map. + */ + public void clearLoadedFeatures() { + loadedFeatures.clear(); + } + + /** + * This method adds the structure to the parent stack. + * @param parent + * the structure to be added to the stack + */ + public void pushParent(Structure parent) { + parentStack.push(parent); + } + + /** + * This method removes the structure from the top of the parent's stack. + * @return the structure that was removed from the stack + */ + public Structure popParent() { + try { + return parentStack.pop(); + } catch (EmptyStackException e) { + return null; + } + } + + /** + * This method retreives the structure at the top of the parent's stack but does not remove it. + * @return the structure from the top of the stack + */ + public Structure peekParent() { + try { + return parentStack.peek(); + } catch (EmptyStackException e) { + return null; + } + } + + public void addIpo(Long ownerOMA, Ipo ipo) { + loadedIpos.put(ownerOMA, ipo); + } + + public Ipo removeIpo(Long ownerOma) { + return loadedIpos.remove(ownerOma); + } + + public Ipo getIpo(Long ownerOMA) { + return loadedIpos.get(ownerOMA); + } + + /** + * This method adds a new modifier to the list. + * @param ownerOMA + * the owner's old memory address + * @param modifierType + * the type of the modifier + * @param loadedModifier + * the loaded modifier object + */ + public void addModifier(Long ownerOMA, String modifierType, Object loadedModifier, Object additionalModifierData) { + List objectModifiers = this.modifiers.get(ownerOMA); + if (objectModifiers == null) { + objectModifiers = new ArrayList(); + this.modifiers.put(ownerOMA, objectModifiers); + } + objectModifiers.add(new Modifier(modifierType, loadedModifier, additionalModifierData)); + } + + /** + * This method returns modifiers for the object specified by its old memory address and the modifier type. If no + * modifiers are found - empty list is returned. If the type is null - all modifiers for the object are returned. + * @param objectOMA + * object's old memory address + * @param type + * the type of the modifier + * @return the list of object's modifiers + */ + public List getModifiers(Long objectOMA, String type) { + List result = new ArrayList(); + List readModifiers = modifiers.get(objectOMA); + if (readModifiers != null && readModifiers.size() > 0) { + for (Modifier modifier : readModifiers) { + if (type == null || type.isEmpty() || modifier.getType().equals(type)) { + result.add(modifier); + } + } + } + return result; + } + + /** + * This metod returns the default material. + * @return the default material + */ + public synchronized Material getDefaultMaterial() { + if (blenderKey.getDefaultMaterial() == null) { + Material defaultMaterial = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); + defaultMaterial.setColor("Color", ColorRGBA.DarkGray); + blenderKey.setDefaultMaterial(defaultMaterial); + } + return blenderKey.getDefaultMaterial(); + } + + /** + * This enum defines what loaded data type user wants to retreive. It can be either filled structure or already + * converted data. + * @author Marcin Roguski + */ + public static enum LoadedFeatureDataType { + + LOADED_STRUCTURE(0), LOADED_FEATURE(1); + private int index; + + private LoadedFeatureDataType(int index) { + this.index = index; + } + + public int getIndex() { + return index; + } + } } diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/utils/DynamicArray.java b/engine/src/blender/com/jme3/scene/plugins/blender/utils/DynamicArray.java index 845a43409..20ed7e38d 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/utils/DynamicArray.java +++ b/engine/src/blender/com/jme3/scene/plugins/blender/utils/DynamicArray.java @@ -40,117 +40,118 @@ import com.jme3.scene.plugins.blender.exception.BlenderFileException; * the type of stored data in the array */ public class DynamicArray implements Cloneable { - /** An array object that holds the required data. */ - private T[] array; - /** - * This table holds the sizes of dimetions of the dynamic table. It's length specifies the table dimension or a - * pointer level. For example: if tableSizes.length == 3 then it either specifies a dynamic table of fixed lengths: - * dynTable[a][b][c], where a,b,c are stored in the tableSizes table. - */ - private int[] tableSizes; - /** - * Constructor. Builds an empty array of the specified sizes. - * @param tableSizes - * the sizes of the table - * @throws BlenderFileException - * an exception is thrown if one of the sizes is not a positive number - */ - @SuppressWarnings("unchecked") - public DynamicArray(int[] tableSizes) throws BlenderFileException { - this.tableSizes = tableSizes; - int totalSize = 1; - for(int size : tableSizes) { - if(size <= 0) { - throw new BlenderFileException("The size of the table must be positive!"); - } - totalSize *= size; - } - this.array = (T[])new Object[totalSize]; - } + /** An array object that holds the required data. */ + private T[] array; + /** + * This table holds the sizes of dimetions of the dynamic table. It's length specifies the table dimension or a + * pointer level. For example: if tableSizes.length == 3 then it either specifies a dynamic table of fixed lengths: + * dynTable[a][b][c], where a,b,c are stored in the tableSizes table. + */ + private int[] tableSizes; - /** - * Constructor. Builds an empty array of the specified sizes. - * @param tableSizes - * the sizes of the table - * @throws BlenderFileException - * an exception is thrown if one of the sizes is not a positive number - */ - public DynamicArray(int[] tableSizes, T[] data) throws BlenderFileException { - this.tableSizes = tableSizes; - int totalSize = 1; - for(int size : tableSizes) { - if(size <= 0) { - throw new BlenderFileException("The size of the table must be positive!"); - } - totalSize *= size; - } - if(totalSize != data.length) { - throw new IllegalArgumentException("The size of the table does not match the size of the given data!"); - } - this.array = data; - } + /** + * Constructor. Builds an empty array of the specified sizes. + * @param tableSizes + * the sizes of the table + * @throws BlenderFileException + * an exception is thrown if one of the sizes is not a positive number + */ + @SuppressWarnings("unchecked") + public DynamicArray(int[] tableSizes) throws BlenderFileException { + this.tableSizes = tableSizes; + int totalSize = 1; + for (int size : tableSizes) { + if (size <= 0) { + throw new BlenderFileException("The size of the table must be positive!"); + } + totalSize *= size; + } + this.array = (T[]) new Object[totalSize]; + } - @Override - public Object clone() throws CloneNotSupportedException { - return super.clone(); - } + /** + * Constructor. Builds an empty array of the specified sizes. + * @param tableSizes + * the sizes of the table + * @throws BlenderFileException + * an exception is thrown if one of the sizes is not a positive number + */ + public DynamicArray(int[] tableSizes, T[] data) throws BlenderFileException { + this.tableSizes = tableSizes; + int totalSize = 1; + for (int size : tableSizes) { + if (size <= 0) { + throw new BlenderFileException("The size of the table must be positive!"); + } + totalSize *= size; + } + if (totalSize != data.length) { + throw new IllegalArgumentException("The size of the table does not match the size of the given data!"); + } + this.array = data; + } - /** - * This method returns a value on the specified position. The dimension of the table is not taken into - * consideration. - * @param position - * the position of the data - * @return required data - */ - public T get(int position) { - return array[position]; - } + @Override + public Object clone() throws CloneNotSupportedException { + return super.clone(); + } - /** - * This method returns a value on the specified position in multidimensional array. Be careful not to exceed the - * table boundaries. Check the table's dimension first. - * @param position - * the position of the data indices of data position - * @return required data required data - */ - public T get(int... position) { - if(position.length != tableSizes.length) { - throw new ArrayIndexOutOfBoundsException("The table accepts " + tableSizes.length + " indexing number(s)!"); - } - int index = 0; - for(int i = 0; i < position.length - 1; ++i) { - index += position[i] * tableSizes[i + 1]; - } - index += position[position.length - 1]; - return array[index]; - } + /** + * This method returns a value on the specified position. The dimension of the table is not taken into + * consideration. + * @param position + * the position of the data + * @return required data + */ + public T get(int position) { + return array[position]; + } - /** - * This method returns the total amount of data stored in the array. - * @return the total amount of data stored in the array - */ - public int getTotalSize() { - return array.length; - } + /** + * This method returns a value on the specified position in multidimensional array. Be careful not to exceed the + * table boundaries. Check the table's dimension first. + * @param position + * the position of the data indices of data position + * @return required data required data + */ + public T get(int... position) { + if (position.length != tableSizes.length) { + throw new ArrayIndexOutOfBoundsException("The table accepts " + tableSizes.length + " indexing number(s)!"); + } + int index = 0; + for (int i = 0; i < position.length - 1; ++i) { + index += position[i] * tableSizes[i + 1]; + } + index += position[position.length - 1]; + return array[index]; + } - @Override - public String toString() { - StringBuilder result = new StringBuilder(); - if(array instanceof Character[]) {//in case of character array we convert it to String - for(int i = 0; i < array.length && (Character)array[i] != '\0'; ++i) {//strings are terminater with '0' - result.append(array[i]); - } - } else { - result.append('['); - for(int i = 0; i < array.length; ++i) { - result.append(array[i].toString()); - if(i + 1 < array.length) { - result.append(','); - } - } - result.append(']'); - } - return result.toString(); - } + /** + * This method returns the total amount of data stored in the array. + * @return the total amount of data stored in the array + */ + public int getTotalSize() { + return array.length; + } + + @Override + public String toString() { + StringBuilder result = new StringBuilder(); + if (array instanceof Character[]) {//in case of character array we convert it to String + for (int i = 0; i < array.length && (Character) array[i] != '\0'; ++i) {//strings are terminater with '0' + result.append(array[i]); + } + } else { + result.append('['); + for (int i = 0; i < array.length; ++i) { + result.append(array[i].toString()); + if (i + 1 < array.length) { + result.append(','); + } + } + result.append(']'); + } + return result.toString(); + } } \ No newline at end of file diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/utils/JmeConverter.java b/engine/src/blender/com/jme3/scene/plugins/blender/utils/JmeConverter.java index 7b60f4ee6..f7c7af49f 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/utils/JmeConverter.java +++ b/engine/src/blender/com/jme3/scene/plugins/blender/utils/JmeConverter.java @@ -55,115 +55,115 @@ import com.jme3.scene.plugins.blender.helpers.ObjectHelper; * This class converts blender file blocks into jMonkeyEngine data structures. * @author Marcin Roguski */ -public class JmeConverter implements IBlenderConverter, Material> { - private static final Logger LOGGER = Logger.getLogger(JmeConverter.class.getName()); +public class JmeConverter implements BlenderConverter, Material> { - private final DataRepository dataRepository; + private static final Logger LOGGER = Logger.getLogger(JmeConverter.class.getName()); + private final DataRepository dataRepository; - /** - * Constructor. Creates the loader and checks if the given data is correct. - * @param dataRepository - * the data repository; it should have the following field set: - asset manager - blender key - dna block - * data - blender input stream Otherwise IllegalArgumentException will be thrown. - * @param featuresToLoad - * bitwise flag describing what features are to be loaded - * @see FeaturesToLoad FeaturesToLoad - */ - public JmeConverter(DataRepository dataRepository) { - //validating the given data first - if(dataRepository.getAssetManager() == null) { - throw new IllegalArgumentException("Cannot find asset manager!"); - } - if(dataRepository.getBlenderKey() == null) { - throw new IllegalArgumentException("Cannot find blender key!"); - } - if(dataRepository.getDnaBlockData() == null) { - throw new IllegalArgumentException("Cannot find dna block!"); - } - if(dataRepository.getInputStream() == null) { - throw new IllegalArgumentException("Cannot find blender file stream!"); - } - this.dataRepository = dataRepository; - } + /** + * Constructor. Creates the loader and checks if the given data is correct. + * @param dataRepository + * the data repository; it should have the following field set: - asset manager - blender key - dna block + * data - blender input stream Otherwise IllegalArgumentException will be thrown. + * @param featuresToLoad + * bitwise flag describing what features are to be loaded + * @see FeaturesToLoad FeaturesToLoad + */ + public JmeConverter(DataRepository dataRepository) { + //validating the given data first + if (dataRepository.getAssetManager() == null) { + throw new IllegalArgumentException("Cannot find asset manager!"); + } + if (dataRepository.getBlenderKey() == null) { + throw new IllegalArgumentException("Cannot find blender key!"); + } + if (dataRepository.getDnaBlockData() == null) { + throw new IllegalArgumentException("Cannot find dna block!"); + } + if (dataRepository.getInputStream() == null) { + throw new IllegalArgumentException("Cannot find blender file stream!"); + } + this.dataRepository = dataRepository; + } - @Override - public Node toScene(Structure structure) {//TODO: poprawny import sceny - if((dataRepository.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.SCENES) == 0) { - return null; - } - Structure id = (Structure)structure.getFieldValue("id"); - String sceneName = id.getFieldValue("name").toString(); - - //veryfying layers to be loaded - if(dataRepository.getBlenderKey().getLayersToLoad()<0) { - int lay = ((Number)structure.getFieldValue("lay")).intValue(); - dataRepository.getBlenderKey().setLayersToLoad(lay);//load only current layer - } - return new Node(sceneName); - } + @Override + public Node toScene(Structure structure) {//TODO: poprawny import sceny + if ((dataRepository.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.SCENES) == 0) { + return null; + } + Structure id = (Structure) structure.getFieldValue("id"); + String sceneName = id.getFieldValue("name").toString(); - @Override - public Camera toCamera(Structure structure) throws BlenderFileException { - if((dataRepository.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.CAMERAS) == 0) { - return null; - } - CameraHelper cameraHelper = dataRepository.getHelper(CameraHelper.class); - return cameraHelper.toCamera(structure); - } + //veryfying layers to be loaded + if (dataRepository.getBlenderKey().getLayersToLoad() < 0) { + int lay = ((Number) structure.getFieldValue("lay")).intValue(); + dataRepository.getBlenderKey().setLayersToLoad(lay);//load only current layer + } + return new Node(sceneName); + } - @Override - public Light toLight(Structure structure) throws BlenderFileException { - if((dataRepository.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.LIGHTS) == 0) { - return null; - } - LightHelper lightHelper = dataRepository.getHelper(LightHelper.class); - return lightHelper.toLight(structure, dataRepository); - } + @Override + public Camera toCamera(Structure structure) throws BlenderFileException { + if ((dataRepository.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.CAMERAS) == 0) { + return null; + } + CameraHelper cameraHelper = dataRepository.getHelper(CameraHelper.class); + return cameraHelper.toCamera(structure); + } - @Override - public Object toObject(Structure structure) throws BlenderFileException { - int lay = ((Number)structure.getFieldValue("lay")).intValue(); - if((lay & dataRepository.getBlenderKey().getLayersToLoad()) == 0 || - (dataRepository.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.OBJECTS) == 0) { - return null; - } - ObjectHelper objectHelper = dataRepository.getHelper(ObjectHelper.class); - return objectHelper.toObject(structure, dataRepository); - } + @Override + public Light toLight(Structure structure) throws BlenderFileException { + if ((dataRepository.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.LIGHTS) == 0) { + return null; + } + LightHelper lightHelper = dataRepository.getHelper(LightHelper.class); + return lightHelper.toLight(structure, dataRepository); + } - @Override - public List toMesh(Structure structure) throws BlenderFileException { - MeshHelper meshHelper = dataRepository.getHelper(MeshHelper.class); - return meshHelper.toMesh(structure, dataRepository); - } + @Override + public Object toObject(Structure structure) throws BlenderFileException { + int lay = ((Number) structure.getFieldValue("lay")).intValue(); + if ((lay & dataRepository.getBlenderKey().getLayersToLoad()) == 0 + || (dataRepository.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.OBJECTS) == 0) { + return null; + } + ObjectHelper objectHelper = dataRepository.getHelper(ObjectHelper.class); + return objectHelper.toObject(structure, dataRepository); + } - @Override - public Material toMaterial(Structure structure) throws BlenderFileException { - if((dataRepository.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.MATERIALS) == 0) { - return null; - } - MaterialHelper materialHelper = dataRepository.getHelper(MaterialHelper.class); - return materialHelper.toMaterial(structure, dataRepository); - } + @Override + public List toMesh(Structure structure) throws BlenderFileException { + MeshHelper meshHelper = dataRepository.getHelper(MeshHelper.class); + return meshHelper.toMesh(structure, dataRepository); + } - /** - * This method returns the data read from the WORLD file block. The block contains data that can be stored as - * separate jme features and therefore cannot be returned as a single jME scene feature. - * @param structure - * the structure with WORLD block data - * @return data read from the WORLD block that can be added to the scene - */ - public WorldData toWorldData(Structure structure) { - WorldData result = new WorldData(); + @Override + public Material toMaterial(Structure structure) throws BlenderFileException { + if ((dataRepository.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.MATERIALS) == 0) { + return null; + } + MaterialHelper materialHelper = dataRepository.getHelper(MaterialHelper.class); + return materialHelper.toMaterial(structure, dataRepository); + } - //reading ambient light - AmbientLight ambientLight = new AmbientLight(); - float ambr = ((Number)structure.getFieldValue("ambr")).floatValue(); - float ambg = ((Number)structure.getFieldValue("ambg")).floatValue(); - float ambb = ((Number)structure.getFieldValue("ambb")).floatValue(); - ambientLight.setColor(new ColorRGBA(ambr, ambg, ambb, 0.0f)); - result.setAmbientLight(ambientLight); + /** + * This method returns the data read from the WORLD file block. The block contains data that can be stored as + * separate jme features and therefore cannot be returned as a single jME scene feature. + * @param structure + * the structure with WORLD block data + * @return data read from the WORLD block that can be added to the scene + */ + public WorldData toWorldData(Structure structure) { + WorldData result = new WorldData(); - return result; - } + //reading ambient light + AmbientLight ambientLight = new AmbientLight(); + float ambr = ((Number) structure.getFieldValue("ambr")).floatValue(); + float ambg = ((Number) structure.getFieldValue("ambg")).floatValue(); + float ambb = ((Number) structure.getFieldValue("ambb")).floatValue(); + ambientLight.setColor(new ColorRGBA(ambr, ambg, ambb, 0.0f)); + result.setAmbientLight(ambientLight); + + return result; + } } diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/utils/Pointer.java b/engine/src/blender/com/jme3/scene/plugins/blender/utils/Pointer.java index dd82c6f35..fd0484348 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/utils/Pointer.java +++ b/engine/src/blender/com/jme3/scene/plugins/blender/utils/Pointer.java @@ -43,133 +43,134 @@ import com.jme3.scene.plugins.blender.exception.BlenderFileException; * @author Marcin Roguski */ public class Pointer { - /** The data repository. */ - private DataRepository dataRepository; - /** The level of the pointer. */ - private int pointerLevel; - /** The address in file it points to. */ - private long oldMemoryAddress; - /** This variable indicates if the field is a function pointer. */ - public boolean function; - /** - * Constructr. Stores the basic data about the pointer. - * @param pointerLevel - * the level of the pointer - * @param function - * this variable indicates if the field is a function pointer - * @param dataRepository - * the repository f data; used in fetching the value that the pointer points - */ - public Pointer(int pointerLevel, boolean function, DataRepository dataRepository) { - this.pointerLevel = pointerLevel; - this.function = function; - this.dataRepository = dataRepository; - } + /** The data repository. */ + private DataRepository dataRepository; + /** The level of the pointer. */ + private int pointerLevel; + /** The address in file it points to. */ + private long oldMemoryAddress; + /** This variable indicates if the field is a function pointer. */ + public boolean function; - /** - * This method fills the pointer with its address value (it doesn't get the actual data yet. Use the 'fetch' method - * for this. - * @param inputStream - * the stream we read the pointer value from - */ - public void fill(BlenderInputStream inputStream) { - oldMemoryAddress = inputStream.readPointer(); - } + /** + * Constructr. Stores the basic data about the pointer. + * @param pointerLevel + * the level of the pointer + * @param function + * this variable indicates if the field is a function pointer + * @param dataRepository + * the repository f data; used in fetching the value that the pointer points + */ + public Pointer(int pointerLevel, boolean function, DataRepository dataRepository) { + this.pointerLevel = pointerLevel; + this.function = function; + this.dataRepository = dataRepository; + } - /** - * This method fetches the data stored under the given address. - * @param inputStream - * the stream we read data from - * @param dataIndices - * the offset of the data in the table pointed by the pointer - * @return the data read from the file - * @throws BlenderFileException - * this exception is thrown when the blend file structure is somehow invalid or corrupted - */ - public List fetchData(BlenderInputStream inputStream) throws BlenderFileException { - if(oldMemoryAddress == 0) { - throw new NullPointerException("The pointer points to nothing!"); - } - List structures = null; - FileBlockHeader dataFileBlock = dataRepository.getFileBlock(oldMemoryAddress); - if(pointerLevel > 1) { - int pointersAmount = dataFileBlock.getSize() / inputStream.getPointerSize() * dataFileBlock.getCount(); - for(int i = 0; i < pointersAmount; ++i) { - inputStream.setPosition(dataFileBlock.getBlockPosition() + inputStream.getPointerSize() * i); - long oldMemoryAddress = inputStream.readPointer(); - if(oldMemoryAddress != 0L) { - Pointer p = new Pointer(pointerLevel - 1, this.function, dataRepository); - p.oldMemoryAddress = oldMemoryAddress; - if(structures == null) { - structures = p.fetchData(inputStream); - } else { - structures.addAll(p.fetchData(inputStream)); - } - } - } - } else { - inputStream.setPosition(dataFileBlock.getBlockPosition()); - structures = new ArrayList(dataFileBlock.getCount()); - for(int i = 0; i < dataFileBlock.getCount(); ++i) { - Structure structure = dataRepository.getDnaBlockData().getStructure(dataFileBlock.getSdnaIndex()); - structure.fill(inputStream); - structures.add(structure); - } - return structures; - } - return structures; - } + /** + * This method fills the pointer with its address value (it doesn't get the actual data yet. Use the 'fetch' method + * for this. + * @param inputStream + * the stream we read the pointer value from + */ + public void fill(BlenderInputStream inputStream) { + oldMemoryAddress = inputStream.readPointer(); + } - /** - * This method indicates if this pointer points to a function. - * @return true if this is a function pointer and false otherwise - */ - public boolean isFunction() { - return function; - } + /** + * This method fetches the data stored under the given address. + * @param inputStream + * the stream we read data from + * @param dataIndices + * the offset of the data in the table pointed by the pointer + * @return the data read from the file + * @throws BlenderFileException + * this exception is thrown when the blend file structure is somehow invalid or corrupted + */ + public List fetchData(BlenderInputStream inputStream) throws BlenderFileException { + if (oldMemoryAddress == 0) { + throw new NullPointerException("The pointer points to nothing!"); + } + List structures = null; + FileBlockHeader dataFileBlock = dataRepository.getFileBlock(oldMemoryAddress); + if (pointerLevel > 1) { + int pointersAmount = dataFileBlock.getSize() / inputStream.getPointerSize() * dataFileBlock.getCount(); + for (int i = 0; i < pointersAmount; ++i) { + inputStream.setPosition(dataFileBlock.getBlockPosition() + inputStream.getPointerSize() * i); + long oldMemoryAddress = inputStream.readPointer(); + if (oldMemoryAddress != 0L) { + Pointer p = new Pointer(pointerLevel - 1, this.function, dataRepository); + p.oldMemoryAddress = oldMemoryAddress; + if (structures == null) { + structures = p.fetchData(inputStream); + } else { + structures.addAll(p.fetchData(inputStream)); + } + } + } + } else { + inputStream.setPosition(dataFileBlock.getBlockPosition()); + structures = new ArrayList(dataFileBlock.getCount()); + for (int i = 0; i < dataFileBlock.getCount(); ++i) { + Structure structure = dataRepository.getDnaBlockData().getStructure(dataFileBlock.getSdnaIndex()); + structure.fill(inputStream); + structures.add(structure); + } + return structures; + } + return structures; + } - /** - * This method indicates if this is a null-pointer or not. - * @return true if the pointer is null and false otherwise - */ - public boolean isNull() { - return oldMemoryAddress == 0; - } + /** + * This method indicates if this pointer points to a function. + * @return true if this is a function pointer and false otherwise + */ + public boolean isFunction() { + return function; + } - /** - * This method returns the old memory address of the structure pointed by the pointer. - * @return the old memory address of the structure pointed by the pointer - */ - public long getOldMemoryAddress() { - return oldMemoryAddress; - } + /** + * This method indicates if this is a null-pointer or not. + * @return true if the pointer is null and false otherwise + */ + public boolean isNull() { + return oldMemoryAddress == 0; + } - @Override - public String toString() { - return oldMemoryAddress == 0 ? "{$null$}" : "{$" + oldMemoryAddress + "$}"; - } + /** + * This method returns the old memory address of the structure pointed by the pointer. + * @return the old memory address of the structure pointed by the pointer + */ + public long getOldMemoryAddress() { + return oldMemoryAddress; + } - @Override - public int hashCode() { - return 31 + (int)(oldMemoryAddress ^ oldMemoryAddress >>> 32); - } + @Override + public String toString() { + return oldMemoryAddress == 0 ? "{$null$}" : "{$" + oldMemoryAddress + "$}"; + } - @Override - public boolean equals(Object obj) { - if(this == obj) { - return true; - } - if(obj == null) { - return false; - } - if(this.getClass() != obj.getClass()) { - return false; - } - Pointer other = (Pointer)obj; - if(oldMemoryAddress != other.oldMemoryAddress) { - return false; - } - return true; - } + @Override + public int hashCode() { + return 31 + (int) (oldMemoryAddress ^ oldMemoryAddress >>> 32); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (this.getClass() != obj.getClass()) { + return false; + } + Pointer other = (Pointer) obj; + if (oldMemoryAddress != other.oldMemoryAddress) { + return false; + } + return true; + } } diff --git a/engine/src/core/com/jme3/animation/Bone.java b/engine/src/core/com/jme3/animation/Bone.java index f5059eb30..30e1b5331 100644 --- a/engine/src/core/com/jme3/animation/Bone.java +++ b/engine/src/core/com/jme3/animation/Bone.java @@ -347,7 +347,6 @@ public final class Bone implements Savable { /** * 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) { @@ -378,10 +377,9 @@ public final class Bone implements Savable { } /** - * Returns teh local transform of this bone combined with the given position and rotation + * Returns the local transform of this bone combined with the given position and rotation * @param position a position * @param rotation a rotation - * @return */ public Transform getCombinedTransform(Vector3f position, Quaternion rotation){ rotation.mult(localPos, tmpTransform.getTranslation()).addLocal(position); diff --git a/engine/src/core/com/jme3/animation/CompactArray.java b/engine/src/core/com/jme3/animation/CompactArray.java index cf24844b5..686d25352 100644 --- a/engine/src/core/com/jme3/animation/CompactArray.java +++ b/engine/src/core/com/jme3/animation/CompactArray.java @@ -234,13 +234,11 @@ public abstract class CompactArray { * deserialize object * @param compactIndex compacted object index * @param store - * @return */ protected abstract T deserialize(int compactIndex, T store); /** * serialized size of one object element - * @return */ protected abstract int getTupleSize(); diff --git a/engine/src/core/com/jme3/app/package.html b/engine/src/core/com/jme3/app/package.html index 1f6e68d12..472ef6142 100644 --- a/engine/src/core/com/jme3/app/package.html +++ b/engine/src/core/com/jme3/app/package.html @@ -10,7 +10,7 @@ The com.jme3.application 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.
+implemented to provide functionality for the main loop.

An Application will typically provide the following services:

    @@ -37,42 +37,42 @@ An Application will typically provide the following services:

    Usage

    -An example use of the Application class is as follows
    -
    +An example use of the Application class is as follows
    +
    -public class ExampleUse extends Application {
    -
    - private Node rootNode = new Node("Root Node");
    -
    - public static void main(String[] args){
    - ExampleUse app = new ExampleUse();
    - app.start();
    - }
    -
    - @Override
    - public void initialize(){
    - super.initialize();
    -
    - // attach root node to viewport
    - viewPort.attachScene(rootNode);
    - }
    -
    - @Override
    - public void update(){
    - super.update();
    -
    - float tpf = timer.getTimePerFrame();
    -
    - // update rootNode
    - rootNode.updateLogicalState(tpf);
    - rootNode.updateGeometricState();
    -
    - // render the viewports
    - renderManager.render(tpf);
    - }
    -}
    -
    +public class ExampleUse extends Application {
    +
    + private Node rootNode = new Node("Root Node");
    +
    + public static void main(String[] args){
    + ExampleUse app = new ExampleUse();
    + app.start();
    + }
    +
    + @Override
    + public void initialize(){
    + super.initialize();
    +
    + // attach root node to viewport
    + viewPort.attachScene(rootNode);
    + }
    +
    + @Override
    + public void update(){
    + super.update();
    +
    + float tpf = timer.getTimePerFrame();
    +
    + // update rootNode
    + rootNode.updateLogicalState(tpf);
    + rootNode.updateGeometricState();
    +
    + // render the viewports
    + renderManager.render(tpf);
    + }
    +}
    +
    diff --git a/engine/src/core/com/jme3/app/state/AppStateManager.java b/engine/src/core/com/jme3/app/state/AppStateManager.java index 6fe0e9c12..76c4047d0 100644 --- a/engine/src/core/com/jme3/app/state/AppStateManager.java +++ b/engine/src/core/com/jme3/app/state/AppStateManager.java @@ -173,7 +173,6 @@ public class AppStateManager { /** * Calls render for all attached states, do not call directly. - * @param rm The RenderManager */ public void postRender(){ AppState[] array = getArray(); diff --git a/engine/src/core/com/jme3/asset/AssetLocator.java b/engine/src/core/com/jme3/asset/AssetLocator.java index c0cb831df..561e37296 100644 --- a/engine/src/core/com/jme3/asset/AssetLocator.java +++ b/engine/src/core/com/jme3/asset/AssetLocator.java @@ -54,7 +54,7 @@ public interface AssetLocator { * * @param manager * @param key - * @return + * @return The {@link AssetInfo} that was located, or null if not found. */ public AssetInfo locate(AssetManager manager, AssetKey key); } diff --git a/engine/src/core/com/jme3/asset/AssetManager.java b/engine/src/core/com/jme3/asset/AssetManager.java index ad44c5151..c57bb1b4d 100644 --- a/engine/src/core/com/jme3/asset/AssetManager.java +++ b/engine/src/core/com/jme3/asset/AssetManager.java @@ -159,7 +159,7 @@ public interface AssetManager { * 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) */ @@ -168,7 +168,7 @@ public interface AssetManager { /** * Load audio file, supported types are WAV or OGG. * @param key - * @return + * @return The audio data loaded * * @see AssetManager#loadAsset(com.jme3.asset.AssetKey) */ @@ -178,7 +178,7 @@ public interface AssetManager { * 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) */ @@ -188,7 +188,7 @@ public interface AssetManager { * 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) */ @@ -198,7 +198,7 @@ public interface AssetManager { * 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) */ @@ -207,7 +207,7 @@ public interface AssetManager { /** * Load a material (J3M) file. * @param name - * @return + * @return The material that was loaded * * @see AssetManager#loadAsset(com.jme3.asset.AssetKey) */ @@ -225,7 +225,7 @@ public interface AssetManager { * and are with the extension "fnt". * * @param name - * @return + * @return The font loaded * * @see AssetManager#loadAsset(com.jme3.asset.AssetKey) */ diff --git a/engine/src/core/com/jme3/asset/TextureKey.java b/engine/src/core/com/jme3/asset/TextureKey.java index 8f8757051..5abb52981 100644 --- a/engine/src/core/com/jme3/asset/TextureKey.java +++ b/engine/src/core/com/jme3/asset/TextureKey.java @@ -70,7 +70,7 @@ public class TextureKey extends AssetKey { /** * Enable smart caching for textures - * @return + * @return true to enable smart cache */ @Override public boolean useSmartCache(){ diff --git a/engine/src/core/com/jme3/audio/AudioRenderer.java b/engine/src/core/com/jme3/audio/AudioRenderer.java index 468b6aed9..20fa2be27 100644 --- a/engine/src/core/com/jme3/audio/AudioRenderer.java +++ b/engine/src/core/com/jme3/audio/AudioRenderer.java @@ -48,7 +48,7 @@ public interface AudioRenderer { /** * 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); diff --git a/engine/src/core/com/jme3/bounding/BoundingBox.java b/engine/src/core/com/jme3/bounding/BoundingBox.java index 6d9894901..1e1440a18 100644 --- a/engine/src/core/com/jme3/bounding/BoundingBox.java +++ b/engine/src/core/com/jme3/bounding/BoundingBox.java @@ -259,12 +259,8 @@ public class BoundingBox extends BoundingVolume { * transform modifies the center of the box to reflect the * change made via a rotation, translation and scale. * - * @param rotate - * the rotation change. - * @param translate - * the translation change. - * @param scale - * the size change. + * @param trans + * the transform to apply * @param store * box to store result in */ @@ -570,7 +566,7 @@ public class BoundingBox extends BoundingVolume { * intersects determines if this Bounding Box intersects with another given * bounding volume. If so, true is returned, otherwise, false is returned. * - * @see com.jme.bounding.BoundingVolume#intersects(com.jme.bounding.BoundingVolume) + * @see BoundingVolume#intersects(com.jme3.bounding.BoundingVolume) */ public boolean intersects(BoundingVolume bv) { return bv.intersectsBoundingBox(this); @@ -579,7 +575,7 @@ public class BoundingBox extends BoundingVolume { /** * determines if this bounding box intersects a given bounding sphere. * - * @see com.jme.bounding.BoundingVolume#intersectsSphere(com.jme.bounding.BoundingSphere) + * @see BoundingVolume#intersectsSphere(com.jme3.bounding.BoundingSphere) */ public boolean intersectsSphere(BoundingSphere bs) { assert Vector3f.isValidVector(center) && Vector3f.isValidVector(bs.center); @@ -600,7 +596,7 @@ public class BoundingBox extends BoundingVolume { * two boxes intersect in any way, true is returned. Otherwise, false is * returned. * - * @see com.jme.bounding.BoundingVolume#intersectsBoundingBox(com.jme.bounding.BoundingBox) + * @see BoundingVolume#intersectsBoundingBox(com.jme3.bounding.BoundingBox) */ public boolean intersectsBoundingBox(BoundingBox bb) { assert Vector3f.isValidVector(center) && Vector3f.isValidVector(bb.center); @@ -632,7 +628,7 @@ public class BoundingBox extends BoundingVolume { * determines if this bounding box intersects with a given ray object. If an * intersection has occurred, true is returned, otherwise false is returned. * - * @see com.jme.bounding.BoundingVolume#intersects(com.jme.math.Ray) + * @see BoundingVolume#intersects(com.jme3.math.Ray) */ public boolean intersects(Ray ray) { assert Vector3f.isValidVector(center); @@ -766,10 +762,11 @@ public class BoundingBox extends BoundingVolume { /** * C code ported from http://www.cs.lth.se/home/Tomas_Akenine_Moller/code/tribox3.txt * - * @param v1 - * @param v2 - * @param v3 - * @return + * @param v1 The first point in the triangle + * @param v2 The second point in the triangle + * @param v3 The third point in the triangle + * @return True if the bounding box intersects the triangle, false + * otherwise. */ public boolean intersects(Vector3f v1, Vector3f v2, Vector3f v3){ return Intersection.intersect(this, v1, v2, v3); diff --git a/engine/src/core/com/jme3/bounding/BoundingSphere.java b/engine/src/core/com/jme3/bounding/BoundingSphere.java index 3d0df1372..6725a4483 100644 --- a/engine/src/core/com/jme3/bounding/BoundingSphere.java +++ b/engine/src/core/com/jme3/bounding/BoundingSphere.java @@ -387,12 +387,8 @@ public class BoundingSphere extends BoundingVolume { * transform modifies the center of the sphere to reflect the * change made via a rotation, translation and scale. * - * @param rotate - * the rotation change. - * @param translate - * the translation change. - * @param scale - * the size change. + * @param trans + * the transform to apply * @param store * sphere to store result in * @return BoundingVolume diff --git a/engine/src/core/com/jme3/input/controls/MouseButtonTrigger.java b/engine/src/core/com/jme3/input/controls/MouseButtonTrigger.java index 303a54e03..ff013d608 100644 --- a/engine/src/core/com/jme3/input/controls/MouseButtonTrigger.java +++ b/engine/src/core/com/jme3/input/controls/MouseButtonTrigger.java @@ -32,6 +32,8 @@ package com.jme3.input.controls; +import com.jme3.input.MouseInput; + /** * A MouseButtonTrigger is used as a mapping to receive events * from mouse buttons. It is generally expected for a mouse to have at least diff --git a/engine/src/core/com/jme3/light/Light.java b/engine/src/core/com/jme3/light/Light.java index 712c45ab1..009e21ecf 100644 --- a/engine/src/core/com/jme3/light/Light.java +++ b/engine/src/core/com/jme3/light/Light.java @@ -160,7 +160,8 @@ public abstract class Light implements Savable, Cloneable { this.color.set(color); } - /** + + /* * Returns true if the light is enabled * * @return true if the light is enabled diff --git a/engine/src/core/com/jme3/material/Material.java b/engine/src/core/com/jme3/material/Material.java index 8a6f39509..2e14055fe 100644 --- a/engine/src/core/com/jme3/material/Material.java +++ b/engine/src/core/com/jme3/material/Material.java @@ -193,8 +193,7 @@ public class Material implements Cloneable, Savable, Comparable { } /** - * Clones this material. The result - * @return + * Clones this material. The result is returned. */ @Override public Material clone() { @@ -219,10 +218,27 @@ public class Material implements Cloneable, Savable, Comparable { } } + /** + * Returns the currently active technique. + *

    + * 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; } @@ -296,6 +312,13 @@ public class Material implements Cloneable, Savable, Comparable { return def; } + /** + * Returns the parameter set on this material with the given name, + * returns null 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) { @@ -304,6 +327,13 @@ public class Material implements Cloneable, Savable, Comparable { return null; } + /** + * Returns the texture parameter set on this material with the given name, + * returns null 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) { @@ -312,6 +342,13 @@ public class Material implements Cloneable, Savable, Comparable { 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 getParams() { return paramValues.values(); } @@ -342,10 +379,11 @@ public class Material implements Cloneable, Savable, Comparable { } /** - * 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); @@ -363,7 +401,7 @@ public class Material implements Cloneable, Savable, Comparable { } /** - * 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) { @@ -416,9 +454,18 @@ public class Material implements Cloneable, Savable, Comparable { 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); @@ -438,8 +485,10 @@ public class Material implements Cloneable, Savable, Comparable { } /** - * 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) { @@ -471,7 +520,8 @@ public class Material implements Cloneable, Savable, Comparable { } /** - * 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 */ @@ -480,7 +530,8 @@ public class Material implements Cloneable, Savable, Comparable { } /** - * 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 */ @@ -489,7 +540,8 @@ public class Material implements Cloneable, Savable, Comparable { } /** - * 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 */ @@ -498,7 +550,8 @@ public class Material implements Cloneable, Savable, Comparable { } /** - * 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 */ @@ -507,7 +560,8 @@ public class Material implements Cloneable, Savable, Comparable { } /** - * 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 */ @@ -516,7 +570,8 @@ public class Material implements Cloneable, Savable, Comparable { } /** - * 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 */ @@ -525,7 +580,8 @@ public class Material implements Cloneable, Savable, Comparable { } /** - * 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 */ @@ -534,7 +590,8 @@ public class Material implements Cloneable, Savable, Comparable { } /** - * 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 */ @@ -567,9 +624,6 @@ public class Material implements Cloneable, Savable, Comparable { * // or the direction of the light (for directional lights).
    * // g_LightPosition.w is the inverse radius (1/r) of the light (for attenuation)
    *

    - * - * @param shader - * @param lightList */ protected void updateLightListUniforms(Shader shader, Geometry g, int numLights) { if (numLights == 0){ // this shader does not do lighting, ignore. @@ -719,6 +773,29 @@ public class Material implements Cloneable, Savable, Comparable { } } + /** + * Select the technique to use for rendering this material. + *

    + * If name 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. + *

    + * Any candidate technique for selection (either default or named) + * must be verified to be compatible with the system, for that, the + * renderManager 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); @@ -730,7 +807,7 @@ public class Material implements Cloneable, Savable, Comparable { if (name.equals("Default")) { List 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; @@ -794,8 +871,13 @@ public class Material implements Cloneable, Savable, Comparable { } /** - * "Pre-load" the material, including textures and shaders, to the - * renderer. + * Preloads this material for the given render manager. + *

    + * 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); @@ -844,9 +926,11 @@ public class Material implements Cloneable, Savable, Comparable { } /** - * 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); diff --git a/engine/src/core/com/jme3/material/Technique.java b/engine/src/core/com/jme3/material/Technique.java index 51ea4ca56..be6be58e9 100644 --- a/engine/src/core/com/jme3/material/Technique.java +++ b/engine/src/core/com/jme3/material/Technique.java @@ -176,6 +176,15 @@ public class Technique implements Savable { updateUniformParam(paramName, type, value, false); } + /** + * Returns true if the technique must be reloaded. + *

    + * If a technique needs to reload, then the {@link Material} should + * call {@link #makeCurrent(com.jme3.asset.AssetManager) } on this + * technique. + * + * @return true if the technique must be reloaded. + */ public boolean isNeedReload() { return needReload; } @@ -183,8 +192,10 @@ public class Technique implements Savable { /** * Prepares the technique for use by loading the shader and setting * the proper defines based on material parameters. + * + * @param assetManager The asset manager to use for loading shaders. */ - public void makeCurrent(AssetManager manager) { + public void makeCurrent(AssetManager assetManager) { // check if reload is needed.. if (def.isUsingShaders()) { DefineList newDefines = new DefineList(); @@ -203,7 +214,7 @@ public class Technique implements Savable { defines.clear(); defines.addFrom(newDefines); // defines changed, recompile needed - loadShader(manager); + loadShader(assetManager); } } } @@ -214,8 +225,8 @@ public class Technique implements Savable { allDefines.addFrom(def.getShaderPresetDefines()); allDefines.addFrom(defines); - ShaderKey key = new ShaderKey(def.getVertName(), - def.getFragName(), + ShaderKey key = new ShaderKey(def.getVertexShaderName(), + def.getFragmentShaderName(), allDefines, def.getShaderLanguage()); shader = manager.loadShader(key); diff --git a/engine/src/core/com/jme3/material/TechniqueDef.java b/engine/src/core/com/jme3/material/TechniqueDef.java index f8880694c..6a9a56f7f 100644 --- a/engine/src/core/com/jme3/material/TechniqueDef.java +++ b/engine/src/core/com/jme3/material/TechniqueDef.java @@ -38,6 +38,7 @@ import com.jme3.export.InputCapsule; 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; @@ -47,12 +48,49 @@ import java.util.EnumSet; 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. + *

    + * 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. + *

    + * 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. + *

    + * The specific details of rendering the lighting is up to the + * renderer implementation. + */ FixedPipeline, } @@ -78,97 +116,140 @@ public class TechniqueDef implements Savable { private HashMap defineParams; private ArrayList worldBinds; + /** + * Creates a new technique definition. + *

    + * Used internally by the J3M/J3MD loader. + * + * @param name The name of the technique, should be set to null + * 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 export is available -// oc.write(defineParams, "defineParams", null); - // TODO: Finish this when List 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 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; @@ -176,6 +257,18 @@ public class TechniqueDef implements Savable { return defineParams.get(paramName); } + /** + * Adds a define linked to a material parameter. + *

    + * 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(); @@ -183,6 +276,30 @@ public class TechniqueDef implements Savable { 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. + *

    + * 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(); @@ -190,33 +307,94 @@ public class TechniqueDef implements Savable { 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(); } - 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 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 export is available +// oc.write(defineParams, "defineParams", null); + // TODO: Finish this when List 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); + } + } diff --git a/engine/src/core/com/jme3/material/package.html b/engine/src/core/com/jme3/material/package.html index adfb66a33..9af9cc843 100644 --- a/engine/src/core/com/jme3/material/package.html +++ b/engine/src/core/com/jme3/material/package.html @@ -9,7 +9,7 @@ The com.jme3.material 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. diff --git a/engine/src/core/com/jme3/math/Matrix4f.java b/engine/src/core/com/jme3/math/Matrix4f.java index 13367a479..abd329d2b 100644 --- a/engine/src/core/com/jme3/math/Matrix4f.java +++ b/engine/src/core/com/jme3/math/Matrix4f.java @@ -1197,8 +1197,7 @@ public final class Matrix4f implements Savable, Cloneable { * * @param vec * vec to multiply against. - * @param store - * a vector to store the result in. created if null is passed. + * * @return the rotated vector. */ public Vector4f multAcross(Vector4f vec) { diff --git a/engine/src/core/com/jme3/math/Ray.java b/engine/src/core/com/jme3/math/Ray.java index a70d9c93d..5f3c53b8d 100644 --- a/engine/src/core/com/jme3/math/Ray.java +++ b/engine/src/core/com/jme3/math/Ray.java @@ -455,7 +455,8 @@ public final class Ray implements Savable, Cloneable, Collidable { * getLimit returns the limit or the ray, aka the length. * If the limit is not infinity, then this ray is a line with length * limit. - * @return + * + * @return the limit or the ray, aka the length. */ public float getLimit(){ return limit; diff --git a/engine/src/core/com/jme3/math/Spline.java b/engine/src/core/com/jme3/math/Spline.java index 942ad39fa..d08d4f7f3 100644 --- a/engine/src/core/com/jme3/math/Spline.java +++ b/engine/src/core/com/jme3/math/Spline.java @@ -277,7 +277,6 @@ public class Spline implements Savable { /** * returns the curve tension - * @return */ public float getCurveTension() { return curveTension; @@ -297,7 +296,6 @@ public class Spline implements Savable { /** * returns true if the spline cycle - * @return */ public boolean isCycle() { return cycle; @@ -326,7 +324,6 @@ public class Spline implements Savable { /** * return the total lenght of the spline - * @return */ public float getTotalLength() { return totalLength; @@ -334,7 +331,6 @@ public class Spline implements Savable { /** * return the type of the spline - * @return */ public SplineType getType() { return type; @@ -351,7 +347,6 @@ public class Spline implements Savable { /** * returns this spline control points - * @return */ public List getControlPoints() { return controlPoints; @@ -359,7 +354,6 @@ public class Spline implements Savable { /** * returns a list of float representing the segments lenght - * @return */ public List getSegmentsLength() { return segmentsLength; diff --git a/engine/src/core/com/jme3/math/Triangle.java b/engine/src/core/com/jme3/math/Triangle.java index 81028b0a8..0c05a3b15 100644 --- a/engine/src/core/com/jme3/math/Triangle.java +++ b/engine/src/core/com/jme3/math/Triangle.java @@ -29,7 +29,6 @@ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - package com.jme3.math; import com.jme3.export.JmeExporter; @@ -50,15 +49,13 @@ public class Triangle extends AbstractTriangle implements Savable { private Vector3f pointa = new Vector3f(); private Vector3f pointb = new Vector3f(); private Vector3f pointc = new Vector3f(); - private transient Vector3f center; private transient Vector3f normal; - private float projection; - private int index; - - public Triangle() {} + + public Triangle() { + } /** * Constructor instantiates a new Triangle object with the @@ -84,25 +81,29 @@ public class Triangle extends AbstractTriangle implements Savable { */ public Vector3f get(int i) { switch (i) { - case 0: return pointa; - case 1: return pointb; - case 2: return pointc; - default: return null; + case 0: + return pointa; + case 1: + return pointb; + case 2: + return pointc; + default: + return null; } } - public Vector3f get1(){ + public Vector3f get1() { return pointa; } - public Vector3f get2(){ + public Vector3f get2() { return pointb; } - public Vector3f get3(){ + public Vector3f get3() { return pointc; } - + /** * * set sets one of the triangles points to that specified as @@ -112,9 +113,15 @@ public class Triangle extends AbstractTriangle implements Savable { */ public void set(int i, Vector3f point) { switch (i) { - case 0: pointa.set(point); break; - case 1: pointb.set(point); break; - case 2: pointc.set(point); break; + case 0: + pointa.set(point); + break; + case 1: + pointb.set(point); + break; + case 2: + pointc.set(point); + break; } } @@ -123,68 +130,77 @@ public class Triangle extends AbstractTriangle implements Savable { * set sets one of the triangles points to that specified as * a parameter. * @param i the index to place the point. - * @param point the point to set. */ public void set(int i, float x, float y, float z) { switch (i) { - case 0: pointa.set(x,y,z); break; - case 1: pointb.set(x,y,z); break; - case 2: pointc.set(x,y,z); break; + case 0: + pointa.set(x, y, z); + break; + case 1: + pointb.set(x, y, z); + break; + case 2: + pointc.set(x, y, z); + break; } } - public void set1(Vector3f v){ + public void set1(Vector3f v) { pointa.set(v); } - public void set2(Vector3f v){ + public void set2(Vector3f v) { pointb.set(v); } - public void set3(Vector3f v){ + public void set3(Vector3f v) { pointc.set(v); } - public void set(Vector3f v1, Vector3f v2, Vector3f v3){ + public void set(Vector3f v1, Vector3f v2, Vector3f v3) { pointa.set(v1); pointb.set(v2); pointc.set(v3); } - + /** * calculateCenter finds the average point of the triangle. * */ public void calculateCenter() { - if (center == null) + if (center == null) { center = new Vector3f(pointa); - else center.set(pointa); + } else { + center.set(pointa); + } center.addLocal(pointb).addLocal(pointc).multLocal(FastMath.ONE_THIRD); } - + /** * calculateCenter finds the average point of the triangle. * */ public void calculateNormal() { - if (normal == null) + if (normal == null) { normal = new Vector3f(pointb); - else normal.set(pointb); - normal.subtractLocal(pointa).crossLocal(pointc.x-pointa.x, pointc.y-pointa.y, pointc.z-pointa.z); + } else { + normal.set(pointb); + } + normal.subtractLocal(pointa).crossLocal(pointc.x - pointa.x, pointc.y - pointa.y, pointc.z - pointa.z); normal.normalizeLocal(); } - + /** * obtains the center point of this triangle (average of the three triangles) * @return the center point. */ public Vector3f getCenter() { - if(center == null) { - calculateCenter(); - } + if (center == null) { + calculateCenter(); + } return center; } - + /** * sets the center point of this triangle (average of the three triangles) * @param center the center point. @@ -192,7 +208,7 @@ public class Triangle extends AbstractTriangle implements Savable { public void setCenter(Vector3f center) { this.center = center; } - + /** * obtains the unit length normal vector of this triangle, if set or * calculated @@ -200,12 +216,12 @@ public class Triangle extends AbstractTriangle implements Savable { * @return the normal vector */ public Vector3f getNormal() { - if(normal == null) { - calculateNormal(); - } + if (normal == null) { + calculateNormal(); + } return normal; } - + /** * sets the normal vector of this triangle (to conform, must be unit length) * @param normal the normal vector. @@ -213,7 +229,7 @@ public class Triangle extends AbstractTriangle implements Savable { public void setNormal(Vector3f normal) { this.normal = normal; } - + /** * obtains the projection of the vertices relative to the line origin. * @return the projection of the triangle. @@ -221,7 +237,7 @@ public class Triangle extends AbstractTriangle implements Savable { public float getProjection() { return this.projection; } - + /** * sets the projection of the vertices relative to the line origin. * @param projection the projection of the triangle. @@ -229,7 +245,7 @@ public class Triangle extends AbstractTriangle implements Savable { public void setProjection(float projection) { this.projection = projection; } - + /** * obtains an index that this triangle represents if it is contained in a OBBTree. * @return the index in an OBBtree @@ -237,7 +253,7 @@ public class Triangle extends AbstractTriangle implements Savable { public int getIndex() { return index; } - + /** * sets an index that this triangle represents if it is contained in a OBBTree. * @param index the index in an OBBtree @@ -246,13 +262,14 @@ public class Triangle extends AbstractTriangle implements Savable { this.index = index; } - public static Vector3f computeTriangleNormal(Vector3f v1, Vector3f v2, Vector3f v3, Vector3f store){ - if (store == null) + public static Vector3f computeTriangleNormal(Vector3f v1, Vector3f v2, Vector3f v3, Vector3f store) { + if (store == null) { store = new Vector3f(v2); - else + } else { store.set(v2); + } - store.subtractLocal(v1).crossLocal(v3.x-v1.x, v3.y-v1.y, v3.z-v1.z); + store.subtractLocal(v1).crossLocal(v3.x - v1.x, v3.y - v1.y, v3.z - v1.z); return store.normalizeLocal(); } @@ -263,11 +280,11 @@ public class Triangle extends AbstractTriangle implements Savable { } public void read(JmeImporter e) throws IOException { - pointa = (Vector3f)e.getCapsule(this).readSavable("pointa", Vector3f.ZERO.clone()); - pointb = (Vector3f)e.getCapsule(this).readSavable("pointb", Vector3f.ZERO.clone()); - pointc = (Vector3f)e.getCapsule(this).readSavable("pointc", Vector3f.ZERO.clone()); + pointa = (Vector3f) e.getCapsule(this).readSavable("pointa", Vector3f.ZERO.clone()); + pointb = (Vector3f) e.getCapsule(this).readSavable("pointb", Vector3f.ZERO.clone()); + pointc = (Vector3f) e.getCapsule(this).readSavable("pointc", Vector3f.ZERO.clone()); } - + @Override public Triangle clone() { try { diff --git a/engine/src/core/com/jme3/math/Vector2f.java b/engine/src/core/com/jme3/math/Vector2f.java index 05a0974ad..fd745a56c 100644 --- a/engine/src/core/com/jme3/math/Vector2f.java +++ b/engine/src/core/com/jme3/math/Vector2f.java @@ -320,7 +320,8 @@ public final class Vector2f implements Savable, Cloneable { * distanceSquared 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) { diff --git a/engine/src/core/com/jme3/math/Vector4f.java b/engine/src/core/com/jme3/math/Vector4f.java index 806908806..d71ebcb98 100644 --- a/engine/src/core/com/jme3/math/Vector4f.java +++ b/engine/src/core/com/jme3/math/Vector4f.java @@ -677,7 +677,7 @@ public final class Vector4f implements Savable, Cloneable { * the y value to subtract. * @param subtractZ * the z value to subtract. - * @param subtract@ + * @param subtractW * the w value to subtract. * @return this */ diff --git a/engine/src/core/com/jme3/post/Filter.java b/engine/src/core/com/jme3/post/Filter.java index 382935b51..93e65b23c 100644 --- a/engine/src/core/com/jme3/post/Filter.java +++ b/engine/src/core/com/jme3/post/Filter.java @@ -189,9 +189,10 @@ public abstract class Filter implements Savable { 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(); @@ -232,16 +233,14 @@ public abstract class Filter implements Savable { } /** - * Override this method if you want to load extra properties when the filter is loaded else only basic properties of the filter will be loaded - * 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() { @@ -269,8 +268,9 @@ public abstract class Filter implements Savable { } /** - * 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; @@ -278,7 +278,8 @@ public abstract class Filter implements Savable { /** * Override this method and return false if your Filter does not need the scene texture - * @return + * + * @return false if your Filter does not need the scene texture */ public boolean isRequiresSceneTexture() { return true; diff --git a/engine/src/core/com/jme3/renderer/Camera.java b/engine/src/core/com/jme3/renderer/Camera.java index 75b2b2806..982e6307f 100644 --- a/engine/src/core/com/jme3/renderer/Camera.java +++ b/engine/src/core/com/jme3/renderer/Camera.java @@ -599,7 +599,6 @@ public class Camera implements Savable, Cloneable { * setLocation 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); @@ -660,7 +659,8 @@ public class Camera implements Savable, Cloneable { * @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); diff --git a/engine/src/core/com/jme3/renderer/GLObject.java b/engine/src/core/com/jme3/renderer/GLObject.java index 0db74731f..43c928b66 100644 --- a/engine/src/core/com/jme3/renderer/GLObject.java +++ b/engine/src/core/com/jme3/renderer/GLObject.java @@ -154,9 +154,8 @@ public abstract class GLObject implements Cloneable { /** * This should create a deep clone. For a shallow clone, use * createDestructableClone(). - * - * @return */ + @Override protected GLObject clone(){ try{ GLObject obj = (GLObject) super.clone(); diff --git a/engine/src/core/com/jme3/renderer/RenderManager.java b/engine/src/core/com/jme3/renderer/RenderManager.java index f9cecbf89..54108e7da 100644 --- a/engine/src/core/com/jme3/renderer/RenderManager.java +++ b/engine/src/core/com/jme3/renderer/RenderManager.java @@ -181,9 +181,6 @@ public class RenderManager { /** * 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); @@ -491,9 +488,6 @@ public class RenderManager { /** * Render scene graph - * @param s - * @param r - * @param cam */ public void renderScene(Spatial scene, ViewPort vp) { if (scene.getParent() == null) { diff --git a/engine/src/core/com/jme3/renderer/Renderer.java b/engine/src/core/com/jme3/renderer/Renderer.java index 88202a034..83ca85649 100644 --- a/engine/src/core/com/jme3/renderer/Renderer.java +++ b/engine/src/core/com/jme3/renderer/Renderer.java @@ -99,7 +99,7 @@ public interface Renderer { 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); @@ -171,7 +171,6 @@ public interface Renderer { /** * Deletes a texture from the GPU. - * @param tex */ public void deleteImage(Image image); diff --git a/engine/src/core/com/jme3/renderer/queue/GeometryList.java b/engine/src/core/com/jme3/renderer/queue/GeometryList.java index 858b4f6b6..0254d2633 100644 --- a/engine/src/core/com/jme3/renderer/queue/GeometryList.java +++ b/engine/src/core/com/jme3/renderer/queue/GeometryList.java @@ -70,10 +70,10 @@ public class GeometryList { } /** - * Adds a spatial to the list. List size is doubled if there is no room. + * Adds a geometry to the list. List size is doubled if there is no room. * - * @param s - * The spatial to add. + * @param g + * The geometry to add. */ public void add(Geometry g) { if (size == geometries.length) { diff --git a/engine/src/core/com/jme3/scene/Geometry.java b/engine/src/core/com/jme3/scene/Geometry.java index afa143f9e..a9635f594 100644 --- a/engine/src/core/com/jme3/scene/Geometry.java +++ b/engine/src/core/com/jme3/scene/Geometry.java @@ -177,7 +177,7 @@ public class Geometry extends Spatial { * 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() { @@ -282,7 +282,6 @@ public class Geometry extends Spatial { * 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){ @@ -308,8 +307,8 @@ public class Geometry extends Spatial { * 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); } @@ -318,7 +317,6 @@ public class Geometry extends Spatial { * 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(){ diff --git a/engine/src/core/com/jme3/scene/Node.java b/engine/src/core/com/jme3/scene/Node.java index a9a54fd39..46142ed49 100644 --- a/engine/src/core/com/jme3/scene/Node.java +++ b/engine/src/core/com/jme3/scene/Node.java @@ -526,7 +526,7 @@ public class Node extends Spatial implements Savable { * @return Non-null, but possibly 0-element, list of matching Spatials (also Instances extending Spatials). * * @see java.util.regex.Pattern - * @see Spatial#matches(Class, String) + * @see Spatial#matches(java.lang.Class, java.lang.String) */ @SuppressWarnings("unchecked") public List descendantMatches( @@ -546,7 +546,7 @@ public class Node extends Spatial implements Savable { /** * Convenience wrapper. * - * @see #descendantMatches(Class, String) + * @see #descendantMatches(java.lang.Class, java.lang.String) */ public List descendantMatches( Class spatialSubclass) { @@ -556,7 +556,7 @@ public class Node extends Spatial implements Savable { /** * Convenience wrapper. * - * @see #descendantMatches(Class, String) + * @see #descendantMatches(java.lang.Class, java.lang.String) */ public List descendantMatches(String nameRegex) { return descendantMatches(null, nameRegex); diff --git a/engine/src/core/com/jme3/scene/Spatial.java b/engine/src/core/com/jme3/scene/Spatial.java index 75189ef06..b78e0bf35 100644 --- a/engine/src/core/com/jme3/scene/Spatial.java +++ b/engine/src/core/com/jme3/scene/Spatial.java @@ -784,9 +784,6 @@ public abstract class Spatial implements Savable, Cloneable, Collidable { /** * setLocalScale 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); diff --git a/engine/src/core/com/jme3/scene/VertexBuffer.java b/engine/src/core/com/jme3/scene/VertexBuffer.java index 7526708d5..858b05d19 100644 --- a/engine/src/core/com/jme3/scene/VertexBuffer.java +++ b/engine/src/core/com/jme3/scene/VertexBuffer.java @@ -664,11 +664,6 @@ public class VertexBuffer extends GLObject implements Savable, Cloneable { * 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) diff --git a/engine/src/core/com/jme3/scene/control/Control.java b/engine/src/core/com/jme3/scene/control/Control.java index c54fb34c9..6be46a322 100644 --- a/engine/src/core/com/jme3/scene/control/Control.java +++ b/engine/src/core/com/jme3/scene/control/Control.java @@ -48,7 +48,7 @@ public interface Control extends Savable { * 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); diff --git a/engine/src/core/com/jme3/scene/control/LightControl.java b/engine/src/core/com/jme3/scene/control/LightControl.java index a0fcfca85..226515974 100644 --- a/engine/src/core/com/jme3/scene/control/LightControl.java +++ b/engine/src/core/com/jme3/scene/control/LightControl.java @@ -76,14 +76,14 @@ public class LightControl extends AbstractControl { } /** - * @param camera The Camera to be synced. + * @param light The light to be synced. */ public LightControl(Light light) { 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; diff --git a/engine/src/core/com/jme3/shader/Shader.java b/engine/src/core/com/jme3/shader/Shader.java index 2670e4c8f..6c6109bdf 100644 --- a/engine/src/core/com/jme3/shader/Shader.java +++ b/engine/src/core/com/jme3/shader/Shader.java @@ -380,7 +380,6 @@ public final class Shader extends GLObject implements Savable { /** * Returns true if this program and all it's shaders have been compiled, * linked and validated successfuly. - * @return */ public boolean isUsable(){ return usable; @@ -417,7 +416,6 @@ public final class Shader extends GLObject implements Savable { /** * Called by the object manager to reset all object IDs. This causes * the shader to be reuploaded to the GPU incase the display was restarted. - * @param r */ @Override public void resetObject() { diff --git a/engine/src/core/com/jme3/system/JmeContext.java b/engine/src/core/com/jme3/system/JmeContext.java index 37283a503..57a341eab 100644 --- a/engine/src/core/com/jme3/system/JmeContext.java +++ b/engine/src/core/com/jme3/system/JmeContext.java @@ -37,6 +37,7 @@ import com.jme3.input.KeyInput; 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.