Browse Source

* 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

sha..rd 14 years ago
parent
commit
95cdde7f53
80 changed files with 9331 additions and 8984 deletions
  1. 732 724
      engine/src/blender/com/jme3/asset/BlenderKey.java
  2. 114 113
      engine/src/blender/com/jme3/scene/plugins/blender/BlenderLoader.java
  3. 82 81
      engine/src/blender/com/jme3/scene/plugins/blender/BlenderModelLoader.java
  4. 164 164
      engine/src/blender/com/jme3/scene/plugins/blender/data/DnaBlockData.java
  5. 293 292
      engine/src/blender/com/jme3/scene/plugins/blender/data/Field.java
  6. 155 156
      engine/src/blender/com/jme3/scene/plugins/blender/data/FileBlockHeader.java
  7. 248 245
      engine/src/blender/com/jme3/scene/plugins/blender/data/Structure.java
  8. 33 31
      engine/src/blender/com/jme3/scene/plugins/blender/exception/BlenderFileException.java
  9. 76 76
      engine/src/blender/com/jme3/scene/plugins/blender/helpers/ArmatureHelper.java
  10. 37 36
      engine/src/blender/com/jme3/scene/plugins/blender/helpers/CameraHelper.java
  11. 24 23
      engine/src/blender/com/jme3/scene/plugins/blender/helpers/ConstraintHelper.java
  12. 10 9
      engine/src/blender/com/jme3/scene/plugins/blender/helpers/CurvesHelper.java
  13. 10 9
      engine/src/blender/com/jme3/scene/plugins/blender/helpers/IpoHelper.java
  14. 10 9
      engine/src/blender/com/jme3/scene/plugins/blender/helpers/LightHelper.java
  15. 10 9
      engine/src/blender/com/jme3/scene/plugins/blender/helpers/MaterialHelper.java
  16. 10 9
      engine/src/blender/com/jme3/scene/plugins/blender/helpers/MeshHelper.java
  17. 10 9
      engine/src/blender/com/jme3/scene/plugins/blender/helpers/ModifierHelper.java
  18. 10 9
      engine/src/blender/com/jme3/scene/plugins/blender/helpers/NoiseHelper.java
  19. 10 9
      engine/src/blender/com/jme3/scene/plugins/blender/helpers/ObjectHelper.java
  20. 10 9
      engine/src/blender/com/jme3/scene/plugins/blender/helpers/ParticlesHelper.java
  21. 37 35
      engine/src/blender/com/jme3/scene/plugins/blender/helpers/TextureHelper.java
  22. 293 292
      engine/src/blender/com/jme3/scene/plugins/blender/helpers/v249/ArmatureHelper.java
  23. 41 40
      engine/src/blender/com/jme3/scene/plugins/blender/helpers/v249/CameraHelper.java
  24. 722 701
      engine/src/blender/com/jme3/scene/plugins/blender/helpers/v249/ConstraintHelper.java
  25. 548 548
      engine/src/blender/com/jme3/scene/plugins/blender/helpers/v249/CurvesHelper.java
  26. 85 85
      engine/src/blender/com/jme3/scene/plugins/blender/helpers/v249/IpoHelper.java
  27. 47 46
      engine/src/blender/com/jme3/scene/plugins/blender/helpers/v249/LightHelper.java
  28. 642 636
      engine/src/blender/com/jme3/scene/plugins/blender/helpers/v249/MaterialHelper.java
  29. 530 529
      engine/src/blender/com/jme3/scene/plugins/blender/helpers/v249/MeshHelper.java
  30. 462 461
      engine/src/blender/com/jme3/scene/plugins/blender/helpers/v249/ModifierHelper.java
  31. 1500 1473
      engine/src/blender/com/jme3/scene/plugins/blender/helpers/v249/NoiseHelper.java
  32. 194 200
      engine/src/blender/com/jme3/scene/plugins/blender/structures/AbstractInfluenceFunction.java
  33. 115 115
      engine/src/blender/com/jme3/scene/plugins/blender/structures/BezierCurve.java
  34. 134 134
      engine/src/blender/com/jme3/scene/plugins/blender/structures/Constraint.java
  35. 126 124
      engine/src/blender/com/jme3/scene/plugins/blender/structures/ConstraintType.java
  36. 156 156
      engine/src/blender/com/jme3/scene/plugins/blender/structures/Ipo.java
  37. 44 43
      engine/src/blender/com/jme3/scene/plugins/blender/structures/Modifier.java
  38. 57 55
      engine/src/blender/com/jme3/scene/plugins/blender/utils/AbstractBlenderHelper.java
  39. 50 49
      engine/src/blender/com/jme3/scene/plugins/blender/utils/BlenderConverter.java
  40. 309 309
      engine/src/blender/com/jme3/scene/plugins/blender/utils/BlenderInputStream.java
  41. 343 343
      engine/src/blender/com/jme3/scene/plugins/blender/utils/DataRepository.java
  42. 107 106
      engine/src/blender/com/jme3/scene/plugins/blender/utils/DynamicArray.java
  43. 100 100
      engine/src/blender/com/jme3/scene/plugins/blender/utils/JmeConverter.java
  44. 121 120
      engine/src/blender/com/jme3/scene/plugins/blender/utils/Pointer.java
  45. 1 3
      engine/src/core/com/jme3/animation/Bone.java
  46. 0 2
      engine/src/core/com/jme3/animation/CompactArray.java
  47. 35 35
      engine/src/core/com/jme3/app/package.html
  48. 0 1
      engine/src/core/com/jme3/app/state/AppStateManager.java
  49. 1 1
      engine/src/core/com/jme3/asset/AssetLocator.java
  50. 7 7
      engine/src/core/com/jme3/asset/AssetManager.java
  51. 1 1
      engine/src/core/com/jme3/asset/TextureKey.java
  52. 1 1
      engine/src/core/com/jme3/audio/AudioRenderer.java
  53. 11 14
      engine/src/core/com/jme3/bounding/BoundingBox.java
  54. 2 6
      engine/src/core/com/jme3/bounding/BoundingSphere.java
  55. 2 0
      engine/src/core/com/jme3/input/controls/MouseButtonTrigger.java
  56. 2 1
      engine/src/core/com/jme3/light/Light.java
  57. 110 26
      engine/src/core/com/jme3/material/Material.java
  58. 15 4
      engine/src/core/com/jme3/material/Technique.java
  59. 226 48
      engine/src/core/com/jme3/material/TechniqueDef.java
  60. 1 1
      engine/src/core/com/jme3/material/package.html
  61. 1 2
      engine/src/core/com/jme3/math/Matrix4f.java
  62. 2 1
      engine/src/core/com/jme3/math/Ray.java
  63. 0 6
      engine/src/core/com/jme3/math/Spline.java
  64. 71 54
      engine/src/core/com/jme3/math/Triangle.java
  65. 2 1
      engine/src/core/com/jme3/math/Vector2f.java
  66. 1 1
      engine/src/core/com/jme3/math/Vector4f.java
  67. 12 11
      engine/src/core/com/jme3/post/Filter.java
  68. 2 2
      engine/src/core/com/jme3/renderer/Camera.java
  69. 1 2
      engine/src/core/com/jme3/renderer/GLObject.java
  70. 0 6
      engine/src/core/com/jme3/renderer/RenderManager.java
  71. 1 2
      engine/src/core/com/jme3/renderer/Renderer.java
  72. 3 3
      engine/src/core/com/jme3/renderer/queue/GeometryList.java
  73. 2 4
      engine/src/core/com/jme3/scene/Geometry.java
  74. 3 3
      engine/src/core/com/jme3/scene/Node.java
  75. 0 3
      engine/src/core/com/jme3/scene/Spatial.java
  76. 0 5
      engine/src/core/com/jme3/scene/VertexBuffer.java
  77. 1 1
      engine/src/core/com/jme3/scene/control/Control.java
  78. 2 2
      engine/src/core/com/jme3/scene/control/LightControl.java
  79. 0 2
      engine/src/core/com/jme3/shader/Shader.java
  80. 1 0
      engine/src/core/com/jme3/system/JmeContext.java

+ 732 - 724
engine/src/blender/com/jme3/asset/BlenderKey.java

@@ -64,728 +64,736 @@ import com.jme3.texture.Texture;
  * @author Marcin Roguski (Kaelthas)
  * @author Marcin Roguski (Kaelthas)
  */
  */
 public class BlenderKey extends ModelKey {
 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<String, Map<String, int[]>>	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<String, Map<String, int[]>>();
-			animations.put(objectName, new HashMap<String, int[]>());
-		}
-		Map<String, int[]> objectAnimations = animations.get(objectName);
-		if(objectAnimations == null) {
-			objectAnimations = new HashMap<String, int[]>();
-			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<String, int[]> 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<String> getAnimationNames(String objectName) {
-		Map<String, int[]> 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<String, Map<String, int[]>> 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<String, Map<String, int[]>> 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<String, int[]> 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<String, Map<String, int[]>>(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<String, int[]> objectAnimations = new HashMap<String, int[]>(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<Node>		scenes;
-		/** Objects from all scenes. */
-		private List<Node>		objects;
-		/** Materials from all objects. */
-		private List<Material>	materials;
-		/** Textures from all objects. */
-		private List<Texture>	textures;
-		/** Animations of all objects. */
-		private List<AnimData>	animations;
-		/** All cameras from the file. */
-		private List<Camera>	cameras;
-		/** All lights from the file. */
-		private List<Light>		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<Node>();
-			}
-			if((featuresToLoad & FeaturesToLoad.OBJECTS) != 0) {
-				objects = new ArrayList<Node>();
-				if((featuresToLoad & FeaturesToLoad.MATERIALS) != 0) {
-					materials = new ArrayList<Material>();
-					if((featuresToLoad & FeaturesToLoad.TEXTURES) != 0) {
-						textures = new ArrayList<Texture>();
-					}
-				}
-				if((featuresToLoad & FeaturesToLoad.ANIMATIONS) != 0) {
-					animations = new ArrayList<AnimData>();
-				}
-			}
-			if((featuresToLoad & FeaturesToLoad.CAMERAS) != 0) {
-				cameras = new ArrayList<Camera>();
-			}
-			if((featuresToLoad & FeaturesToLoad.LIGHTS) != 0) {
-				lights = new ArrayList<Light>();
-			}
-		}
-
-		/**
-		 * 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<Node> getScenes() {
-			return scenes;
-		}
-
-		/**
-		 * This method returns all loaded objects.
-		 * @return all loaded objects
-		 */
-		public List<Node> getObjects() {
-			return objects;
-		}
-
-		/**
-		 * This method returns all loaded materials.
-		 * @return all loaded materials
-		 */
-		public List<Material> getMaterials() {
-			return materials;
-		}
-
-		/**
-		 * This method returns all loaded textures.
-		 * @return all loaded textures
-		 */
-		public List<Texture> getTextures() {
-			return textures;
-		}
-
-		/**
-		 * This method returns all loaded animations.
-		 * @return all loaded animations
-		 */
-		public List<AnimData> getAnimations() {
-			return animations;
-		}
-
-		/**
-		 * This method returns all loaded cameras.
-		 * @return all loaded cameras
-		 */
-		public List<Camera> getCameras() {
-			return cameras;
-		}
-
-		/**
-		 * This method returns all loaded lights.
-		 * @return all loaded lights
-		 */
-		public List<Light> 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<Spatial> 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<String, Map<String, int[]>> 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<String, Map<String, int[]>>();
+            animations.put(objectName, new HashMap<String, int[]>());
+        }
+        Map<String, int[]> objectAnimations = animations.get(objectName);
+        if (objectAnimations == null) {
+            objectAnimations = new HashMap<String, int[]>();
+            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<String, int[]> 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<String> getAnimationNames(String objectName) {
+        Map<String, int[]> 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<String, Map<String, int[]>> 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<String, Map<String, int[]>> 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<String, int[]> 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<String, Map<String, int[]>>(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<String, int[]> objectAnimations = new HashMap<String, int[]>(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<Node> scenes;
+        /** Objects from all scenes. */
+        private List<Node> objects;
+        /** Materials from all objects. */
+        private List<Material> materials;
+        /** Textures from all objects. */
+        private List<Texture> textures;
+        /** Animations of all objects. */
+        private List<AnimData> animations;
+        /** All cameras from the file. */
+        private List<Camera> cameras;
+        /** All lights from the file. */
+        private List<Light> 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<Node>();
+            }
+            if ((featuresToLoad & FeaturesToLoad.OBJECTS) != 0) {
+                objects = new ArrayList<Node>();
+                if ((featuresToLoad & FeaturesToLoad.MATERIALS) != 0) {
+                    materials = new ArrayList<Material>();
+                    if ((featuresToLoad & FeaturesToLoad.TEXTURES) != 0) {
+                        textures = new ArrayList<Texture>();
+                    }
+                }
+                if ((featuresToLoad & FeaturesToLoad.ANIMATIONS) != 0) {
+                    animations = new ArrayList<AnimData>();
+                }
+            }
+            if ((featuresToLoad & FeaturesToLoad.CAMERAS) != 0) {
+                cameras = new ArrayList<Camera>();
+            }
+            if ((featuresToLoad & FeaturesToLoad.LIGHTS) != 0) {
+                lights = new ArrayList<Light>();
+            }
+        }
+
+        /**
+         * 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<Node> getScenes() {
+            return scenes;
+        }
+
+        /**
+         * This method returns all loaded objects.
+         * @return all loaded objects
+         */
+        public List<Node> getObjects() {
+            return objects;
+        }
+
+        /**
+         * This method returns all loaded materials.
+         * @return all loaded materials
+         */
+        public List<Material> getMaterials() {
+            return materials;
+        }
+
+        /**
+         * This method returns all loaded textures.
+         * @return all loaded textures
+         */
+        public List<Texture> getTextures() {
+            return textures;
+        }
+
+        /**
+         * This method returns all loaded animations.
+         * @return all loaded animations
+         */
+        public List<AnimData> getAnimations() {
+            return animations;
+        }
+
+        /**
+         * This method returns all loaded cameras.
+         * @return all loaded cameras
+         */
+        public List<Camera> getCameras() {
+            return cameras;
+        }
+
+        /**
+         * This method returns all loaded lights.
+         * @return all loaded lights
+         */
+        public List<Light> 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<Spatial> 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;
+        }
+    }
 }
 }

+ 114 - 113
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
  * @author Marcin Roguski
  */
  */
 public class BlenderLoader implements AssetLoader {
 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<FileBlockHeader> blocks = new ArrayList<FileBlockHeader>();
-			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<FileBlockHeader> blocks = new ArrayList<FileBlockHeader>();
+            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;
+    }
 }
 }

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

+ 164 - 164
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
  * @author Marcin Roguski
  */
  */
 public class DnaBlockData {
 public class DnaBlockData {
-	private static final int				SDNA_ID	= 'S' << 24 | 'D' << 16 | 'N' << 8 | 'A';	//SDNA
-	private static final int				NAME_ID	= 'N' << 24 | 'A' << 16 | 'M' << 8 | 'E';	//NAME
-	private static final int				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<String, Structure>	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<String, Structure>(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<String, Structure> 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<String, Structure>(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();
+    }
 }
 }

+ 293 - 292
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.
  * another structure.
  * @author Marcin Roguski
  * @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<Pointer>(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<Character>(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<Number>(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<Number>(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<Number>(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<Number>(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<Number>(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<Structure>(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<Pointer>(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<Character>(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<Number>(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<Number>(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<Number>(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<Number>(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<Number>(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<Structure>(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<Integer> lengths = new ArrayList<Integer>(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<Integer> lengths = new ArrayList<Integer>(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();
+    }
 }
 }

+ 155 - 156
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
  * @author Marcin Roguski
  */
  */
 public class FileBlockHeader {
 public class FileBlockHeader {
-	public static final int	BLOCK_TE00	= 'T' << 24 | 'E' << 16;					//TE00
-	public static final int	BLOCK_ME00	= 'M' << 24 | 'E' << 16;					//ME00
-	public static final int	BLOCK_SR00	= 'S' << 24 | 'R' << 16;					//SR00
-	public static final int	BLOCK_CA00	= 'C' << 24 | 'A' << 16;					//CA00
-	public static final int	BLOCK_LA00	= 'L' << 24 | 'A' << 16;					//LA00
-	public static final int	BLOCK_OB00	= 'O' << 24 | 'B' << 16;					//OB00
-	public static final int	BLOCK_MA00	= 'M' << 24 | 'A' << 16;					//MA00
-	public static final int	BLOCK_SC00	= 'S' << 24 | 'C' << 16;					//SC00
-	public static final int	BLOCK_WO00	= 'W' << 24 | 'O' << 16;					//WO00
-	public static final int	BLOCK_TX00	= 'T' << 24 | 'X' << 16;					//TX00
-	public static final int	BLOCK_IP00	= 'I' << 24 | 'P' << 16;					//IP00
-	public static final int	BLOCK_AC00	= 'A' << 24 | 'C' << 16;					//AC00
 
 
-	public static final int	BLOCK_GLOB	= 'G' << 24 | 'L' << 16 | 'O' << 8 | 'B';	//GLOB
-	public static final int	BLOCK_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;
+    }
 }
 }

+ 248 - 245
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
  * @author Marcin Roguski
  */
  */
 public class Structure implements Cloneable {
 public class Structure implements Cloneable {
-	/** The data repository. */
-	private DataRepository	dataRepository;
-	/** The address of the block that fills the structure. */
-	private transient Long	oldMemoryAddress;
-	/** The type of the structure. */
-	private String			type;
-	/**
-	 * The fields of the structure. Each field consists of a pair: name-type.
-	 */
-	private Field[]			fields;
 
 
-	/**
-	 * 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<Structure> evaluateListBase(DataRepository dataRepository) throws BlenderFileException {
-		if(!"ListBase".equals(this.type)) {
-			throw new IllegalStateException("This structure is not of type: 'ListBase'");
-		}
-		Pointer first = (Pointer)this.getFieldValue("first");
-		Pointer last = (Pointer)this.getFieldValue("last");
-		long currentAddress = 0;
-		long lastAddress = last.getOldMemoryAddress();
-		List<Structure> result = new LinkedList<Structure>();
-		while(currentAddress != lastAddress) {
-			currentAddress = first.getOldMemoryAddress();
-			Structure structure = first.fetchData(dataRepository.getInputStream()).get(0);
-			result.add(structure);
-			first = (Pointer)structure.getFlatFieldValue("next");
-		}
-		return result;
-	}
+    /**
+     * This method returns the 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<Structure> evaluateListBase(DataRepository dataRepository) throws BlenderFileException {
+        if (!"ListBase".equals(this.type)) {
+            throw new IllegalStateException("This structure is not of type: 'ListBase'");
+        }
+        Pointer first = (Pointer) this.getFieldValue("first");
+        Pointer last = (Pointer) this.getFieldValue("last");
+        long currentAddress = 0;
+        long lastAddress = last.getOldMemoryAddress();
+        List<Structure> result = new LinkedList<Structure>();
+        while (currentAddress != lastAddress) {
+            currentAddress = first.getOldMemoryAddress();
+            Structure structure = first.fetchData(dataRepository.getInputStream()).get(0);
+            result.add(structure);
+            first = (Pointer) structure.getFlatFieldValue("next");
+        }
+        return result;
+    }
 
 
-	/**
-	 * This method returns the amount of fields for the current structure.
-	 * @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<String, DataType>	PRIMARY_TYPES	= new HashMap<String, DataType>(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<String, DataType> PRIMARY_TYPES = new HashMap<String, DataType>(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);
+        }
+    }
 }
 }

+ 33 - 31
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
  * @author Marcin Roguski
  */
  */
 public class BlenderFileException extends Exception {
 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);
+    }
 }
 }

+ 76 - 76
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.DataRepository;
 import com.jme3.scene.plugins.blender.utils.Pointer;
 import com.jme3.scene.plugins.blender.utils.Pointer;
 
 
-
 /**
 /**
  * This class defines the methods to calculate certain aspects of animation and armature functionalities.
  * This class defines the methods to calculate certain aspects of animation and armature functionalities.
  * @author Marcin Roguski
  * @author Marcin Roguski
  */
  */
 public class ArmatureHelper extends com.jme3.scene.plugins.blender.helpers.v249.ArmatureHelper {
 public class ArmatureHelper extends com.jme3.scene.plugins.blender.helpers.v249.ArmatureHelper {
-	private static final Logger				LOGGER			= Logger.getLogger(ArmatureHelper.class.getName());
-	
-	/**
-	 * 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<Structure> 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<BoneTrack> tracks = new ArrayList<BoneTrack>();
-		for(Structure actionGroup : actionGroups) {
-			String name = actionGroup.getFieldValue("name").toString();
-			Integer boneIndex = bonesMap.get(name);
-			if(boneIndex != null) {
-				List<Structure> 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<Structure> 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<Structure> 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<BoneTrack> tracks = new ArrayList<BoneTrack>();
+        for (Structure actionGroup : actionGroups) {
+            String name = actionGroup.getFieldValue("name").toString();
+            Integer boneIndex = bonesMap.get(name);
+            if (boneIndex != null) {
+                List<Structure> 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<Structure> 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);
+    }
 }
 }

+ 37 - 36
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
  * @author Marcin Roguski
  */
  */
 public class CameraHelper extends com.jme3.scene.plugins.blender.helpers.v249.CameraHelper {
 public class CameraHelper extends com.jme3.scene.plugins.blender.helpers.v249.CameraHelper {
-	private static final Logger			LOGGER		= Logger.getLogger(CameraHelper.class.getName());
-	
-	/**
-	 * 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;
+    }
 }
 }

+ 24 - 23
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
  * @author Marcin Roguski
  */
  */
 public class ConstraintHelper extends com.jme3.scene.plugins.blender.helpers.v249.ConstraintHelper {
 public class ConstraintHelper extends com.jme3.scene.plugins.blender.helpers.v249.ConstraintHelper {
-	private static final Logger LOGGER = Logger.getLogger(ConstraintHelper.class.getName());
-	
-	/**
-	 * 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
+        }
+    }
 }
 }

+ 10 - 9
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
  * @author Marcin Roguski
  */
  */
 public class CurvesHelper extends com.jme3.scene.plugins.blender.helpers.v249.CurvesHelper {
 public class CurvesHelper extends com.jme3.scene.plugins.blender.helpers.v249.CurvesHelper {
-	/**
-	 * This constructor parses the given blender version and stores the result. Some functionalities may differ in
-	 * different blender versions.
-	 * @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);
+    }
 }
 }

+ 10 - 9
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
  * @author Marcin Roguski
  */
  */
 public class IpoHelper extends com.jme3.scene.plugins.blender.helpers.v249.IpoHelper {
 public class IpoHelper extends com.jme3.scene.plugins.blender.helpers.v249.IpoHelper {
-	/**
-	 * This constructor parses the given blender version and stores the result. Some functionalities may differ in
-	 * different blender versions.
-	 * @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);
+    }
 }
 }

+ 10 - 9
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
  * @author Marcin Roguski
  */
  */
 public class LightHelper extends com.jme3.scene.plugins.blender.helpers.v249.LightHelper {
 public class LightHelper extends com.jme3.scene.plugins.blender.helpers.v249.LightHelper {
-	/**
-	 * This constructor parses the given blender version and stores the result. Some functionalities may differ in
-	 * different blender versions.
-	 * @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);
+    }
 }
 }

+ 10 - 9
engine/src/blender/com/jme3/scene/plugins/blender/helpers/MaterialHelper.java

@@ -32,13 +32,14 @@
 package com.jme3.scene.plugins.blender.helpers;
 package com.jme3.scene.plugins.blender.helpers;
 
 
 public class MaterialHelper extends com.jme3.scene.plugins.blender.helpers.v249.MaterialHelper {
 public class MaterialHelper extends com.jme3.scene.plugins.blender.helpers.v249.MaterialHelper {
-	/**
-	 * This constructor parses the given blender version and stores the result. Some functionalities may differ in
-	 * different blender versions.
-	 * @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);
+    }
 }
 }

+ 10 - 9
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
  * @author Marcin Roguski
  */
  */
 public class MeshHelper extends com.jme3.scene.plugins.blender.helpers.v249.MeshHelper {
 public class MeshHelper extends com.jme3.scene.plugins.blender.helpers.v249.MeshHelper {
-	/**
-	 * This constructor parses the given blender version and stores the result. Some functionalities may differ in
-	 * different blender versions.
-	 * @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);
+    }
 }
 }

+ 10 - 9
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
  * @author Marcin Roguski
  */
  */
 public class ModifierHelper extends com.jme3.scene.plugins.blender.helpers.v249.ModifierHelper {
 public class ModifierHelper extends com.jme3.scene.plugins.blender.helpers.v249.ModifierHelper {
-	/**
-	 * This constructor parses the given blender version and stores the result. Some functionalities may differ in
-	 * different blender versions.
-	 * @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);
+    }
 }
 }

+ 10 - 9
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)
  * @author Marcin Roguski (Kaelthas)
  */
  */
 public class NoiseHelper extends com.jme3.scene.plugins.blender.helpers.v249.NoiseHelper {
 public class NoiseHelper extends com.jme3.scene.plugins.blender.helpers.v249.NoiseHelper {
-	/**
-	 * Constructor. Stores the blender version number and loads the constants needed for computations.
-	 * 
-	 * @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);
+    }
 }
 }

+ 10 - 9
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
  * @author Marcin Roguski
  */
  */
 public class ObjectHelper extends com.jme3.scene.plugins.blender.helpers.v249.ObjectHelper {
 public class ObjectHelper extends com.jme3.scene.plugins.blender.helpers.v249.ObjectHelper {
-	/**
-	 * This constructor parses the given blender version and stores the result. Some functionalities may differ in
-	 * different blender versions.
-	 * @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);
+    }
 }
 }

+ 10 - 9
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)
  * @author Marcin Roguski (Kaelthas)
  */
  */
 public class ParticlesHelper extends com.jme3.scene.plugins.blender.helpers.v249.ParticlesHelper {
 public class ParticlesHelper extends com.jme3.scene.plugins.blender.helpers.v249.ParticlesHelper {
-	/**
-	 * This constructor parses the given blender version and stores the result. Some functionalities may differ in
-	 * different blender versions.
-	 * @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);
+    }
 }
 }

+ 37 - 35
engine/src/blender/com/jme3/scene/plugins/blender/helpers/TextureHelper.java

@@ -44,39 +44,41 @@ import com.jme3.texture.Texture;
  * @author Marcin Roguski
  * @author Marcin Roguski
  */
  */
 public class TextureHelper extends com.jme3.scene.plugins.blender.helpers.v249.TextureHelper {
 public class TextureHelper extends com.jme3.scene.plugins.blender.helpers.v249.TextureHelper {
-	private static final Logger	LOGGER			= Logger.getLogger(TextureHelper.class.getName());
-	public static final int		TEX_POINTDENSITY	= 14;
-	public static final int		TEX_VOXELDATA		= 15;
-	/**
-	 * This constructor parses the given blender version and stores the result. Some functionalities may differ in
-	 * different blender versions.
-	 * @param blenderVersion
-	 *        the version read from the blend file
-	 */
-	public TextureHelper(String blenderVersion) {
-		super(blenderVersion);
-	}
-	
-	@Override
-	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;
+    }
 }
 }

+ 293 - 292
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
  * @author Marcin Roguski
  */
  */
 public class ArmatureHelper extends AbstractBlenderHelper {
 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<String, Integer>				bonesMap		= new HashMap<String, Integer>();
-	/** A map of bones and their old memory addresses. */
-	protected Map<Bone, Long>					bonesOMAs		= new HashMap<Bone, Long>();
-	/** This list contains bones hierarchy and their matrices. It is later converted into jme bones. */
-	protected List<BoneTransformationData>		boneDataRoots	= new ArrayList<BoneTransformationData>();
-	
-	/**
-	 * This constructor parses the given blender version and stores the result. Some functionalities may differ in
-	 * 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<String, Integer> bonesMap = new HashMap<String, Integer>();
+    /** A map of bones and their old memory addresses. */
+    protected Map<Bone, Long> bonesOMAs = new HashMap<Bone, Long>();
+    /** This list contains bones hierarchy and their matrices. It is later converted into jme bones. */
+    protected List<BoneTransformationData> boneDataRoots = new ArrayList<BoneTransformationData>();
 
 
-	/**
-	 * 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<Structure> 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<Integer, Integer> getGroupToBoneIndexMap(Structure defBaseStructure, DataRepository dataRepository) throws BlenderFileException {
-		Map<Integer, Integer> result = null;
-		if(bonesMap != null && bonesMap.size() != 0) {
-			result = new HashMap<Integer, Integer>();
-			List<Structure> 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<Structure> 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<Structure> 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<BoneTrack> tracks = new ArrayList<BoneTrack>();
-		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<Integer, Integer> getGroupToBoneIndexMap(Structure defBaseStructure, DataRepository dataRepository) throws BlenderFileException {
+        Map<Integer, Integer> result = null;
+        if (bonesMap != null && bonesMap.size() != 0) {
+            result = new HashMap<Integer, Integer>();
+            List<Structure> 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<Number> boneMat = (DynamicArray<Number>)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<Structure> 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<BoneTrack> tracks = new ArrayList<BoneTrack>();
+        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<Float> sizeArray = (DynamicArray<Float>) 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<Number> boneMat = (DynamicArray<Number>) 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<Structure> 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<Float> sizeArray = (DynamicArray<Float>) 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<Bone> 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<Structure> 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<Bone> 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<BoneTransformationData>	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<ArmatureHelper.BoneTransformationData>();
-			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<Bone> bones = new ArrayList<Bone>(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<BoneTransformationData> 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<ArmatureHelper.BoneTransformationData>();
+            if (this.parent != null) {
+                this.parent.children.add(this);
+            }
+        }
+    }
+
+    /**
+     * This method creates the whole bones structure. Assignes transformations to bones and combines children with
+     * 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<Bone> bones = new ArrayList<Bone>(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();
+    }
 }
 }

+ 41 - 40
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
  * @author Marcin Roguski
  */
  */
 public class CameraHelper extends AbstractBlenderHelper {
 public class CameraHelper extends AbstractBlenderHelper {
-	private static final Logger			LOGGER		= Logger.getLogger(CameraHelper.class.getName());
-	
-	protected static final int		DEFAULT_CAM_WIDTH			= 100;
-	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;
+    }
 }
 }

+ 722 - 701
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.AbstractBlenderHelper;
 import com.jme3.scene.plugins.blender.utils.DataRepository;
 import com.jme3.scene.plugins.blender.utils.DataRepository;
 import com.jme3.scene.plugins.blender.utils.Pointer;
 import com.jme3.scene.plugins.blender.utils.Pointer;
+import java.util.logging.Level;
 
 
 /**
 /**
  * This class should be used for constraint calculations.
  * This class should be used for constraint calculations.
  * @author Marcin Roguski
  * @author Marcin Roguski
  */
  */
 public class ConstraintHelper extends AbstractBlenderHelper {
 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<Long, Constraint[]>				constraints	= new HashMap<Long, Constraint[]>();
-
-	/**
-	 * 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<bones.length;++i) {
-						System.out.println(Arrays.toString(bones[i].track.getTranslations()));
-						System.out.println(Arrays.toString(bones[i].track.getRotations()));
-						System.out.println("===============================");
-					}
-					Quaternion rotation = new Quaternion();
-					int maxFrames = bones[0].track.getTimes().length;//all tracks should have the same amount of frames
-
-					for(int frame = 0; frame < maxFrames; ++frame) {
-						float error = IK_SOLVER_ERROR;
-						int iteration = 0;
-						while(error >= 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<bones.length;++i) {
-						System.out.println(Arrays.toString(bones[i].track.getTranslations()));
-						System.out.println(Arrays.toString(bones[i].track.getRotations()));
-						System.out.println("===============================");
-					}*/
-				}
-
-				/**
-				 * This method returns bones used for rotation calculations.
-				 * @param bone
-				 *        the bone to which the constraint is applied
-				 * @param skeleton
-				 *        the skeleton owning the bone and its ancestors
-				 * @param boneAnimation
-				 *        the bone animation data that stores the traces for the skeleton's bones
-				 * @return a list of bones to imitate the bone's movement during IK solving
-				 */
-				private CalculationBone[] getBonesToCalculate(Bone bone, Skeleton skeleton, BoneAnimation boneAnimation) {
-					List<CalculationBone> bonesList = new ArrayList<CalculationBone>();
-					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<String, Map<String, Ipo>> constraintsIpos = new HashMap<String, Map<String, Ipo>>();
-		Pointer pActions = (Pointer)objectStructure.getFieldValue("action");
-		if(!pActions.isNull()) {
-			List<Structure> actions = pActions.fetchData(dataRepository.getInputStream());
-			for(Structure action : actions) {
-				Structure chanbase = (Structure)action.getFieldValue("chanbase");
-				List<Structure> actionChannels = chanbase.evaluateListBase(dataRepository);
-				for(Structure actionChannel : actionChannels) {
-					Map<String, Ipo> ipos = new HashMap<String, Ipo>();
-					Structure constChannels = (Structure)actionChannel.getFieldValue("constraintChannels");
-					List<Structure> 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<Constraint> constraintsList = new ArrayList<Constraint>();
-		Pointer pPose = (Pointer)objectStructure.getFieldValue("pose");//TODO: what if the object has two armatures ????
-		if(!pPose.isNull()) {
-			//getting pose channels
-			List<Structure> 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<Structure> 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<Structure> 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<Structure> 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<Long, Constraint[]> constraints = new HashMap<Long, Constraint[]>();
+
+    /**
+     * 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<bones.length;++i) {
+                    System.out.println(Arrays.toString(bones[i].track.getTranslations()));
+                    System.out.println(Arrays.toString(bones[i].track.getRotations()));
+                    System.out.println("===============================");
+                    }
+                    Quaternion rotation = new Quaternion();
+                    int maxFrames = bones[0].track.getTimes().length;//all tracks should have the same amount of frames
+                    
+                    for(int frame = 0; frame < maxFrames; ++frame) {
+                    float error = IK_SOLVER_ERROR;
+                    int iteration = 0;
+                    while(error >= 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<bones.length;++i) {
+                    System.out.println(Arrays.toString(bones[i].track.getTranslations()));
+                    System.out.println(Arrays.toString(bones[i].track.getRotations()));
+                    System.out.println("===============================");
+                    }*/
+                }
+
+                /**
+                 * This method returns bones used for rotation calculations.
+                 * @param bone
+                 *        the bone to which the constraint is applied
+                 * @param skeleton
+                 *        the skeleton owning the bone and its ancestors
+                 * @param boneAnimation
+                 *        the bone animation data that stores the traces for the skeleton's bones
+                 * @return a list of bones to imitate the bone's movement during IK solving
+                 */
+                private CalculationBone[] getBonesToCalculate(Bone bone, Skeleton skeleton, BoneAnimation boneAnimation) {
+                    List<CalculationBone> bonesList = new ArrayList<CalculationBone>();
+                    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<String, Map<String, Ipo>> constraintsIpos = new HashMap<String, Map<String, Ipo>>();
+        Pointer pActions = (Pointer) objectStructure.getFieldValue("action");
+        if (!pActions.isNull()) {
+            List<Structure> actions = pActions.fetchData(dataRepository.getInputStream());
+            for (Structure action : actions) {
+                Structure chanbase = (Structure) action.getFieldValue("chanbase");
+                List<Structure> actionChannels = chanbase.evaluateListBase(dataRepository);
+                for (Structure actionChannel : actionChannels) {
+                    Map<String, Ipo> ipos = new HashMap<String, Ipo>();
+                    Structure constChannels = (Structure) actionChannel.getFieldValue("constraintChannels");
+                    List<Structure> 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<Constraint> constraintsList = new ArrayList<Constraint>();
+        Pointer pPose = (Pointer) objectStructure.getFieldValue("pose");//TODO: what if the object has two armatures ????
+        if (!pPose.isNull()) {
+            //getting pose channels
+            List<Structure> 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<Structure> 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<Structure> 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<Structure> 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();
+        }
+    }
 }
 }

+ 548 - 548
engine/src/blender/com/jme3/scene/plugins/blender/helpers/v249/CurvesHelper.java

@@ -39,552 +39,552 @@ import com.jme3.util.BufferUtils;
  * @author Marcin Roguski
  * @author Marcin Roguski
  */
  */
 public class CurvesHelper extends AbstractBlenderHelper {
 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<Geometry> 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<Structure> nurbStructures = ((Structure)curveStructure.getFieldValue("nurb")).evaluateListBase(dataRepository);
-		Map<Number, List<Structure>> nurbs = new HashMap<Number, List<Structure>>();
-		for(Structure nurb : nurbStructures) {
-			Number matNumber = (Number) nurb.getFieldValue("mat_nr");
-			List<Structure> nurbList = nurbs.get(matNumber);
-			if(nurbList==null) {
-				nurbList = new ArrayList<Structure>();
-				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<Geometry> 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<Vector3f> conrtolPoints = new ArrayList<Vector3f>(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<Geometry>(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<Geometry>(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<Geometry> result = new ArrayList<Geometry>(nurbs.size());
-		for(Entry<Number, List<Structure>> nurbEntry : nurbs.entrySet()) {
-			for(Structure nurb : nurbEntry.getValue()) {
-				int type = ((Number)nurb.getFieldValue("type")).intValue();
-				List<Geometry> 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<Geometry> loadBezierCurve(Vector3f loc, Structure nurb, List<Geometry> bevelObject, Curve taperObject, 
-											 DataRepository dataRepository) throws BlenderFileException {
-		Pointer pBezierTriple = (Pointer) nurb.getFieldValue("bezt");
-		List<Geometry> result = new ArrayList<Geometry>();
-		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<Vector3f> 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<Geometry> loadNurb(Vector3f loc, Structure nurb, List<Geometry> bevelObject, Curve taperObject, 
-			 						  DataRepository dataRepository) throws BlenderFileException {
-		//loading the knots
-		List<Float>[] 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<Float>(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<Structure> bPoints = ((Pointer)nurb.getFieldValue("bp")).fetchData(dataRepository.getInputStream());
-		List<List<Vector4f>> controlPoints = new ArrayList<List<Vector4f>>(pntsV);
-		for(int i=0;i<pntsV;++i) {
-			List<Vector4f> uControlPoints = new ArrayList<Vector4f>(pntsU);
-			for(int j=0;j<pntsU;++j) {
-				DynamicArray<Float> vec = (DynamicArray<Float>)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<Geometry> 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<Geometry>(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<Geometry>(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<Geometry> applyBevelAndTaper(Curve curve, List<Geometry> 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<Vector3f, Vector3f> normalMap = new HashMap<Vector3f, Vector3f>();//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<Geometry> result = new ArrayList<Geometry>(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<Structure> 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<Vector3f> 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<Number> locArray = (DynamicArray<Number>)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<Geometry> 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<Structure> nurbStructures = ((Structure) curveStructure.getFieldValue("nurb")).evaluateListBase(dataRepository);
+        Map<Number, List<Structure>> nurbs = new HashMap<Number, List<Structure>>();
+        for (Structure nurb : nurbStructures) {
+            Number matNumber = (Number) nurb.getFieldValue("mat_nr");
+            List<Structure> nurbList = nurbs.get(matNumber);
+            if (nurbList == null) {
+                nurbList = new ArrayList<Structure>();
+                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<Geometry> 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<Vector3f> conrtolPoints = new ArrayList<Vector3f>(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<Geometry>(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<Geometry>(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<Geometry> result = new ArrayList<Geometry>(nurbs.size());
+        for (Entry<Number, List<Structure>> nurbEntry : nurbs.entrySet()) {
+            for (Structure nurb : nurbEntry.getValue()) {
+                int type = ((Number) nurb.getFieldValue("type")).intValue();
+                List<Geometry> 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<Geometry> loadBezierCurve(Vector3f loc, Structure nurb, List<Geometry> bevelObject, Curve taperObject,
+            DataRepository dataRepository) throws BlenderFileException {
+        Pointer pBezierTriple = (Pointer) nurb.getFieldValue("bezt");
+        List<Geometry> result = new ArrayList<Geometry>();
+        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<Vector3f> 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<Geometry> loadNurb(Vector3f loc, Structure nurb, List<Geometry> bevelObject, Curve taperObject,
+            DataRepository dataRepository) throws BlenderFileException {
+        //loading the knots
+        List<Float>[] 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<Float>(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<Structure> bPoints = ((Pointer) nurb.getFieldValue("bp")).fetchData(dataRepository.getInputStream());
+        List<List<Vector4f>> controlPoints = new ArrayList<List<Vector4f>>(pntsV);
+        for (int i = 0; i < pntsV; ++i) {
+            List<Vector4f> uControlPoints = new ArrayList<Vector4f>(pntsU);
+            for (int j = 0; j < pntsU; ++j) {
+                DynamicArray<Float> vec = (DynamicArray<Float>) 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<Geometry> 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<Geometry>(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<Geometry>(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<Geometry> applyBevelAndTaper(Curve curve, List<Geometry> 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<Vector3f, Vector3f> normalMap = new HashMap<Vector3f, Vector3f>();//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<Geometry> result = new ArrayList<Geometry>(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<Structure> 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<Vector3f> 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<Number> locArray = (DynamicArray<Number>) 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());
+        }
+    }
 }
 }

+ 85 - 85
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
  * @author Marcin Roguski
  */
  */
 public class IpoHelper extends AbstractBlenderHelper {
 public class IpoHelper extends AbstractBlenderHelper {
-	/**
-	 * This constructor parses the given blender version and stores the result. Some functionalities may differ in
-	 * different blender versions.
-	 * @param blenderVersion
-	 *        the version read from the blend file
-	 */
-	public IpoHelper(String blenderVersion) {
-		super(blenderVersion);
-	}
 
 
-	/**
-	 * This method creates an ipo object used for interpolation calculations.
-	 * @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<Structure> 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<Structure> bezTriples = pBezTriple.fetchData(dataRepository.getInputStream());
-				int type = ((Number)curve.getFieldValue("adrcode")).intValue();
-				bezierCurves[frame++] = new BezierCurve(type, bezTriples, 2);
-			}
-			curves.clear();
-			result = new Ipo(bezierCurves);
-			dataRepository.addLoadedFeatures(ipoStructure.getOldMemoryAddress(), ipoStructure.getName(), ipoStructure, result);
-		}
-		return result;
-	}
+    /**
+     * This method creates an ipo 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<Structure> 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<Structure> bezTriples = pBezTriple.fetchData(dataRepository.getInputStream());
+                int type = ((Number) curve.getFieldValue("adrcode")).intValue();
+                bezierCurves[frame++] = new BezierCurve(type, bezTriples, 2);
+            }
+            curves.clear();
+            result = new Ipo(bezierCurves);
+            dataRepository.addLoadedFeatures(ipoStructure.getOldMemoryAddress(), ipoStructure.getName(), ipoStructure, result);
+        }
+        return result;
+    }
 
 
-	
+    /**
+     * This method creates an ipo with only a single value. No track type is specified so do not use it for calculating
+     * tracks.
+     * @param constValue
+     *        the value of this ipo
+     * @return constant ipo
+     */
+    public Ipo createIpo(float constValue) {
+        return new ConstIpo(constValue);
+    }
 
 
-	/**
-	 * Ipo constant curve. This is a curve with only one value and no specified type. This type of ipo cannot be used to
-	 * calculate tracks. It should only be used to calculate single value for a given frame.
-	 * @author Marcin Roguski
-	 */
-	private class ConstIpo extends Ipo {
-		/** The constant value of this ipo. */
-		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!");
+        }
+    }
 }
 }

+ 47 - 46
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
  * @author Marcin Roguski
  */
  */
 public class LightHelper extends AbstractBlenderHelper {
 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;
+    }
 }
 }

+ 642 - 636
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;
 import com.jme3.util.BufferUtils;
 
 
 public class MaterialHelper extends AbstractBlenderHelper {
 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<Integer, IAlphaMask>	alphaMasks				= new HashMap<Integer, IAlphaMask>();
-
-	/**
-	 * 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<Pointer> mtexs = (DynamicArray<Pointer>) structure.getFieldValue("mtex");
-			for (int i = 0; i < mtexs.getTotalSize(); ++i) {
-				Pointer p = mtexs.get(i);
-				if (!p.isNull()) {
-					List<Structure> 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<Structure> 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<String, Texture> textures = new HashMap<String, Texture>(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<String, Texture> 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<String, Texture> 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 <b>true</b> if the texture exists in the material and <B>false</b> 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<Structure> 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<Integer, AlphaMask> alphaMasks = new HashMap<Integer, AlphaMask>();
+
+    /**
+     * 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<Pointer> mtexs = (DynamicArray<Pointer>) structure.getFieldValue("mtex");
+            for (int i = 0; i < mtexs.getTotalSize(); ++i) {
+                Pointer p = mtexs.get(i);
+                if (!p.isNull()) {
+                    List<Structure> 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<Structure> 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<String, Texture> textures = new HashMap<String, Texture>(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<String, Texture> 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<String, Texture> 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 <b>true</b> if the texture exists in the material and <B>false</b> 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<Structure> 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);
+    }
 }
 }

+ 530 - 529
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)
  * @author Marcin Roguski (Kaelthas)
  */
  */
 public class MeshHelper extends AbstractBlenderHelper {
 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<Geometry> toMesh(Structure structure, DataRepository dataRepository) throws BlenderFileException {
-		List<Geometry> geometries = (List<Geometry>) dataRepository.getLoadedFeature(structure.getOldMemoryAddress(),
-				LoadedFeatureDataType.LOADED_FEATURE);
-		if (geometries != null) {
-			List<Geometry> copiedGeometries = new ArrayList<Geometry>(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<float[]> 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<Integer, List<Integer>> meshesMap = new HashMap<Integer, List<Integer>>();
-		Pointer pMFace = (Pointer) structure.getFieldValue("mface");
-		List<Structure> mFaces = pMFace.fetchData(dataRepository.getInputStream());
-
-		Pointer pMTFace = (Pointer) structure.getFieldValue("mtface");
-		List<Vector2f> uvCoordinates = null;
-		List<Structure> 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<Vector2f>();// TODO: calculate the amount of coordinates if possible
-		}
-
-		// normalMap merges normals of faces that will be rendered smooth
-		Map<Vector3f, Vector3f> normalMap = new HashMap<Vector3f, Vector3f>(verticesAmount);
-
-		List<Vector3f> normalList = new ArrayList<Vector3f>();
-		List<Vector3f> vertexList = new ArrayList<Vector3f>();
-		Map<Integer, Texture> materialNumberToTexture = new HashMap<Integer, Texture>();// 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<Integer, List<Integer>> vertexReferenceMap = new HashMap<Integer, List<Integer>>(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<Number> 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<Number>) 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<Integer> indexList = meshesMap.get(materialNumber);
-			if (indexList == null) {
-				indexList = new ArrayList<Integer>();
-				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<Structure> 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<Integer, Integer> 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<Geometry>(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<Integer, List<Integer>> meshEntry : meshesMap.entrySet()) {
-			Mesh mesh = new Mesh();
-
-			// creating vertices indices for this mesh
-			List<Integer> 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<Vector3f, Vector3f> 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<Integer, List<Integer>> vertexReferenceMap) {
-		List<Integer> referenceList = vertexReferenceMap.get(Integer.valueOf(basicVertexIndex));
-		if (referenceList == null) {
-			referenceList = new ArrayList<Integer>();
-			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<float[]> getVerticesColors(Structure meshStructure, DataRepository dataRepository) throws BlenderFileException {
-		Pointer pMCol = (Pointer) meshStructure.getFieldValue("mcol");
-		List<float[]> verticesColors = null;
-		List<Structure> mCol = null;
-		if (!pMCol.isNull()) {
-			verticesColors = new LinkedList<float[]>();
-			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<Structure> mVerts = pMVert.fetchData(dataRepository.getInputStream());
-		for (int i = 0; i < verticesAmount; ++i) {
-			DynamicArray<Number> coordinates = (DynamicArray<Number>) 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<Integer, List<Integer>> vertexReferenceMap, Map<Integer, Integer> 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<Structure> 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<Integer> 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<Structure> 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<Integer> 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<Geometry> toMesh(Structure structure, DataRepository dataRepository) throws BlenderFileException {
+        List<Geometry> geometries = (List<Geometry>) dataRepository.getLoadedFeature(structure.getOldMemoryAddress(),
+                LoadedFeatureDataType.LOADED_FEATURE);
+        if (geometries != null) {
+            List<Geometry> copiedGeometries = new ArrayList<Geometry>(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<float[]> 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<Integer, List<Integer>> meshesMap = new HashMap<Integer, List<Integer>>();
+        Pointer pMFace = (Pointer) structure.getFieldValue("mface");
+        List<Structure> mFaces = pMFace.fetchData(dataRepository.getInputStream());
+
+        Pointer pMTFace = (Pointer) structure.getFieldValue("mtface");
+        List<Vector2f> uvCoordinates = null;
+        List<Structure> 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<Vector2f>();// TODO: calculate the amount of coordinates if possible
+        }
+
+        // normalMap merges normals of faces that will be rendered smooth
+        Map<Vector3f, Vector3f> normalMap = new HashMap<Vector3f, Vector3f>(verticesAmount);
+
+        List<Vector3f> normalList = new ArrayList<Vector3f>();
+        List<Vector3f> vertexList = new ArrayList<Vector3f>();
+        Map<Integer, Texture> materialNumberToTexture = new HashMap<Integer, Texture>();// 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<Integer, List<Integer>> vertexReferenceMap = new HashMap<Integer, List<Integer>>(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<Number> 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<Number>) 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<Integer> indexList = meshesMap.get(materialNumber);
+            if (indexList == null) {
+                indexList = new ArrayList<Integer>();
+                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<Structure> 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<Integer, Integer> 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<Geometry>(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<Integer, List<Integer>> meshEntry : meshesMap.entrySet()) {
+            Mesh mesh = new Mesh();
+
+            // creating vertices indices for this mesh
+            List<Integer> 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<Vector3f, Vector3f> 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<Integer, List<Integer>> vertexReferenceMap) {
+        List<Integer> referenceList = vertexReferenceMap.get(Integer.valueOf(basicVertexIndex));
+        if (referenceList == null) {
+            referenceList = new ArrayList<Integer>();
+            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<float[]> getVerticesColors(Structure meshStructure, DataRepository dataRepository) throws BlenderFileException {
+        Pointer pMCol = (Pointer) meshStructure.getFieldValue("mcol");
+        List<float[]> verticesColors = null;
+        List<Structure> mCol = null;
+        if (!pMCol.isNull()) {
+            verticesColors = new LinkedList<float[]>();
+            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<Structure> mVerts = pMVert.fetchData(dataRepository.getInputStream());
+        for (int i = 0; i < verticesAmount; ++i) {
+            DynamicArray<Number> coordinates = (DynamicArray<Number>) 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<Integer, List<Integer>> vertexReferenceMap, Map<Integer, Integer> 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<Structure> 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<Integer> 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<Structure> 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<Integer> 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;
+    }
 }
 }

+ 462 - 461
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
  * @author Marcin Roguski
  */
  */
 public class ModifierHelper extends AbstractBlenderHelper {
 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<Structure> 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<String, Object> params = new HashMap<String, Object>();
-				
-				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<Number> offsetArray = (DynamicArray<Number>)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<Number> scaleArray = (DynamicArray<Number>)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<String> animationNames = dataRepository.getBlenderKey().getAnimationNames(objectName);
-					if(animationNames != null && animationNames.size() > 0) {
-						ArrayList<BoneAnimation> animations = new ArrayList<BoneAnimation>();
-						List<FileBlockHeader> 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<Mesh> meshes = new ArrayList<Mesh>();
-		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<BoneAnimation> 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<String, BoneAnimation> anims = new HashMap<String, BoneAnimation>();
-			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<Mesh> meshesList = new ArrayList<Mesh>();
-			List<Spatial> 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<String, BoneAnimation> animations = new HashMap<String, BoneAnimation>();
-				for(String animationName : control.getAnimationNames()) {
-					animations.put(animationName, control.getAnim(animationName));
-				}
-				for(Entry<String, BoneAnimation> 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<String, Object> modifierData = (Map<String, Object>) 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<Bone> bones = new ArrayList<Bone>(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<Structure> 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<String, Object> params = new HashMap<String, Object>();
+
+                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<Number> offsetArray = (DynamicArray<Number>) 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<Number> scaleArray = (DynamicArray<Number>) 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<String> animationNames = dataRepository.getBlenderKey().getAnimationNames(objectName);
+                    if (animationNames != null && animationNames.size() > 0) {
+                        ArrayList<BoneAnimation> animations = new ArrayList<BoneAnimation>();
+                        List<FileBlockHeader> 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<Mesh> meshes = new ArrayList<Mesh>();
+        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<BoneAnimation> 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<String, BoneAnimation> anims = new HashMap<String, BoneAnimation>();
+            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<Mesh> meshesList = new ArrayList<Mesh>();
+            List<Spatial> 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<String, BoneAnimation> animations = new HashMap<String, BoneAnimation>();
+                for (String animationName : control.getAnimationNames()) {
+                    animations.put(animationName, control.getAnim(animationName));
+                }
+                for (Entry<String, BoneAnimation> 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<String, Object> modifierData = (Map<String, Object>) 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<Bone> bones = new ArrayList<Bone>(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()]));
+    }
 }
 }

+ 1500 - 1473
engine/src/blender/com/jme3/scene/plugins/blender/helpers/v249/NoiseHelper.java

@@ -28,7 +28,6 @@
  * ***** END GPL LICENSE BLOCK *****
  * ***** END GPL LICENSE BLOCK *****
  *
  *
  */
  */
-
 package com.jme3.scene.plugins.blender.helpers.v249;
 package com.jme3.scene.plugins.blender.helpers.v249;
 
 
 import java.io.IOException;
 import java.io.IOException;
@@ -56,1476 +55,1504 @@ import com.jme3.scene.plugins.blender.utils.DataRepository;
  * @author Marcin Roguski (Kaelthas)
  * @author Marcin Roguski (Kaelthas)
  */
  */
 public class NoiseHelper extends AbstractBlenderHelper {
 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<Integer, AbstractNoiseFunc>	noiseFunctions		= new HashMap<Integer, AbstractNoiseFunc>();
-	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<Integer, IDistanceFunc>		distanceFunctions	= new HashMap<Integer, NoiseHelper.IDistanceFunc>();
-	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<Integer, IMusgraveFunction>	musgraveFunctions	= new HashMap<Integer, NoiseHelper.IMusgraveFunction>();
-	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 <b>true</b> if calculation suceedess and <b>false</b> 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<Integer, AbstractNoiseFunc> noiseFunctions = new HashMap<Integer, AbstractNoiseFunc>();
+
+    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<Integer, DistanceFunc> distanceFunctions = new HashMap<Integer, NoiseHelper.DistanceFunc>();
+
+    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<Integer, MusgraveFunction> musgraveFunctions = new HashMap<Integer, NoiseHelper.MusgraveFunction>();
+
+    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 <b>true</b> if calculation suceedess and <b>false</b> 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);
+    }
 }
 }

+ 194 - 200
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
  * @author Marcin Roguski
  */
  */
 public abstract class AbstractInfluenceFunction {
 public abstract class AbstractInfluenceFunction {
-	protected static final Logger	LOGGER				= Logger.getLogger(AbstractInfluenceFunction.class.getName());
 
 
-	protected static final float	IK_SOLVER_ERROR		= 0.5f;
-
-	//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());
+        }
+    }
 }
 }

+ 115 - 115
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
  * @author Marcin Roguski
  */
  */
 public class BezierCurve {
 public class BezierCurve {
-	public static final int X_VALUE = 0;
-	public static final int	Y_VALUE	= 1;
-	public static final int Z_VALUE = 2;
 
 
-	/** 
-	 * 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<Structure> 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<Number> vec = (DynamicArray<Number>)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<Structure> 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<Number> vec = (DynamicArray<Number>) 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<Vector3f> getControlPoints() {
-		List<Vector3f> controlPoints = new ArrayList<Vector3f>(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;
-	}
+    /**
+     * 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];
+    }
 
 
-	@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 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 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] + ")]";
-		}
-	}
+    /**
+     * This method returns a list of control points for this curve.
+     * @return a list of control points for this curve.
+     */
+    public List<Vector3f> getControlPoints() {
+        List<Vector3f> controlPoints = new ArrayList<Vector3f>(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] + ")]";
+        }
+    }
 }
 }

+ 134 - 134
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
  * @author Marcin Roguski
  */
  */
 public class Constraint {
 public class Constraint {
-	/** The type of this constraint. */
-	private final ConstraintType			type;
-	/** The name of this constraint. */
-	private final String					name;
-	/** The old memory address of the constraint's owner. */
-	private final Long						boneOMA;
 
 
-	private final Space						ownerSpace;
+    /** The type of this constraint. */
+    private final ConstraintType type;
+    /** The name of this constraint. */
+    private final String name;
+    /** The old memory address of the constraint's owner. */
+    private final Long boneOMA;
+    private final Space ownerSpace;
+    private final Space targetSpace;
+    /** The structure with constraint's data. */
+    private final Structure data;
+    /** The ipo object defining influence. */
+    private final Ipo ipo;
+    /** The influence function of this constraint. */
+    private final AbstractInfluenceFunction influenceFunction;
 
 
-	private final Space						targetSpace;
-	/** The structure with constraint's data. */
-	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;
+            }
+        }
+    }
 }
 }

+ 126 - 124
engine/src/blender/com/jme3/scene/plugins/blender/structures/ConstraintType.java

@@ -10,134 +10,136 @@ import java.util.Map;
  * @author Marcin Roguski
  * @author Marcin Roguski
  */
  */
 public enum ConstraintType {
 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<String, ConstraintType>	typesMap	= new HashMap<String, ConstraintType>(ConstraintType.values().length);
-	/** The map containing class names and types of constraints. */
-	private static Map<Integer, ConstraintType>	idsMap	= new HashMap<Integer, ConstraintType>(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<String, ConstraintType> typesMap = new HashMap<String, ConstraintType>(ConstraintType.values().length);
+    /** The map containing class names and types of constraints. */
+    private static final Map<Integer, ConstraintType> idsMap = new HashMap<Integer, ConstraintType>(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();
+    }
 }
 }

+ 156 - 156
engine/src/blender/com/jme3/scene/plugins/blender/structures/Ipo.java

@@ -10,167 +10,167 @@ import com.jme3.math.Vector3f;
  * @author Marcin Roguski
  * @author Marcin Roguski
  */
  */
 public class Ipo {
 public class Ipo {
-	public static final int AC_LOC_X = 1;
-	public static final int AC_LOC_Y = 2;
-	public static final int AC_LOC_Z = 3;
-	public static final int OB_ROT_X = 7;
-	public static final int OB_ROT_Y = 8;
-	public static final int OB_ROT_Z = 9;
-	public static final int AC_SIZE_X = 13;
-	public static final int AC_SIZE_Y = 14;
-	public static final int AC_SIZE_Z = 15;
-	public static final int AC_QUAT_W = 25;
-	public static final int AC_QUAT_X = 26;
-	public static final int AC_QUAT_Y = 27;
-	public static final int AC_QUAT_Z = 28;
-	
-	/** A list of bezier curves for this interpolation object. */
-	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;
+    }
 }
 }

+ 44 - 43
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)
  * @author Marcin Roguski (Kaelthas)
  */
  */
 public class Modifier {
 public class Modifier {
-	public static final String	ARRAY_MODIFIER_DATA		= "ArrayModifierData";
-	public static final String	ARMATURE_MODIFIER_DATA	= "ArmatureModifierData";
-	public static final String	PARTICLE_MODIFIER_DATA	= "ParticleSystemModifierData";
 
 
-	/** Blender's type of modifier. */
-	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;
+    }
 }
 }

+ 57 - 55
engine/src/blender/com/jme3/scene/plugins/blender/utils/AbstractBlenderHelper.java

@@ -42,61 +42,63 @@ import com.jme3.util.BufferUtils;
  * @author Marcin Roguski
  * @author Marcin Roguski
  */
  */
 public abstract class AbstractBlenderHelper {
 public abstract class AbstractBlenderHelper {
-	/** The version of the blend file. */
-	protected final int blenderVersion;
 
 
-	/**
-	 * 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 <b>true</b> if the text is blank and <b>false</b> otherwise
-	 */
-	protected boolean isBlank(String text) {
-		if (text != null) {
-			for (int i = 0; i < text.length(); ++i) {
-				if (!Character.isWhitespace(text.charAt(i))) {
-					return false;
-				}
-			}
-		}
-		return true;
-	}
-	
-	/**
-	 * Generate a new FloatBuffer using the given array of float[4] objects. The FloatBuffer will be 4 * data.length
-	 * long and contain the vector data as data[0][0], data[0][1], data[0][2], data[0][3], data[1][0]... etc.
-	 * @param data
-	 *        list of float[4] objects to place into a new FloatBuffer
-	 */
-	protected FloatBuffer createFloatBuffer(List<float[]> data) {
-		if(data == null) {
-			return null;
-		}
-		FloatBuffer buff = BufferUtils.createFloatBuffer(4 * data.size());
-		for(float[] v : data) {
-			if(v != null) {
-				buff.put(v[0]).put(v[1]).put(v[2]).put(v[3]);
-			} else {
-				buff.put(0).put(0).put(0).put(0);
-			}
-		}
-		buff.flip();
-		return buff;
-	}
+    /**
+     * 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 <b>true</b> if the text is blank and <b>false</b> otherwise
+     */
+    protected boolean isBlank(String text) {
+        if (text != null) {
+            for (int i = 0; i < text.length(); ++i) {
+                if (!Character.isWhitespace(text.charAt(i))) {
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Generate a new FloatBuffer using the given array of float[4] objects. The FloatBuffer will be 4 * data.length
+     * long and contain the vector data as data[0][0], data[0][1], data[0][2], data[0][3], data[1][0]... etc.
+     * @param data
+     *        list of float[4] objects to place into a new FloatBuffer
+     */
+    protected FloatBuffer createFloatBuffer(List<float[]> data) {
+        if (data == null) {
+            return null;
+        }
+        FloatBuffer buff = BufferUtils.createFloatBuffer(4 * data.size());
+        for (float[] v : data) {
+            if (v != null) {
+                buff.put(v[0]).put(v[1]).put(v[2]).put(v[3]);
+            } else {
+                buff.put(0).put(0).put(0).put(0);
+            }
+        }
+        buff.flip();
+        return buff;
+    }
 }
 }

+ 50 - 49
engine/src/blender/com/jme3/scene/plugins/blender/utils/IBlenderConverter.java → 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
  *        the type of material element
  */
  */
 //TODO: ujednolicić wyrzucane wyjątki
 //TODO: ujednolicić wyrzucane wyjątki
-public interface IBlenderConverter<NodeType, CameraType, LightType, ObjectType, MeshType, MaterialType> {
-	/**
-	 * This method reads converts the given structure into scene. The given structure needs to be filled with the
-	 * appropriate data.
-	 * @param structure
-	 *        the structure we read the scene from
-	 * @return the scene feature
-	 */
-	NodeType toScene(Structure structure);
+public interface BlenderConverter<NodeType, CameraType, LightType, ObjectType, MeshType, MaterialType> {
 
 
-	/**
-	 * 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;
 }
 }

+ 309 - 309
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
  * @author Marcin Roguski
  */
  */
 public class BlenderInputStream extends InputStream {
 public class BlenderInputStream extends InputStream {
-	private static final Logger	LOGGER				= Logger.getLogger(BlenderInputStream.class.getName());
 
 
-	/** The default size of the blender buffer. */
-	private static final 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<bytes.length;++i) {
-			bytes[i] = (byte) this.readByte();
-		}
-		return bytes.length;
-	}
-	
-	/**
-	 * This method reads 2-byte number from the stream.
-	 * @return a number from the stream (2 bytes read)
-	 */
-	public int readShort() {
-		int part1 = this.readByte();
-		int part2 = this.readByte();
-		if(endianess == 'v') {
-			return (part2 << 8) + part1;
-		} else {
-			return (part1 << 8) + part2;
-		}
-	}
+    @Override
+    public int read() throws IOException {
+        return this.readByte();
+    }
 
 
-	/**
-	 * This method reads 4-byte number from the stream.
-	 * @return a number from the stream (4 bytes read)
-	 */
-	public int readInt() {
-		int part1 = this.readByte();
-		int part2 = this.readByte();
-		int part3 = this.readByte();
-		int part4 = this.readByte();
-		if(endianess == 'v') {
-			return (part4 << 24) + (part3 << 16) + (part2 << 8) + part1;
-		} else {
-			return (part1 << 24) + (part2 << 16) + (part3 << 8) + part4;
-		}
-	}
+    /**
+     * 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 4-byte floating point number (float) from the stream.
-	 * @return a number from the stream (4 bytes read)
-	 */
-	public float readFloat() {
-		int intValue = this.readInt();
-		return Float.intBitsToFloat(intValue);
-	}
+    /**
+     * 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 < bytes.length; ++i) {
+            bytes[i] = (byte) this.readByte();
+        }
+        return bytes.length;
+    }
 
 
-	/**
-	 * This method reads 8-byte number from the stream.
-	 * @return a number from the stream (8 bytes read)
-	 */
-	public long readLong() {
-		long part1 = this.readInt();
-		long part2 = this.readInt();
-		long result = -1;
-		if(endianess == 'v') {
-			result = part2 << 32 | part1;
-		} else {
-			result = part1 << 32 | part2;
-		}
-		return result;
-	}
+    /**
+     * This method reads 2-byte number from the stream.
+     * @return a number from the stream (2 bytes read)
+     */
+    public int readShort() {
+        int part1 = this.readByte();
+        int part2 = this.readByte();
+        if (endianess == 'v') {
+            return (part2 << 8) + part1;
+        } else {
+            return (part1 << 8) + part2;
+        }
+    }
 
 
-	/**
-	 * This method reads 8-byte floating point number (double) from the stream.
-	 * @return a number from the stream (8 bytes read)
-	 */
-	public double readDouble() {
-		long longValue = this.readLong();
-		return Double.longBitsToDouble(longValue);
-	}
+    /**
+     * This method reads 4-byte number from the stream.
+     * @return a number from the stream (4 bytes read)
+     */
+    public int readInt() {
+        int part1 = this.readByte();
+        int part2 = this.readByte();
+        int part3 = this.readByte();
+        int part4 = this.readByte();
+        if (endianess == 'v') {
+            return (part4 << 24) + (part3 << 16) + (part2 << 8) + part1;
+        } else {
+            return (part1 << 24) + (part2 << 16) + (part3 << 8) + part4;
+        }
+    }
 
 
-	/**
-	 * This method reads the pointer value. Depending on the pointer size defined in the header, the stream reads either
-	 * 4 or 8 bytes of data.
-	 * @return the pointer value
-	 */
-	public long readPointer() {
-		if(pointerSize == 4) {
-			return this.readInt();
-		}
-		return this.readLong();
-	}
+    /**
+     * This method reads 4-byte floating point number (float) from the stream.
+     * @return a number from the stream (4 bytes read)
+     */
+    public float readFloat() {
+        int intValue = this.readInt();
+        return Float.intBitsToFloat(intValue);
+    }
 
 
-	/**
-	 * This method reads the string. It assumes the string is terminated with zero in the stream.
-	 * @return the string read from the stream
-	 */
-	public String readString() {
-		StringBuilder stringBuilder = new StringBuilder();
-		int data = this.readByte();
-		while(data != 0) {
-			stringBuilder.append((char)data);
-			data = this.readByte();
-		}
-		return stringBuilder.toString();
-	}
+    /**
+     * This method reads 8-byte number from the stream.
+     * @return a number from the stream (8 bytes read)
+     */
+    public long readLong() {
+        long part1 = this.readInt();
+        long part2 = this.readInt();
+        long result = -1;
+        if (endianess == 'v') {
+            result = part2 << 32 | part1;
+        } else {
+            result = part1 << 32 | part2;
+        }
+        return result;
+    }
 
 
-	/**
-	 * This method sets the current position of the read cursor.
-	 * @param position
-	 *        the position of the read cursor
-	 */
-	public void setPosition(int position) {
-		this.position = position;
-	}
+    /**
+     * This method reads 8-byte floating point number (double) from the stream.
+     * @return a number from the stream (8 bytes read)
+     */
+    public double readDouble() {
+        long longValue = this.readLong();
+        return Double.longBitsToDouble(longValue);
+    }
 
 
-	/**
-	 * This method returns the position of the read cursor.
-	 * @return the position of the read cursor
-	 */
-	public int getPosition() {
-		return position;
-	}
+    /**
+     * This method reads the pointer value. Depending on the pointer size defined in the header, the stream reads either
+     * 4 or 8 bytes of data.
+     * @return the pointer value
+     */
+    public long readPointer() {
+        if (pointerSize == 4) {
+            return this.readInt();
+        }
+        return this.readLong();
+    }
 
 
-	/**
-	 * This method returns the blender version number where the file was created.
-	 * @return blender version number
-	 */
-	public String getVersionNumber() {
-		return versionNumber;
-	}
+    /**
+     * This method reads the string. It assumes the string is terminated with zero in the stream.
+     * @return the string read from the stream
+     */
+    public String readString() {
+        StringBuilder stringBuilder = new StringBuilder();
+        int data = this.readByte();
+        while (data != 0) {
+            stringBuilder.append((char) data);
+            data = this.readByte();
+        }
+        return stringBuilder.toString();
+    }
 
 
-	/**
-	 * This method returns the size of the pointer.
-	 * @return the size of the pointer
-	 */
-	public int getPointerSize() {
-		return pointerSize;
-	}
+    /**
+     * This method sets the current position of the read cursor.
+     * @param position
+     *        the position of the read cursor
+     */
+    public void setPosition(int position) {
+        this.position = position;
+    }
 
 
-	/**
-	 * This method returns the application's asset manager.
-	 * @return the application's asset manager
-	 */
-	public AssetManager getAssetManager() {
-		return assetManager;
-	}
+    /**
+     * This method returns the position of the read cursor.
+     * @return the position of the read cursor
+     */
+    public int getPosition() {
+        return position;
+    }
 
 
-	/**
-	 * 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;
-		}
-	}
+    /**
+     * 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;
 //		cachedBuffer = null;
 //		size = position = 0;
 //		size = position = 0;
-	}
+    }
 }
 }

+ 343 - 343
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
  * @author Marcin Roguski
  */
  */
 public class DataRepository {
 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<Long, FileBlockHeader>				fileBlockHeadersByOma	= new HashMap<Long, FileBlockHeader>();
-	/** A map containing the file block headers. The key is the block code. */
-	private Map<Integer, List<FileBlockHeader>>		fileBlockHeadersByCode	= new HashMap<Integer, List<FileBlockHeader>>();
-	/**
-	 * This map stores the loaded features by their old memory address. The first object in the value table is the
-	 * loaded structure and the second - the structure already converted into proper data.
-	 */
-	private Map<Long, Object[]>						loadedFeatures			= new HashMap<Long, Object[]>();
-	/**
-	 * This map stores the loaded features by their name. Only features with ID structure can be stored here.
-	 * The first object in the value table is the
-	 * loaded structure and the second - the structure already converted into proper data.
-	 */
-	private Map<String, Object[]>					loadedFeaturesByName	= new HashMap<String, Object[]>();
-	/** A stack that hold the parent structure of currently loaded feature. */
-	private Stack<Structure>						parentStack				= new Stack<Structure>();
-	/** A map storing loaded ipos. The key is the ipo's owner old memory address and the value is the ipo. */
-	private Map<Long, Ipo>							loadedIpos				= new HashMap<Long, Ipo>();
-	/** A list of modifiers for the specified object. */
-	protected Map<Long, List<Modifier>>				modifiers				= new HashMap<Long, List<Modifier>>();
-	/** A map og helpers that perform loading. */
-	private Map<String, AbstractBlenderHelper>		helpers					= new HashMap<String, AbstractBlenderHelper>();
-	
-	/**
-	 * 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<FileBlockHeader> headers = fileBlockHeadersByCode.get(Integer.valueOf(fileBlockHeader.getCode()));
-		if(headers == null) {
-			headers = new ArrayList<FileBlockHeader>();
-			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<FileBlockHeader> 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 <T>
-	 *        the type of the helper
-	 * @param clazz
-	 *        helper's class definition
-	 * @param helper
-	 *        the helper instance
-	 */
-	public <T> void putHelper(Class<T> clazz, AbstractBlenderHelper helper) {
-		helpers.put(clazz.getSimpleName(), helper);
-	}
-
-	@SuppressWarnings("unchecked")
-	public <T> T getHelper(Class<?> clazz) {
-		return (T)helpers.get(clazz.getSimpleName());
-	}
-	
-
-	/**
-	 * This method adds a loaded feature to the map. The key is its unique old memory address.
-	 * @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<Modifier> objectModifiers = this.modifiers.get(ownerOMA);
-		if(objectModifiers == null) {
-			objectModifiers = new ArrayList<Modifier>();
-			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<Modifier> getModifiers(Long objectOMA, String type) {
-		List<Modifier> result = new ArrayList<Modifier>();
-		List<Modifier> 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<Long, FileBlockHeader> fileBlockHeadersByOma = new HashMap<Long, FileBlockHeader>();
+    /** A map containing the file block headers. The key is the block code. */
+    private Map<Integer, List<FileBlockHeader>> fileBlockHeadersByCode = new HashMap<Integer, List<FileBlockHeader>>();
+    /**
+     * This map stores the loaded features by their old memory address. The first object in the value table is the
+     * loaded structure and the second - the structure already converted into proper data.
+     */
+    private Map<Long, Object[]> loadedFeatures = new HashMap<Long, Object[]>();
+    /**
+     * This map stores the loaded features by their name. Only features with ID structure can be stored here.
+     * The first object in the value table is the
+     * loaded structure and the second - the structure already converted into proper data.
+     */
+    private Map<String, Object[]> loadedFeaturesByName = new HashMap<String, Object[]>();
+    /** A stack that hold the parent structure of currently loaded feature. */
+    private Stack<Structure> parentStack = new Stack<Structure>();
+    /** A map storing loaded ipos. The key is the ipo's owner old memory address and the value is the ipo. */
+    private Map<Long, Ipo> loadedIpos = new HashMap<Long, Ipo>();
+    /** A list of modifiers for the specified object. */
+    protected Map<Long, List<Modifier>> modifiers = new HashMap<Long, List<Modifier>>();
+    /** A map og helpers that perform loading. */
+    private Map<String, AbstractBlenderHelper> helpers = new HashMap<String, AbstractBlenderHelper>();
+
+    /**
+     * 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<FileBlockHeader> headers = fileBlockHeadersByCode.get(Integer.valueOf(fileBlockHeader.getCode()));
+        if (headers == null) {
+            headers = new ArrayList<FileBlockHeader>();
+            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<FileBlockHeader> 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 <T>
+     *        the type of the helper
+     * @param clazz
+     *        helper's class definition
+     * @param helper
+     *        the helper instance
+     */
+    public <T> void putHelper(Class<T> clazz, AbstractBlenderHelper helper) {
+        helpers.put(clazz.getSimpleName(), helper);
+    }
+
+    @SuppressWarnings("unchecked")
+    public <T> T getHelper(Class<?> clazz) {
+        return (T) helpers.get(clazz.getSimpleName());
+    }
+
+    /**
+     * This method adds a loaded feature to the map. The key is its unique old memory address.
+     * @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<Modifier> objectModifiers = this.modifiers.get(ownerOMA);
+        if (objectModifiers == null) {
+            objectModifiers = new ArrayList<Modifier>();
+            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<Modifier> getModifiers(Long objectOMA, String type) {
+        List<Modifier> result = new ArrayList<Modifier>();
+        List<Modifier> 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;
+        }
+    }
 }
 }

+ 107 - 106
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
  *        the type of stored data in the array
  */
  */
 public class DynamicArray<T> implements Cloneable {
 public class DynamicArray<T> implements Cloneable {
-	/** An array object that holds the required data. */
-	private T[]		array;
-	/**
-	 * This table holds the sizes of dimetions of the dynamic table. It's length specifies the table dimension or a
-	 * pointer level. For example: if tableSizes.length == 3 then it either specifies a dynamic table of fixed lengths:
-	 * dynTable[a][b][c], where a,b,c are stored in the tableSizes table.
-	 */
-	private int[]	tableSizes;
 
 
-	/**
-	 * 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();
+    }
 }
 }

+ 100 - 100
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.
  * This class converts blender file blocks into jMonkeyEngine data structures.
  * @author Marcin Roguski
  * @author Marcin Roguski
  */
  */
-public class JmeConverter implements IBlenderConverter<Node, Camera, Light, Object, List<Geometry>, Material> {
-	private static final Logger		LOGGER						= Logger.getLogger(JmeConverter.class.getName());
+public class JmeConverter implements BlenderConverter<Node, Camera, Light, Object, List<Geometry>, 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<Geometry> 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<Geometry> 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;
+    }
 }
 }

+ 121 - 120
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
  * @author Marcin Roguski
  */
  */
 public class Pointer {
 public class Pointer {
-	/** The data repository. */
-	private DataRepository	dataRepository;
-	/** The level of the pointer. */
-	private int				pointerLevel;
-	/** The address in file it points to. */
-	private long			oldMemoryAddress;
-	/** This variable indicates if the field is a function pointer. */
-	public boolean			function;
 
 
-	/**
-	 * 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<Structure> fetchData(BlenderInputStream inputStream) throws BlenderFileException {
-		if(oldMemoryAddress == 0) {
-			throw new NullPointerException("The pointer points to nothing!");
-		}
-		List<Structure> structures = null;
-		FileBlockHeader dataFileBlock = dataRepository.getFileBlock(oldMemoryAddress);
-		if(pointerLevel > 1) {
-			int pointersAmount = dataFileBlock.getSize() / inputStream.getPointerSize() * dataFileBlock.getCount();
-			for(int i = 0; i < pointersAmount; ++i) {
-				inputStream.setPosition(dataFileBlock.getBlockPosition() + inputStream.getPointerSize() * i);
-				long oldMemoryAddress = inputStream.readPointer();
-				if(oldMemoryAddress != 0L) {
-					Pointer p = new Pointer(pointerLevel - 1, this.function, dataRepository);
-					p.oldMemoryAddress = oldMemoryAddress;
-					if(structures == null) {
-						structures = p.fetchData(inputStream);
-					} else {
-						structures.addAll(p.fetchData(inputStream));
-					}
-				}
-			}
-		} else {
-			inputStream.setPosition(dataFileBlock.getBlockPosition());
-			structures = new ArrayList<Structure>(dataFileBlock.getCount());
-			for(int i = 0; i < dataFileBlock.getCount(); ++i) {
-				Structure structure = dataRepository.getDnaBlockData().getStructure(dataFileBlock.getSdnaIndex());
-				structure.fill(inputStream);
-				structures.add(structure);
-			}
-			return structures;
-		}
-		return structures;
-	}
+    /**
+     * This method 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 <b>true</b> if this is a function pointer and <b>false</b> 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<Structure> fetchData(BlenderInputStream inputStream) throws BlenderFileException {
+        if (oldMemoryAddress == 0) {
+            throw new NullPointerException("The pointer points to nothing!");
+        }
+        List<Structure> structures = null;
+        FileBlockHeader dataFileBlock = dataRepository.getFileBlock(oldMemoryAddress);
+        if (pointerLevel > 1) {
+            int pointersAmount = dataFileBlock.getSize() / inputStream.getPointerSize() * dataFileBlock.getCount();
+            for (int i = 0; i < pointersAmount; ++i) {
+                inputStream.setPosition(dataFileBlock.getBlockPosition() + inputStream.getPointerSize() * i);
+                long oldMemoryAddress = inputStream.readPointer();
+                if (oldMemoryAddress != 0L) {
+                    Pointer p = new Pointer(pointerLevel - 1, this.function, dataRepository);
+                    p.oldMemoryAddress = oldMemoryAddress;
+                    if (structures == null) {
+                        structures = p.fetchData(inputStream);
+                    } else {
+                        structures.addAll(p.fetchData(inputStream));
+                    }
+                }
+            }
+        } else {
+            inputStream.setPosition(dataFileBlock.getBlockPosition());
+            structures = new ArrayList<Structure>(dataFileBlock.getCount());
+            for (int i = 0; i < dataFileBlock.getCount(); ++i) {
+                Structure structure = dataRepository.getDnaBlockData().getStructure(dataFileBlock.getSdnaIndex());
+                structure.fill(inputStream);
+                structures.add(structure);
+            }
+            return structures;
+        }
+        return structures;
+    }
 
 
-	/**
-	 * This method indicates if this is a null-pointer or not.
-	 * @return <b>true</b> if the pointer is null and <b>false</b> otherwise
-	 */
-	public boolean isNull() {
-		return oldMemoryAddress == 0;
-	}
+    /**
+     * This method indicates if this pointer points to a function.
+     * @return <b>true</b> if this is a function pointer and <b>false</b> 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 <b>true</b> if the pointer is null and <b>false</b> 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;
+    }
 }
 }

+ 1 - 3
engine/src/core/com/jme3/animation/Bone.java

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

+ 0 - 2
engine/src/core/com/jme3/animation/CompactArray.java

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

+ 35 - 35
engine/src/core/com/jme3/app/package.html

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

+ 0 - 1
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.
      * Calls render for all attached states, do not call directly.
-     * @param rm The RenderManager
      */
      */
     public void postRender(){
     public void postRender(){
         AppState[] array = getArray();
         AppState[] array = getArray();

+ 1 - 1
engine/src/core/com/jme3/asset/AssetLocator.java

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

+ 7 - 7
engine/src/core/com/jme3/asset/AssetManager.java

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

+ 1 - 1
engine/src/core/com/jme3/asset/TextureKey.java

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

+ 1 - 1
engine/src/core/com/jme3/audio/AudioRenderer.java

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

+ 11 - 14
engine/src/core/com/jme3/bounding/BoundingBox.java

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

+ 2 - 6
engine/src/core/com/jme3/bounding/BoundingSphere.java

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

+ 2 - 0
engine/src/core/com/jme3/input/controls/MouseButtonTrigger.java

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

+ 2 - 1
engine/src/core/com/jme3/light/Light.java

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

+ 110 - 26
engine/src/core/com/jme3/material/Material.java

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

+ 15 - 4
engine/src/core/com/jme3/material/Technique.java

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

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

+ 1 - 1
engine/src/core/com/jme3/material/package.html

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

+ 1 - 2
engine/src/core/com/jme3/math/Matrix4f.java

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

+ 2 - 1
engine/src/core/com/jme3/math/Ray.java

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

+ 0 - 6
engine/src/core/com/jme3/math/Spline.java

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

+ 71 - 54
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
  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
  */
-
 package com.jme3.math;
 package com.jme3.math;
 
 
 import com.jme3.export.JmeExporter;
 import com.jme3.export.JmeExporter;
@@ -50,15 +49,13 @@ public class Triangle extends AbstractTriangle implements Savable {
     private Vector3f pointa = new Vector3f();
     private Vector3f pointa = new Vector3f();
     private Vector3f pointb = new Vector3f();
     private Vector3f pointb = new Vector3f();
     private Vector3f pointc = new Vector3f();
     private Vector3f pointc = new Vector3f();
-    
     private transient Vector3f center;
     private transient Vector3f center;
     private transient Vector3f normal;
     private transient Vector3f normal;
-    
     private float projection;
     private float projection;
-    
     private int index;
     private int index;
-    
-    public Triangle() {} 
+
+    public Triangle() {
+    }
 
 
     /**
     /**
      * Constructor instantiates a new <Code>Triangle</code> object with the
      * Constructor instantiates a new <Code>Triangle</code> object with the
@@ -84,25 +81,29 @@ public class Triangle extends AbstractTriangle implements Savable {
      */
      */
     public Vector3f get(int i) {
     public Vector3f get(int i) {
         switch (i) {
         switch (i) {
-        case 0: return pointa;
-        case 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;
         return pointa;
     }
     }
 
 
-    public Vector3f get2(){
+    public Vector3f get2() {
         return pointb;
         return pointb;
     }
     }
 
 
-    public Vector3f get3(){
+    public Vector3f get3() {
         return pointc;
         return pointc;
     }
     }
-    
+
     /**
     /**
      *
      *
      * <code>set</code> sets one of the triangles points to that specified as
      * <code>set</code> sets one of the triangles points to that specified as
@@ -112,9 +113,15 @@ public class Triangle extends AbstractTriangle implements Savable {
      */
      */
     public void set(int i, Vector3f point) {
     public void set(int i, Vector3f point) {
         switch (i) {
         switch (i) {
-        case 0: pointa.set(point); break;
-        case 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 {
      * <code>set</code> sets one of the triangles points to that specified as
      * <code>set</code> sets one of the triangles points to that specified as
      * a parameter.
      * a parameter.
      * @param i the index to place the point.
      * @param i the index to place the point.
-     * @param point the point to set.
      */
      */
     public void set(int i, float x, float y, float z) {
     public void set(int i, float x, float y, float z) {
         switch (i) {
         switch (i) {
-        case 0: pointa.set(x,y,z); break;
-        case 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);
         pointa.set(v);
     }
     }
 
 
-    public void set2(Vector3f v){
+    public void set2(Vector3f v) {
         pointb.set(v);
         pointb.set(v);
     }
     }
 
 
-    public void set3(Vector3f v){
+    public void set3(Vector3f v) {
         pointc.set(v);
         pointc.set(v);
     }
     }
 
 
-    public void set(Vector3f v1, Vector3f v2, Vector3f v3){
+    public void set(Vector3f v1, Vector3f v2, Vector3f v3) {
         pointa.set(v1);
         pointa.set(v1);
         pointb.set(v2);
         pointb.set(v2);
         pointc.set(v3);
         pointc.set(v3);
     }
     }
-    
+
     /**
     /**
      * calculateCenter finds the average point of the triangle. 
      * calculateCenter finds the average point of the triangle. 
      *
      *
      */
      */
     public void calculateCenter() {
     public void calculateCenter() {
-        if (center == null)
+        if (center == null) {
             center = new Vector3f(pointa);
             center = new Vector3f(pointa);
-        else center.set(pointa);
+        } else {
+            center.set(pointa);
+        }
         center.addLocal(pointb).addLocal(pointc).multLocal(FastMath.ONE_THIRD);
         center.addLocal(pointb).addLocal(pointc).multLocal(FastMath.ONE_THIRD);
     }
     }
-    
+
     /**
     /**
      * calculateCenter finds the average point of the triangle. 
      * calculateCenter finds the average point of the triangle. 
      *
      *
      */
      */
     public void calculateNormal() {
     public void calculateNormal() {
-        if (normal == null)
+        if (normal == null) {
             normal = new Vector3f(pointb);
             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();
         normal.normalizeLocal();
     }
     }
-    
+
     /**
     /**
      * obtains the center point of this triangle (average of the three triangles)
      * obtains the center point of this triangle (average of the three triangles)
      * @return the center point.
      * @return the center point.
      */
      */
     public Vector3f getCenter() {
     public Vector3f getCenter() {
-    	if(center == null) {
-    		calculateCenter();
-    	}
+        if (center == null) {
+            calculateCenter();
+        }
         return center;
         return center;
     }
     }
-    
+
     /**
     /**
      * sets the center point of this triangle (average of the three triangles)
      * sets the center point of this triangle (average of the three triangles)
      * @param center the center point.
      * @param center the center point.
@@ -192,7 +208,7 @@ public class Triangle extends AbstractTriangle implements Savable {
     public void setCenter(Vector3f center) {
     public void setCenter(Vector3f center) {
         this.center = center;
         this.center = center;
     }
     }
-    
+
     /**
     /**
      * obtains the unit length normal vector of this triangle, if set or
      * obtains the unit length normal vector of this triangle, if set or
      * calculated
      * calculated
@@ -200,12 +216,12 @@ public class Triangle extends AbstractTriangle implements Savable {
      * @return the normal vector
      * @return the normal vector
      */
      */
     public Vector3f getNormal() {
     public Vector3f getNormal() {
-    	if(normal == null) {
-    		calculateNormal();
-    	}
+        if (normal == null) {
+            calculateNormal();
+        }
         return normal;
         return normal;
     }
     }
-    
+
     /**
     /**
      * sets the normal vector of this triangle (to conform, must be unit length)
      * sets the normal vector of this triangle (to conform, must be unit length)
      * @param normal the normal vector.
      * @param normal the normal vector.
@@ -213,7 +229,7 @@ public class Triangle extends AbstractTriangle implements Savable {
     public void setNormal(Vector3f normal) {
     public void setNormal(Vector3f normal) {
         this.normal = normal;
         this.normal = normal;
     }
     }
-    
+
     /**
     /**
      * obtains the projection of the vertices relative to the line origin.
      * obtains the projection of the vertices relative to the line origin.
      * @return the projection of the triangle.
      * @return the projection of the triangle.
@@ -221,7 +237,7 @@ public class Triangle extends AbstractTriangle implements Savable {
     public float getProjection() {
     public float getProjection() {
         return this.projection;
         return this.projection;
     }
     }
-    
+
     /**
     /**
      * sets the projection of the vertices relative to the line origin.
      * sets the projection of the vertices relative to the line origin.
      * @param projection the projection of the triangle.
      * @param projection the projection of the triangle.
@@ -229,7 +245,7 @@ public class Triangle extends AbstractTriangle implements Savable {
     public void setProjection(float projection) {
     public void setProjection(float projection) {
         this.projection = projection;
         this.projection = projection;
     }
     }
-    
+
     /**
     /**
      * obtains an index that this triangle represents if it is contained in a OBBTree.
      * obtains an index that this triangle represents if it is contained in a OBBTree.
      * @return the index in an OBBtree
      * @return the index in an OBBtree
@@ -237,7 +253,7 @@ public class Triangle extends AbstractTriangle implements Savable {
     public int getIndex() {
     public int getIndex() {
         return index;
         return index;
     }
     }
-    
+
     /**
     /**
      * sets an index that this triangle represents if it is contained in a OBBTree.
      * sets an index that this triangle represents if it is contained in a OBBTree.
      * @param index the index in an OBBtree
      * @param index the index in an OBBtree
@@ -246,13 +262,14 @@ public class Triangle extends AbstractTriangle implements Savable {
         this.index = index;
         this.index = index;
     }
     }
 
 
-    public static Vector3f computeTriangleNormal(Vector3f v1, Vector3f v2, Vector3f v3, Vector3f store){
-        if (store == null)
+    public static Vector3f computeTriangleNormal(Vector3f v1, Vector3f v2, Vector3f v3, Vector3f store) {
+        if (store == null) {
             store = new Vector3f(v2);
             store = new Vector3f(v2);
-        else
+        } else {
             store.set(v2);
             store.set(v2);
+        }
 
 
-        store.subtractLocal(v1).crossLocal(v3.x-v1.x, v3.y-v1.y, v3.z-v1.z);
+        store.subtractLocal(v1).crossLocal(v3.x - v1.x, v3.y - v1.y, v3.z - v1.z);
         return store.normalizeLocal();
         return store.normalizeLocal();
     }
     }
 
 
@@ -263,11 +280,11 @@ public class Triangle extends AbstractTriangle implements Savable {
     }
     }
 
 
     public void read(JmeImporter e) throws IOException {
     public void read(JmeImporter e) throws IOException {
-        pointa = (Vector3f)e.getCapsule(this).readSavable("pointa", Vector3f.ZERO.clone());
-        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
     @Override
     public Triangle clone() {
     public Triangle clone() {
         try {
         try {

+ 2 - 1
engine/src/core/com/jme3/math/Vector2f.java

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

+ 1 - 1
engine/src/core/com/jme3/math/Vector4f.java

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

+ 12 - 11
engine/src/core/com/jme3/post/Filter.java

@@ -189,9 +189,10 @@ public abstract class Filter implements Savable {
     public abstract void cleanUpFilter(Renderer r);
     public abstract void cleanUpFilter(Renderer r);
 
 
     /**
     /**
-     * this method should return the material used for this filter.
-     * 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();
     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 {
     public void read(JmeImporter im) throws IOException {
         InputCapsule ic = im.getCapsule(this);
         InputCapsule ic = im.getCapsule(this);
         name = ic.readString("name", "");
         name = ic.readString("name", "");
         enabled = ic.readBoolean("enabled", true);
         enabled = ic.readBoolean("enabled", true);
-
     }
     }
 
 
     public String getName() {
     public String getName() {
@@ -269,8 +268,9 @@ public abstract class Filter implements Savable {
     }
     }
 
 
     /**
     /**
-     * Override this method and retrun true if your Filter need the depth texture
-     * @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() {
     public boolean isRequiresDepthTexture() {
         return false;
         return false;
@@ -278,7 +278,8 @@ public abstract class Filter implements Savable {
     
     
      /**
      /**
      * Override this method and return false if your Filter does not need the scene texture
      * Override this method and return false if your Filter does not need the scene texture
-     * @return
+     * 
+     * @return false if your Filter does not need the scene texture
      */
      */
     public boolean isRequiresSceneTexture() {
     public boolean isRequiresSceneTexture() {
         return true;
         return true;

+ 2 - 2
engine/src/core/com/jme3/renderer/Camera.java

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

+ 1 - 2
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
      * This should create a deep clone. For a shallow clone, use
      * createDestructableClone().
      * createDestructableClone().
-     *
-     * @return
      */
      */
+    @Override
     protected GLObject clone(){
     protected GLObject clone(){
         try{
         try{
             GLObject obj = (GLObject) super.clone();
             GLObject obj = (GLObject) super.clone();

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

+ 1 - 2
engine/src/core/com/jme3/renderer/Renderer.java

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

+ 3 - 3
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) {
     public void add(Geometry g) {
         if (size == geometries.length) {
         if (size == geometries.length) {

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

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

+ 0 - 3
engine/src/core/com/jme3/scene/Spatial.java

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

+ 0 - 5
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
      * of the parameters. The buffer will be of the type specified by
      * {@link Format format} and would be able to contain the given number
      * {@link Format format} and would be able to contain the given number
      * of elements with the given number of components in each element.
      * of elements with the given number of components in each element.
-     *
-     * @param format
-     * @param components
-     * @param numElements
-     * @return
      */
      */
     public static Buffer createBuffer(Format format, int components, int numElements){
     public static Buffer createBuffer(Format format, int components, int numElements){
         if (components < 1 || components > 4)
         if (components < 1 || components > 4)

+ 1 - 1
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
      * Creates a clone of the Control, the given Spatial is the cloned
      * version of the spatial to which this control is attached to.
      * version of the spatial to which this control is attached to.
      * @param spatial
      * @param spatial
-     * @return
+     * @return A clone of this control for the spatial
      */
      */
     public Control cloneForSpatial(Spatial spatial);
     public Control cloneForSpatial(Spatial spatial);
 
 

+ 2 - 2
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) {
     public LightControl(Light light) {
         this.light = light;
         this.light = light;
     }
     }
 
 
     /**
     /**
-     * @param camera The Camera to be synced.
+     * @param light The light to be synced.
      */
      */
     public LightControl(Light light, ControlDirection controlDir) {
     public LightControl(Light light, ControlDirection controlDir) {
         this.light = light;
         this.light = light;

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

+ 1 - 0
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.MouseInput;
 import com.jme3.input.TouchInput;
 import com.jme3.input.TouchInput;
 import com.jme3.renderer.Renderer;
 import com.jme3.renderer.Renderer;
+import com.jme3.system.JmeCanvasContext;
 
 
 /**
 /**
  * Represents a rendering context within the engine.
  * Represents a rendering context within the engine.