Kaynağa Gözat

Refactoring: tabs replaced by spaces in blender importer sources. (4 spaces for each tab)

git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@10401 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
Kae..pl 12 yıl önce
ebeveyn
işleme
ecc8d8387b
100 değiştirilmiş dosya ile 13061 ekleme ve 13130 silme
  1. 709 705
      engine/src/blender/com/jme3/asset/BlenderKey.java
  2. 4 3
      engine/src/blender/com/jme3/asset/GeneratedTextureKey.java
  3. 110 110
      engine/src/blender/com/jme3/scene/plugins/blender/AbstractBlenderHelper.java
  4. 120 120
      engine/src/blender/com/jme3/scene/plugins/blender/AbstractBlenderLoader.java
  5. 622 621
      engine/src/blender/com/jme3/scene/plugins/blender/BlenderContext.java
  6. 153 153
      engine/src/blender/com/jme3/scene/plugins/blender/BlenderLoader.java
  7. 16 16
      engine/src/blender/com/jme3/scene/plugins/blender/BlenderModelLoader.java
  8. 200 200
      engine/src/blender/com/jme3/scene/plugins/blender/animations/ArmatureHelper.java
  9. 211 211
      engine/src/blender/com/jme3/scene/plugins/blender/animations/BoneContext.java
  10. 105 105
      engine/src/blender/com/jme3/scene/plugins/blender/animations/CalculationBone.java
  11. 230 230
      engine/src/blender/com/jme3/scene/plugins/blender/animations/Ipo.java
  12. 178 178
      engine/src/blender/com/jme3/scene/plugins/blender/animations/IpoHelper.java
  13. 52 52
      engine/src/blender/com/jme3/scene/plugins/blender/cameras/CameraHelper.java
  14. 189 190
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/BoneConstraint.java
  15. 105 105
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/Constraint.java
  16. 436 436
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintHelper.java
  17. 18 17
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/SkeletonConstraint.java
  18. 140 142
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/SpatialConstraint.java
  19. 221 226
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinition.java
  20. 11 11
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionAction.java
  21. 11 11
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionCameraSolver.java
  22. 12 12
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionChildOf.java
  23. 12 12
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionClampTo.java
  24. 12 12
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionDampTrack.java
  25. 47 47
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionDistLimit.java
  26. 71 71
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionFactory.java
  27. 12 12
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionFollowPath.java
  28. 11 11
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionFollowTrack.java
  29. 129 129
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionInverseKinematics.java
  30. 57 57
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionLocLike.java
  31. 57 57
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionLocLimit.java
  32. 12 12
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionLockTrack.java
  33. 12 12
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionMinMax.java
  34. 9 9
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionNull.java
  35. 11 11
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionObjectSolver.java
  36. 12 12
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionPivot.java
  37. 12 12
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionPython.java
  38. 12 12
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionRigidBodyJoint.java
  39. 48 48
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionRotLike.java
  40. 68 68
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionRotLimit.java
  41. 12 12
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionSameVolume.java
  42. 49 50
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionShrinkWrap.java
  43. 38 38
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionSizeLike.java
  44. 63 63
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionSizeLimit.java
  45. 12 12
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionSplineInverseKinematic.java
  46. 11 11
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionStretchTo.java
  47. 10 10
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionTrackTo.java
  48. 12 12
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionTransLike.java
  49. 11 11
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionTransform.java
  50. 29 33
      engine/src/blender/com/jme3/scene/plugins/blender/curves/BezierCurve.java
  51. 387 392
      engine/src/blender/com/jme3/scene/plugins/blender/curves/CurvesHelper.java
  52. 5 5
      engine/src/blender/com/jme3/scene/plugins/blender/exceptions/BlenderFileException.java
  53. 38 39
      engine/src/blender/com/jme3/scene/plugins/blender/file/BlenderInputStream.java
  54. 24 29
      engine/src/blender/com/jme3/scene/plugins/blender/file/DnaBlockData.java
  55. 10 10
      engine/src/blender/com/jme3/scene/plugins/blender/file/DynamicArray.java
  56. 32 32
      engine/src/blender/com/jme3/scene/plugins/blender/file/Field.java
  57. 29 30
      engine/src/blender/com/jme3/scene/plugins/blender/file/FileBlockHeader.java
  58. 18 19
      engine/src/blender/com/jme3/scene/plugins/blender/file/Pointer.java
  59. 41 41
      engine/src/blender/com/jme3/scene/plugins/blender/file/Structure.java
  60. 22 22
      engine/src/blender/com/jme3/scene/plugins/blender/lights/LightHelper.java
  61. 18 18
      engine/src/blender/com/jme3/scene/plugins/blender/materials/IAlphaMask.java
  62. 360 361
      engine/src/blender/com/jme3/scene/plugins/blender/materials/MaterialContext.java
  63. 407 406
      engine/src/blender/com/jme3/scene/plugins/blender/materials/MaterialHelper.java
  64. 254 236
      engine/src/blender/com/jme3/scene/plugins/blender/meshes/MeshBuilder.java
  65. 123 119
      engine/src/blender/com/jme3/scene/plugins/blender/meshes/MeshContext.java
  66. 233 235
      engine/src/blender/com/jme3/scene/plugins/blender/meshes/MeshHelper.java
  67. 371 374
      engine/src/blender/com/jme3/scene/plugins/blender/modifiers/ArmatureModifier.java
  68. 113 113
      engine/src/blender/com/jme3/scene/plugins/blender/modifiers/ArrayModifier.java
  69. 82 86
      engine/src/blender/com/jme3/scene/plugins/blender/modifiers/MirrorModifier.java
  70. 59 59
      engine/src/blender/com/jme3/scene/plugins/blender/modifiers/Modifier.java
  71. 132 132
      engine/src/blender/com/jme3/scene/plugins/blender/modifiers/ModifierHelper.java
  72. 62 62
      engine/src/blender/com/jme3/scene/plugins/blender/modifiers/ObjectAnimationModifier.java
  73. 67 68
      engine/src/blender/com/jme3/scene/plugins/blender/modifiers/ParticlesModifier.java
  74. 306 307
      engine/src/blender/com/jme3/scene/plugins/blender/objects/ObjectHelper.java
  75. 345 345
      engine/src/blender/com/jme3/scene/plugins/blender/objects/Properties.java
  76. 175 175
      engine/src/blender/com/jme3/scene/plugins/blender/particles/ParticlesHelper.java
  77. 287 287
      engine/src/blender/com/jme3/scene/plugins/blender/textures/ColorBand.java
  78. 507 508
      engine/src/blender/com/jme3/scene/plugins/blender/textures/CombinedTexture.java
  79. 134 134
      engine/src/blender/com/jme3/scene/plugins/blender/textures/DDSTexelData.java
  80. 112 112
      engine/src/blender/com/jme3/scene/plugins/blender/textures/GeneratedTexture.java
  81. 82 82
      engine/src/blender/com/jme3/scene/plugins/blender/textures/ImageLoader.java
  82. 604 606
      engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureHelper.java
  83. 353 353
      engine/src/blender/com/jme3/scene/plugins/blender/textures/TexturePixel.java
  84. 642 644
      engine/src/blender/com/jme3/scene/plugins/blender/textures/TriangulatedTexture.java
  85. 399 413
      engine/src/blender/com/jme3/scene/plugins/blender/textures/UVCoordinatesGenerator.java
  86. 224 231
      engine/src/blender/com/jme3/scene/plugins/blender/textures/UVProjectionGenerator.java
  87. 118 118
      engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/AbstractTextureBlender.java
  88. 38 38
      engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlender.java
  89. 175 193
      engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderAWT.java
  90. 100 104
      engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderDDS.java
  91. 79 79
      engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderFactory.java
  92. 216 224
      engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderLuminance.java
  93. 138 145
      engine/src/blender/com/jme3/scene/plugins/blender/textures/generating/NoiseGenerator.java
  94. 59 57
      engine/src/blender/com/jme3/scene/plugins/blender/textures/generating/TextureGenerator.java
  95. 82 82
      engine/src/blender/com/jme3/scene/plugins/blender/textures/generating/TextureGeneratorBlend.java
  96. 61 61
      engine/src/blender/com/jme3/scene/plugins/blender/textures/generating/TextureGeneratorClouds.java
  97. 39 39
      engine/src/blender/com/jme3/scene/plugins/blender/textures/generating/TextureGeneratorDistnoise.java
  98. 33 33
      engine/src/blender/com/jme3/scene/plugins/blender/textures/generating/TextureGeneratorFactory.java
  99. 113 113
      engine/src/blender/com/jme3/scene/plugins/blender/textures/generating/TextureGeneratorMagic.java
  100. 49 49
      engine/src/blender/com/jme3/scene/plugins/blender/textures/generating/TextureGeneratorMarble.java

+ 709 - 705
engine/src/blender/com/jme3/asset/BlenderKey.java

@@ -63,709 +63,713 @@ import com.jme3.texture.Texture;
  */
 public class BlenderKey extends ModelKey {
 
-	protected static final int					DEFAULT_FPS				= 25;
-	/**
-	 * FramesPerSecond parameter describe how many frames there are in each second. It allows to calculate the time
-	 * between the frames.
-	 */
-	protected int								fps						= DEFAULT_FPS;
-	/**
-	 * This variable is a bitwise flag of FeatureToLoad interface values; By default everything is being loaded.
-	 */
-	protected int								featuresToLoad			= FeaturesToLoad.ALL;
-	/** This variable determines if assets that are not linked to the objects should be loaded. */
-	protected boolean							loadUnlinkedAssets;
-	/** The root path for all the assets. */
-	protected String							assetRootPath;
-	/** This variable indicate if Y axis is UP axis. If not then Z is up. By default set to true. */
-	protected boolean							fixUpAxis				= true;
-	/** Generated textures resolution (PPU - Pixels Per Unit). */
-	protected int								generatedTexturePPU		= 128;
-	/**
-	 * The name of world settings that the importer will use. If not set or specified name does not occur in the file
-	 * then the first world settings in the file will be used.
-	 */
-	protected String							usedWorld;
-	/**
-	 * User's default material that is set fo objects that have no material definition in blender. The default value is
-	 * null. If the value is null the importer will use its own default material (gray color - like in blender).
-	 */
-	protected Material							defaultMaterial;
-	/** Face cull mode. By default it is disabled. */
-	protected FaceCullMode						faceCullMode			= FaceCullMode.Back;
-	/**
-	 * Variable describes which layers will be loaded. N-th bit set means N-th layer will be loaded.
-	 * If set to -1 then the current layer will be loaded.
-	 */
-	protected int								layersToLoad			= -1;
-	/** A variable that toggles the object custom properties loading. */
-	protected boolean							loadObjectProperties	= true;
-	/** Maximum texture size. Might be dependant on the graphic card.*/
-	protected int								maxTextureSize = -1;
-	/** Allows to toggle generated textures loading. Disabled by default because it very often takes too much memory and needs to be used wisely. */
-	protected boolean							loadGeneratedTextures;
-	/** Tells if the mipmaps will be generated by jme or not. By default generation is dependant on the blender settings. */
-	protected MipmapGenerationMethod			mipmapGenerationMethod = MipmapGenerationMethod.GENERATE_WHEN_NEEDED;
-	
-	/**
-	 * Constructor used by serialization mechanisms.
-	 */
-	public BlenderKey() {}
-
-	/**
-	 * Constructor. Creates a key for the given file name.
-	 * @param name
-	 *        the name (path) of a file
-	 */
-	public BlenderKey(String name) {
-		super(name);
-	}
-
-	/**
-	 * This method returns frames per second amount. The default value is BlenderKey.DEFAULT_FPS = 25.
-	 * @return the frames per second amount
-	 */
-	public int getFps() {
-		return fps;
-	}
-
-	/**
-	 * This method sets frames per second amount.
-	 * @param fps
-	 *        the frames per second amount
-	 */
-	public void setFps(int fps) {
-		this.fps = fps;
-	}
-
-	/**
-	 * This method returns the face cull mode.
-	 * @return the face cull mode
-	 */
-	public FaceCullMode getFaceCullMode() {
-		return faceCullMode;
-	}
-
-	/**
-	 * This method sets the face cull mode.
-	 * @param faceCullMode
-	 *        the face cull mode
-	 */
-	public void setFaceCullMode(FaceCullMode faceCullMode) {
-		this.faceCullMode = faceCullMode;
-	}
-
-	/**
-	 * This method sets layers to be loaded.
-	 * @param layersToLoad
-	 *        layers to be loaded
-	 */
-	public void setLayersToLoad(int layersToLoad) {
-		this.layersToLoad = layersToLoad;
-	}
-
-	/**
-	 * This method returns layers to be loaded.
-	 * @return layers to be loaded
-	 */
-	public int getLayersToLoad() {
-		return layersToLoad;
-	}
-
-	/**
-	 * This method sets the properies loading policy.
-	 * By default the value is true.
-	 * @param loadObjectProperties true to load properties and false to suspend their loading
-	 */
-	public void setLoadObjectProperties(boolean loadObjectProperties) {
-		this.loadObjectProperties = loadObjectProperties;
-	}
-	
-	/**
-	 * @return the current properties loading properties
-	 */
-	public boolean isLoadObjectProperties() {
-		return loadObjectProperties;
-	}
-	
-	/**
-	 * @return maximum texture size (width/height)
-	 */
-	public int getMaxTextureSize() {
-		if(maxTextureSize <= 0) {
-			try {
-				maxTextureSize = GL11.glGetInteger(GL11.GL_MAX_TEXTURE_SIZE);
-			} catch(Exception e) {
-				//this is in case this method was called before openGL initialization
-				return 8192;
-			}
-		}
-		return maxTextureSize;
-	}
-	
-	/**
-	 * This method sets the maximum texture size.
-	 * @param maxTextureSize the maximum texture size
-	 */
-	public void setMaxTextureSize(int maxTextureSize) {
-		this.maxTextureSize = maxTextureSize;
-	}
-	
-	/**
-	 * This method sets the flag that toggles the generated textures loading.
-	 * @param loadGeneratedTextures <b>true</b> if generated textures should be loaded and <b>false</b> otherwise
-	 */
-	public void setLoadGeneratedTextures(boolean loadGeneratedTextures) {
-		this.loadGeneratedTextures = loadGeneratedTextures;
-	}
-	
-	/**
-	 * @return tells if the generated textures should be loaded (<b>false</b> is the default value)
-	 */
-	public boolean isLoadGeneratedTextures() {
-		return loadGeneratedTextures;
-	}
-	
-	/**
-	 * This method sets the asset root path.
-	 * @param assetRootPath
-	 *        the assets root path
-	 */
-	public void setAssetRootPath(String assetRootPath) {
-		this.assetRootPath = assetRootPath;
-	}
-
-	/**
-	 * This method returns the asset root path.
-	 * @return the asset root path
-	 */
-	public String getAssetRootPath() {
-		return assetRootPath;
-	}
-
-	/**
-	 * This method adds features to be loaded.
-	 * @param featuresToLoad
-	 *        bitwise flag of FeaturesToLoad interface values
-	 */
-	public void includeInLoading(int featuresToLoad) {
-		this.featuresToLoad |= featuresToLoad;
-	}
-
-	/**
-	 * This method removes features from being loaded.
-	 * @param featuresNotToLoad
-	 *        bitwise flag of FeaturesToLoad interface values
-	 */
-	public void excludeFromLoading(int featuresNotToLoad) {
-		this.featuresToLoad &= ~featuresNotToLoad;
-	}
-
-	/**
-	 * This method returns bitwise value of FeaturesToLoad interface value. It describes features that will be loaded by
-	 * the blender file loader.
-	 * @return features that will be loaded by the blender file loader
-	 */
-	public int getFeaturesToLoad() {
-		return featuresToLoad;
-	}
-
-	/**
-	 * This method determines if unlinked assets should be loaded.
-	 * If not then only objects on selected layers will be loaded and their assets if required.
-	 * If yes then all assets will be loaded even if they are on inactive layers or are not linked
-	 * to anything.
-	 * @return <b>true</b> if unlinked assets should be loaded and <b>false</b> otherwise
-	 */
-	public boolean isLoadUnlinkedAssets() {
-		return loadUnlinkedAssets;
-	}
-
-	/**
-	 * This method sets if unlinked assets should be loaded.
-	 * If not then only objects on selected layers will be loaded and their assets if required.
-	 * If yes then all assets will be loaded even if they are on inactive layers or are not linked
-	 * to anything.
-	 * @param loadUnlinkedAssets
-	 *        <b>true</b> if unlinked assets should be loaded and <b>false</b> otherwise
-	 */
-	public void setLoadUnlinkedAssets(boolean loadUnlinkedAssets) {
-		this.loadUnlinkedAssets = loadUnlinkedAssets;
-	}
-
-	/**
-	 * This method creates an object where loading results will be stores. Only those features will be allowed to store
-	 * that were specified by features-to-load flag.
-	 * @return an object to store loading results
-	 */
-	public LoadingResults prepareLoadingResults() {
-		return new LoadingResults(featuresToLoad);
-	}
-
-	/**
-	 * This method sets the fix up axis state. If set to true then Y is up axis. Otherwise the up i Z axis. By default Y
-	 * is up axis.
-	 * @param fixUpAxis
-	 *        the up axis state variable
-	 */
-	public void setFixUpAxis(boolean fixUpAxis) {
-		this.fixUpAxis = fixUpAxis;
-	}
-
-	/**
-	 * This method returns the fix up axis state. If set to true then Y is up axis. Otherwise the up i Z axis. By
-	 * default Y is up axis.
-	 * @return the up axis state variable
-	 */
-	public boolean isFixUpAxis() {
-		return fixUpAxis;
-	}
-
-	/**
-	 * This method sets the generated textures resolution.
-	 * @param generatedTexturePPU the generated textures resolution
-	 */
-	public void setGeneratedTexturePPU(int generatedTexturePPU) {
-		this.generatedTexturePPU = generatedTexturePPU;
-	}
-	
-	/**
-	 * @return the generated textures resolution
-	 */
-	public int getGeneratedTexturePPU() {
-		return generatedTexturePPU;
-	}
-	
-	/**
-	 * @return mipmaps generation method
-	 */
-	public MipmapGenerationMethod getMipmapGenerationMethod() {
-		return mipmapGenerationMethod;
-	}
-
-	/**
-	 * @param mipmapGenerationMethod
-	 * 				mipmaps generation method
-	 */
-	public void setMipmapGenerationMethod(MipmapGenerationMethod mipmapGenerationMethod) {
-		this.mipmapGenerationMethod = mipmapGenerationMethod;
-	}
-	
-	/**
-	 * This mehtod sets the name of the WORLD data block taht should be used during file loading. By default the name is
-	 * not set. If no name is set or the given name does not occur in the file - the first WORLD data block will be used
-	 * during loading (assumin any exists in the file).
-	 * @param usedWorld
-	 *        the name of the WORLD block used during loading
-	 */
-	public void setUsedWorld(String usedWorld) {
-		this.usedWorld = usedWorld;
-	}
-
-	/**
-	 * This mehtod returns the name of the WORLD data block taht should be used during file loading.
-	 * @return the name of the WORLD block used during loading
-	 */
-	public String getUsedWorld() {
-		return usedWorld;
-	}
-
-	/**
-	 * This method sets the default material for objects.
-	 * @param defaultMaterial
-	 *        the default material
-	 */
-	public void setDefaultMaterial(Material defaultMaterial) {
-		this.defaultMaterial = defaultMaterial;
-	}
-
-	/**
-	 * This method returns the default material.
-	 * @return the default material
-	 */
-	public Material getDefaultMaterial() {
-		return defaultMaterial;
-	}
-
-	@Override
-	public void write(JmeExporter e) throws IOException {
-		super.write(e);
-		OutputCapsule oc = e.getCapsule(this);
-		oc.write(fps, "fps", DEFAULT_FPS);
-		oc.write(featuresToLoad, "features-to-load", FeaturesToLoad.ALL);
-		oc.write(loadUnlinkedAssets, "load-unlinked-assets", false);
-		oc.write(assetRootPath, "asset-root-path", null);
-		oc.write(fixUpAxis, "fix-up-axis", true);
-		oc.write(generatedTexturePPU, "generated-texture-ppu", 128);
-		oc.write(usedWorld, "used-world", null);
-		oc.write(defaultMaterial, "default-material", null);
-		oc.write(faceCullMode, "face-cull-mode", FaceCullMode.Off);
-		oc.write(layersToLoad, "layers-to-load", -1);
-		oc.write(mipmapGenerationMethod , "mipmap-generation-method", MipmapGenerationMethod.GENERATE_WHEN_NEEDED);
-	}
-
-	@Override
-	public void read(JmeImporter e) throws IOException {
-		super.read(e);
-		InputCapsule ic = e.getCapsule(this);
-		fps = ic.readInt("fps", DEFAULT_FPS);
-		featuresToLoad = ic.readInt("features-to-load", FeaturesToLoad.ALL);
-		loadUnlinkedAssets = ic.readBoolean("load-unlinked-assets", false);
-		assetRootPath = ic.readString("asset-root-path", null);
-		fixUpAxis = ic.readBoolean("fix-up-axis", true);
-		generatedTexturePPU = ic.readInt("generated-texture-ppu", 128);
-		usedWorld = ic.readString("used-world", null);
-		defaultMaterial = (Material) ic.readSavable("default-material", null);
-		faceCullMode = ic.readEnum("face-cull-mode", FaceCullMode.class, FaceCullMode.Off);
-		layersToLoad = ic.readInt("layers-to=load", -1);
-		mipmapGenerationMethod = ic.readEnum("mipmap-generation-method", MipmapGenerationMethod.class, MipmapGenerationMethod.GENERATE_WHEN_NEEDED);
-	}
-
-	@Override
-	public int hashCode() {
-		final int prime = 31;
-		int result = super.hashCode();
-		result = prime * result + (assetRootPath == null ? 0 : assetRootPath.hashCode());
-		result = prime * result + (defaultMaterial == null ? 0 : defaultMaterial.hashCode());
-		result = prime * result + (faceCullMode == null ? 0 : faceCullMode.hashCode());
-		result = prime * result + featuresToLoad;
-		result = prime * result + (fixUpAxis ? 1231 : 1237);
-		result = prime * result + fps;
-		result = prime * result + generatedTexturePPU;
-		result = prime * result + layersToLoad;
-		result = prime * result + (loadUnlinkedAssets ? 1231 : 1237);
-		result = prime * result + (usedWorld == null ? 0 : usedWorld.hashCode());
-		return result;
-	}
-
-	@Override
-	public boolean equals(Object obj) {
-		if (this == obj) {
-			return true;
-		}
-		if (!super.equals(obj)) {
-			return false;
-		}
-		if (this.getClass() != obj.getClass()) {
-			return false;
-		}
-		BlenderKey other = (BlenderKey) obj;
-		if (assetRootPath == null) {
-			if (other.assetRootPath != null) {
-				return false;
-			}
-		} else if (!assetRootPath.equals(other.assetRootPath)) {
-			return false;
-		}
-		if (defaultMaterial == null) {
-			if (other.defaultMaterial != null) {
-				return false;
-			}
-		} else if (!defaultMaterial.equals(other.defaultMaterial)) {
-			return false;
-		}
-		if (faceCullMode != other.faceCullMode) {
-			return false;
-		}
-		if (featuresToLoad != other.featuresToLoad) {
-			return false;
-		}
-		if (fixUpAxis != other.fixUpAxis) {
-			return false;
-		}
-		if (fps != other.fps) {
-			return false;
-		}
-		if (generatedTexturePPU != other.generatedTexturePPU) {
-			return false;
-		}
-		if (layersToLoad != other.layersToLoad) {
-			return false;
-		}
-		if (loadUnlinkedAssets != other.loadUnlinkedAssets) {
-			return false;
-		}
-		if (usedWorld == null) {
-			if (other.usedWorld != null) {
-				return false;
-			}
-		} else if (!usedWorld.equals(other.usedWorld)) {
-			return false;
-		}
-		return true;
-	}
-
-	/**
-	 * This enum tells the importer if the mipmaps for textures will be generated by jme.
-	 * <li> NEVER_GENERATE and ALWAYS_GENERATE are quite understandable
-	 * <li> GENERATE_WHEN_NEEDED is an option that checks if the texture had 'Generate mipmaps' option set
-	 * in blender, mipmaps are generated only when the option is set
-	 * @author Marcin Roguski (Kaelthas)
-	 */
-	public static enum MipmapGenerationMethod {
-		NEVER_GENERATE,
-		ALWAYS_GENERATE,
-		GENERATE_WHEN_NEEDED;
-	}
-
-	/**
-	 * This interface describes the features of the scene that are to be loaded.
-	 * @author Marcin Roguski (Kaelthas)
-	 */
-	public static interface FeaturesToLoad {
-
-		int	SCENES		= 0x0000FFFF;
-		int	OBJECTS		= 0x0000000B;
-		int	ANIMATIONS	= 0x00000004;
-		int	MATERIALS	= 0x00000003;
-		int	TEXTURES	= 0x00000001;
-		int	CAMERAS		= 0x00000020;
-		int	LIGHTS		= 0x00000010;
-		int	ALL			= 0xFFFFFFFF;
-	}
-
-	/**
-	 * This class holds the loading results according to the given loading flag.
-	 * @author Marcin Roguski (Kaelthas)
-	 */
-	public static class LoadingResults extends Spatial {
-
-		/** Bitwise mask of features that are to be loaded. */
-		private final int		featuresToLoad;
-		/** The scenes from the file. */
-		private List<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<CameraNode>cameras;
-		/** All lights from the file. */
-		private List<LightNode>	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<CameraNode>();
-			}
-			if ((featuresToLoad & FeaturesToLoad.LIGHTS) != 0) {
-				lights = new ArrayList<LightNode>();
-			}
-		}
-
-		/**
-		 * This method returns a bitwise flag describing what features of the blend file will be included in the result.
-		 * @return bitwise mask of features that are to be loaded
-		 * @see FeaturesToLoad FeaturesToLoad
-		 */
-		public int getLoadedFeatures() {
-			return featuresToLoad;
-		}
-
-		/**
-		 * This method adds a scene to the result set.
-		 * @param scene
-		 *        scene to be added to the result set
-		 */
-		public void addScene(Node scene) {
-			if (scenes != null) {
-				scenes.add(scene);
-			}
-		}
-
-		/**
-		 * This method adds an object to the result set.
-		 * @param object
-		 *        object to be added to the result set
-		 */
-		public void addObject(Node object) {
-			if (objects != null) {
-				objects.add(object);
-			}
-		}
-
-		/**
-		 * This method adds a material to the result set.
-		 * @param material
-		 *        material to be added to the result set
-		 */
-		public void addMaterial(Material material) {
-			if (materials != null) {
-				materials.add(material);
-			}
-		}
-
-		/**
-		 * This method adds a texture to the result set.
-		 * @param texture
-		 *        texture to be added to the result set
-		 */
-		public void addTexture(Texture texture) {
-			if (textures != null) {
-				textures.add(texture);
-			}
-		}
-
-		/**
-		 * This method adds a camera to the result set.
-		 * @param camera
-		 *        camera to be added to the result set
-		 */
-		public void addCamera(CameraNode camera) {
-			if (cameras != null) {
-				cameras.add(camera);
-			}
-		}
-
-		/**
-		 * This method adds a light to the result set.
-		 * @param light
-		 *        light to be added to the result set
-		 */
-		public void addLight(LightNode light) {
-			if (lights != null) {
-				lights.add(light);
-			}
-		}
-
-		/**
-		 * This method returns all loaded scenes.
-		 * @return all loaded scenes
-		 */
-		public List<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<CameraNode> getCameras() {
-			return cameras;
-		}
-
-		/**
-		 * This method returns all loaded lights.
-		 * @return all loaded lights
-		 */
-		public List<LightNode> getLights() {
-			return lights;
-		}
-
-		public int collideWith(Collidable other, CollisionResults results) throws UnsupportedCollisionException {
-			return 0;
-		}
-
-		@Override
-		public void updateModelBound() {}
-
-		@Override
-		public void setModelBound(BoundingVolume modelBound) {}
-
-		@Override
-		public int getVertexCount() {
-			return 0;
-		}
-
-		@Override
-		public int getTriangleCount() {
-			return 0;
-		}
-
-		@Override
-		public Spatial deepClone() {
-			return null;
-		}
-
-		@Override
-		public void depthFirstTraversal(SceneGraphVisitor visitor) {}
-
-		@Override
-		protected void breadthFirstTraversal(SceneGraphVisitor visitor, Queue<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;
+    /**
+     * FramesPerSecond parameter describe how many frames there are in each second. It allows to calculate the time
+     * between the frames.
+     */
+    protected int                    fps                    = DEFAULT_FPS;
+    /**
+     * This variable is a bitwise flag of FeatureToLoad interface values; By default everything is being loaded.
+     */
+    protected int                    featuresToLoad         = FeaturesToLoad.ALL;
+    /** This variable determines if assets that are not linked to the objects should be loaded. */
+    protected boolean                loadUnlinkedAssets;
+    /** The root path for all the assets. */
+    protected String                 assetRootPath;
+    /** This variable indicate if Y axis is UP axis. If not then Z is up. By default set to true. */
+    protected boolean                fixUpAxis              = true;
+    /** Generated textures resolution (PPU - Pixels Per Unit). */
+    protected int                    generatedTexturePPU    = 128;
+    /**
+     * The name of world settings that the importer will use. If not set or specified name does not occur in the file
+     * then the first world settings in the file will be used.
+     */
+    protected String                 usedWorld;
+    /**
+     * User's default material that is set fo objects that have no material definition in blender. The default value is
+     * null. If the value is null the importer will use its own default material (gray color - like in blender).
+     */
+    protected Material               defaultMaterial;
+    /** Face cull mode. By default it is disabled. */
+    protected FaceCullMode           faceCullMode           = FaceCullMode.Back;
+    /**
+     * Variable describes which layers will be loaded. N-th bit set means N-th layer will be loaded.
+     * If set to -1 then the current layer will be loaded.
+     */
+    protected int                    layersToLoad           = -1;
+    /** A variable that toggles the object custom properties loading. */
+    protected boolean                loadObjectProperties   = true;
+    /** Maximum texture size. Might be dependant on the graphic card. */
+    protected int                    maxTextureSize         = -1;
+    /** Allows to toggle generated textures loading. Disabled by default because it very often takes too much memory and needs to be used wisely. */
+    protected boolean                loadGeneratedTextures;
+    /** Tells if the mipmaps will be generated by jme or not. By default generation is dependant on the blender settings. */
+    protected MipmapGenerationMethod mipmapGenerationMethod = MipmapGenerationMethod.GENERATE_WHEN_NEEDED;
+
+    /**
+     * Constructor used by serialization mechanisms.
+     */
+    public BlenderKey() {
+    }
+
+    /**
+     * Constructor. Creates a key for the given file name.
+     * @param name
+     *            the name (path) of a file
+     */
+    public BlenderKey(String name) {
+        super(name);
+    }
+
+    /**
+     * This method returns frames per second amount. The default value is BlenderKey.DEFAULT_FPS = 25.
+     * @return the frames per second amount
+     */
+    public int getFps() {
+        return fps;
+    }
+
+    /**
+     * This method sets frames per second amount.
+     * @param fps
+     *            the frames per second amount
+     */
+    public void setFps(int fps) {
+        this.fps = fps;
+    }
+
+    /**
+     * This method returns the face cull mode.
+     * @return the face cull mode
+     */
+    public FaceCullMode getFaceCullMode() {
+        return faceCullMode;
+    }
+
+    /**
+     * This method sets the face cull mode.
+     * @param faceCullMode
+     *            the face cull mode
+     */
+    public void setFaceCullMode(FaceCullMode faceCullMode) {
+        this.faceCullMode = faceCullMode;
+    }
+
+    /**
+     * This method sets layers to be loaded.
+     * @param layersToLoad
+     *            layers to be loaded
+     */
+    public void setLayersToLoad(int layersToLoad) {
+        this.layersToLoad = layersToLoad;
+    }
+
+    /**
+     * This method returns layers to be loaded.
+     * @return layers to be loaded
+     */
+    public int getLayersToLoad() {
+        return layersToLoad;
+    }
+
+    /**
+     * This method sets the properies loading policy.
+     * By default the value is true.
+     * @param loadObjectProperties
+     *            true to load properties and false to suspend their loading
+     */
+    public void setLoadObjectProperties(boolean loadObjectProperties) {
+        this.loadObjectProperties = loadObjectProperties;
+    }
+
+    /**
+     * @return the current properties loading properties
+     */
+    public boolean isLoadObjectProperties() {
+        return loadObjectProperties;
+    }
+
+    /**
+     * @return maximum texture size (width/height)
+     */
+    public int getMaxTextureSize() {
+        if (maxTextureSize <= 0) {
+            try {
+                maxTextureSize = GL11.glGetInteger(GL11.GL_MAX_TEXTURE_SIZE);
+            } catch (Exception e) {
+                // this is in case this method was called before openGL initialization
+                return 8192;
+            }
+        }
+        return maxTextureSize;
+    }
+
+    /**
+     * This method sets the maximum texture size.
+     * @param maxTextureSize
+     *            the maximum texture size
+     */
+    public void setMaxTextureSize(int maxTextureSize) {
+        this.maxTextureSize = maxTextureSize;
+    }
+
+    /**
+     * This method sets the flag that toggles the generated textures loading.
+     * @param loadGeneratedTextures
+     *            <b>true</b> if generated textures should be loaded and <b>false</b> otherwise
+     */
+    public void setLoadGeneratedTextures(boolean loadGeneratedTextures) {
+        this.loadGeneratedTextures = loadGeneratedTextures;
+    }
+
+    /**
+     * @return tells if the generated textures should be loaded (<b>false</b> is the default value)
+     */
+    public boolean isLoadGeneratedTextures() {
+        return loadGeneratedTextures;
+    }
+
+    /**
+     * This method sets the asset root path.
+     * @param assetRootPath
+     *            the assets root path
+     */
+    public void setAssetRootPath(String assetRootPath) {
+        this.assetRootPath = assetRootPath;
+    }
+
+    /**
+     * This method returns the asset root path.
+     * @return the asset root path
+     */
+    public String getAssetRootPath() {
+        return assetRootPath;
+    }
+
+    /**
+     * This method adds features to be loaded.
+     * @param featuresToLoad
+     *            bitwise flag of FeaturesToLoad interface values
+     */
+    public void includeInLoading(int featuresToLoad) {
+        this.featuresToLoad |= featuresToLoad;
+    }
+
+    /**
+     * This method removes features from being loaded.
+     * @param featuresNotToLoad
+     *            bitwise flag of FeaturesToLoad interface values
+     */
+    public void excludeFromLoading(int featuresNotToLoad) {
+        this.featuresToLoad &= ~featuresNotToLoad;
+    }
+
+    /**
+     * This method returns bitwise value of FeaturesToLoad interface value. It describes features that will be loaded by
+     * the blender file loader.
+     * @return features that will be loaded by the blender file loader
+     */
+    public int getFeaturesToLoad() {
+        return featuresToLoad;
+    }
+
+    /**
+     * This method determines if unlinked assets should be loaded.
+     * If not then only objects on selected layers will be loaded and their assets if required.
+     * If yes then all assets will be loaded even if they are on inactive layers or are not linked
+     * to anything.
+     * @return <b>true</b> if unlinked assets should be loaded and <b>false</b> otherwise
+     */
+    public boolean isLoadUnlinkedAssets() {
+        return loadUnlinkedAssets;
+    }
+
+    /**
+     * This method sets if unlinked assets should be loaded.
+     * If not then only objects on selected layers will be loaded and their assets if required.
+     * If yes then all assets will be loaded even if they are on inactive layers or are not linked
+     * to anything.
+     * @param loadUnlinkedAssets
+     *            <b>true</b> if unlinked assets should be loaded and <b>false</b> otherwise
+     */
+    public void setLoadUnlinkedAssets(boolean loadUnlinkedAssets) {
+        this.loadUnlinkedAssets = loadUnlinkedAssets;
+    }
+
+    /**
+     * This method creates an object where loading results will be stores. Only those features will be allowed to store
+     * that were specified by features-to-load flag.
+     * @return an object to store loading results
+     */
+    public LoadingResults prepareLoadingResults() {
+        return new LoadingResults(featuresToLoad);
+    }
+
+    /**
+     * This method sets the fix up axis state. If set to true then Y is up axis. Otherwise the up i Z axis. By default Y
+     * is up axis.
+     * @param fixUpAxis
+     *            the up axis state variable
+     */
+    public void setFixUpAxis(boolean fixUpAxis) {
+        this.fixUpAxis = fixUpAxis;
+    }
+
+    /**
+     * This method returns the fix up axis state. If set to true then Y is up axis. Otherwise the up i Z axis. By
+     * default Y is up axis.
+     * @return the up axis state variable
+     */
+    public boolean isFixUpAxis() {
+        return fixUpAxis;
+    }
+
+    /**
+     * This method sets the generated textures resolution.
+     * @param generatedTexturePPU
+     *            the generated textures resolution
+     */
+    public void setGeneratedTexturePPU(int generatedTexturePPU) {
+        this.generatedTexturePPU = generatedTexturePPU;
+    }
+
+    /**
+     * @return the generated textures resolution
+     */
+    public int getGeneratedTexturePPU() {
+        return generatedTexturePPU;
+    }
+
+    /**
+     * @return mipmaps generation method
+     */
+    public MipmapGenerationMethod getMipmapGenerationMethod() {
+        return mipmapGenerationMethod;
+    }
+
+    /**
+     * @param mipmapGenerationMethod
+     *            mipmaps generation method
+     */
+    public void setMipmapGenerationMethod(MipmapGenerationMethod mipmapGenerationMethod) {
+        this.mipmapGenerationMethod = mipmapGenerationMethod;
+    }
+
+    /**
+     * This mehtod sets the name of the WORLD data block taht should be used during file loading. By default the name is
+     * not set. If no name is set or the given name does not occur in the file - the first WORLD data block will be used
+     * during loading (assumin any exists in the file).
+     * @param usedWorld
+     *            the name of the WORLD block used during loading
+     */
+    public void setUsedWorld(String usedWorld) {
+        this.usedWorld = usedWorld;
+    }
+
+    /**
+     * This mehtod returns the name of the WORLD data block taht should be used during file loading.
+     * @return the name of the WORLD block used during loading
+     */
+    public String getUsedWorld() {
+        return usedWorld;
+    }
+
+    /**
+     * This method sets the default material for objects.
+     * @param defaultMaterial
+     *            the default material
+     */
+    public void setDefaultMaterial(Material defaultMaterial) {
+        this.defaultMaterial = defaultMaterial;
+    }
+
+    /**
+     * This method returns the default material.
+     * @return the default material
+     */
+    public Material getDefaultMaterial() {
+        return defaultMaterial;
+    }
+
+    @Override
+    public void write(JmeExporter e) throws IOException {
+        super.write(e);
+        OutputCapsule oc = e.getCapsule(this);
+        oc.write(fps, "fps", DEFAULT_FPS);
+        oc.write(featuresToLoad, "features-to-load", FeaturesToLoad.ALL);
+        oc.write(loadUnlinkedAssets, "load-unlinked-assets", false);
+        oc.write(assetRootPath, "asset-root-path", null);
+        oc.write(fixUpAxis, "fix-up-axis", true);
+        oc.write(generatedTexturePPU, "generated-texture-ppu", 128);
+        oc.write(usedWorld, "used-world", null);
+        oc.write(defaultMaterial, "default-material", null);
+        oc.write(faceCullMode, "face-cull-mode", FaceCullMode.Off);
+        oc.write(layersToLoad, "layers-to-load", -1);
+        oc.write(mipmapGenerationMethod, "mipmap-generation-method", MipmapGenerationMethod.GENERATE_WHEN_NEEDED);
+    }
+
+    @Override
+    public void read(JmeImporter e) throws IOException {
+        super.read(e);
+        InputCapsule ic = e.getCapsule(this);
+        fps = ic.readInt("fps", DEFAULT_FPS);
+        featuresToLoad = ic.readInt("features-to-load", FeaturesToLoad.ALL);
+        loadUnlinkedAssets = ic.readBoolean("load-unlinked-assets", false);
+        assetRootPath = ic.readString("asset-root-path", null);
+        fixUpAxis = ic.readBoolean("fix-up-axis", true);
+        generatedTexturePPU = ic.readInt("generated-texture-ppu", 128);
+        usedWorld = ic.readString("used-world", null);
+        defaultMaterial = (Material) ic.readSavable("default-material", null);
+        faceCullMode = ic.readEnum("face-cull-mode", FaceCullMode.class, FaceCullMode.Off);
+        layersToLoad = ic.readInt("layers-to=load", -1);
+        mipmapGenerationMethod = ic.readEnum("mipmap-generation-method", MipmapGenerationMethod.class, MipmapGenerationMethod.GENERATE_WHEN_NEEDED);
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = super.hashCode();
+        result = prime * result + (assetRootPath == null ? 0 : assetRootPath.hashCode());
+        result = prime * result + (defaultMaterial == null ? 0 : defaultMaterial.hashCode());
+        result = prime * result + (faceCullMode == null ? 0 : faceCullMode.hashCode());
+        result = prime * result + featuresToLoad;
+        result = prime * result + (fixUpAxis ? 1231 : 1237);
+        result = prime * result + fps;
+        result = prime * result + generatedTexturePPU;
+        result = prime * result + layersToLoad;
+        result = prime * result + (loadUnlinkedAssets ? 1231 : 1237);
+        result = prime * result + (usedWorld == null ? 0 : usedWorld.hashCode());
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!super.equals(obj)) {
+            return false;
+        }
+        if (this.getClass() != obj.getClass()) {
+            return false;
+        }
+        BlenderKey other = (BlenderKey) obj;
+        if (assetRootPath == null) {
+            if (other.assetRootPath != null) {
+                return false;
+            }
+        } else if (!assetRootPath.equals(other.assetRootPath)) {
+            return false;
+        }
+        if (defaultMaterial == null) {
+            if (other.defaultMaterial != null) {
+                return false;
+            }
+        } else if (!defaultMaterial.equals(other.defaultMaterial)) {
+            return false;
+        }
+        if (faceCullMode != other.faceCullMode) {
+            return false;
+        }
+        if (featuresToLoad != other.featuresToLoad) {
+            return false;
+        }
+        if (fixUpAxis != other.fixUpAxis) {
+            return false;
+        }
+        if (fps != other.fps) {
+            return false;
+        }
+        if (generatedTexturePPU != other.generatedTexturePPU) {
+            return false;
+        }
+        if (layersToLoad != other.layersToLoad) {
+            return false;
+        }
+        if (loadUnlinkedAssets != other.loadUnlinkedAssets) {
+            return false;
+        }
+        if (usedWorld == null) {
+            if (other.usedWorld != null) {
+                return false;
+            }
+        } else if (!usedWorld.equals(other.usedWorld)) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * This enum tells the importer if the mipmaps for textures will be generated by jme. <li>NEVER_GENERATE and ALWAYS_GENERATE are quite understandable <li>GENERATE_WHEN_NEEDED is an option that checks if the texture had 'Generate mipmaps' option set in blender, mipmaps are generated only when the option is set
+     * @author Marcin Roguski (Kaelthas)
+     */
+    public static enum MipmapGenerationMethod {
+        NEVER_GENERATE, ALWAYS_GENERATE, GENERATE_WHEN_NEEDED;
+    }
+
+    /**
+     * This interface describes the features of the scene that are to be loaded.
+     * @author Marcin Roguski (Kaelthas)
+     */
+    public static interface FeaturesToLoad {
+
+        int SCENES     = 0x0000FFFF;
+        int OBJECTS    = 0x0000000B;
+        int ANIMATIONS = 0x00000004;
+        int MATERIALS  = 0x00000003;
+        int TEXTURES   = 0x00000001;
+        int CAMERAS    = 0x00000020;
+        int LIGHTS     = 0x00000010;
+        int ALL        = 0xFFFFFFFF;
+    }
+
+    /**
+     * This class holds the loading results according to the given loading flag.
+     * @author Marcin Roguski (Kaelthas)
+     */
+    public static class LoadingResults extends Spatial {
+
+        /** Bitwise mask of features that are to be loaded. */
+        private final int        featuresToLoad;
+        /** The scenes from the file. */
+        private List<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<CameraNode> cameras;
+        /** All lights from the file. */
+        private List<LightNode>  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<CameraNode>();
+            }
+            if ((featuresToLoad & FeaturesToLoad.LIGHTS) != 0) {
+                lights = new ArrayList<LightNode>();
+            }
+        }
+
+        /**
+         * This method returns a bitwise flag describing what features of the blend file will be included in the result.
+         * @return bitwise mask of features that are to be loaded
+         * @see FeaturesToLoad FeaturesToLoad
+         */
+        public int getLoadedFeatures() {
+            return featuresToLoad;
+        }
+
+        /**
+         * This method adds a scene to the result set.
+         * @param scene
+         *            scene to be added to the result set
+         */
+        public void addScene(Node scene) {
+            if (scenes != null) {
+                scenes.add(scene);
+            }
+        }
+
+        /**
+         * This method adds an object to the result set.
+         * @param object
+         *            object to be added to the result set
+         */
+        public void addObject(Node object) {
+            if (objects != null) {
+                objects.add(object);
+            }
+        }
+
+        /**
+         * This method adds a material to the result set.
+         * @param material
+         *            material to be added to the result set
+         */
+        public void addMaterial(Material material) {
+            if (materials != null) {
+                materials.add(material);
+            }
+        }
+
+        /**
+         * This method adds a texture to the result set.
+         * @param texture
+         *            texture to be added to the result set
+         */
+        public void addTexture(Texture texture) {
+            if (textures != null) {
+                textures.add(texture);
+            }
+        }
+
+        /**
+         * This method adds a camera to the result set.
+         * @param camera
+         *            camera to be added to the result set
+         */
+        public void addCamera(CameraNode camera) {
+            if (cameras != null) {
+                cameras.add(camera);
+            }
+        }
+
+        /**
+         * This method adds a light to the result set.
+         * @param light
+         *            light to be added to the result set
+         */
+        public void addLight(LightNode light) {
+            if (lights != null) {
+                lights.add(light);
+            }
+        }
+
+        /**
+         * This method returns all loaded scenes.
+         * @return all loaded scenes
+         */
+        public List<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<CameraNode> getCameras() {
+            return cameras;
+        }
+
+        /**
+         * This method returns all loaded lights.
+         * @return all loaded lights
+         */
+        public List<LightNode> getLights() {
+            return lights;
+        }
+
+        public int collideWith(Collidable other, CollisionResults results) throws UnsupportedCollisionException {
+            return 0;
+        }
+
+        @Override
+        public void updateModelBound() {
+        }
+
+        @Override
+        public void setModelBound(BoundingVolume modelBound) {
+        }
+
+        @Override
+        public int getVertexCount() {
+            return 0;
+        }
+
+        @Override
+        public int getTriangleCount() {
+            return 0;
+        }
+
+        @Override
+        public Spatial deepClone() {
+            return null;
+        }
+
+        @Override
+        public void depthFirstTraversal(SceneGraphVisitor visitor) {
+        }
+
+        @Override
+        protected void breadthFirstTraversal(SceneGraphVisitor visitor, Queue<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;
+        }
+    }
 }

+ 4 - 3
engine/src/blender/com/jme3/asset/GeneratedTextureKey.java

@@ -36,7 +36,7 @@ package com.jme3.asset;
  * This key is mostly used to distinguish between textures that are loaded from
  * the given assets and those being generated automatically. Every generated
  * texture will have this kind of key attached.
- *
+ * 
  * @author Marcin Roguski (Kaelthas)
  */
 public class GeneratedTextureKey extends TextureKey {
@@ -44,8 +44,9 @@ public class GeneratedTextureKey extends TextureKey {
     /**
      * Constructor. Stores the name. Extension and folder name are empty
      * strings.
-     *
-     * @param name the name of the texture
+     * 
+     * @param name
+     *            the name of the texture
      */
     public GeneratedTextureKey(String name) {
         super(name);

+ 110 - 110
engine/src/blender/com/jme3/scene/plugins/blender/AbstractBlenderHelper.java

@@ -50,116 +50,116 @@ import com.jme3.scene.plugins.blender.objects.Properties;
  */
 public abstract class AbstractBlenderHelper {
 
-	/** The version of the blend file. */
-	protected final int	blenderVersion;
-	/** This variable indicates if the Y asxis is the UP axis or not. */
-	protected boolean						fixUpAxis;
-	/** Quaternion used to rotate data when Y is up axis. */
-	protected Quaternion					upAxisRotationQuaternion;
-	
-	/**
-	 * This constructor parses the given blender version and stores the result. Some functionalities may differ in different blender
-	 * versions.
-	 * @param blenderVersion
-	 *        the version read from the blend file
-	 * @param fixUpAxis
-     *        a variable that indicates if the Y asxis is the UP axis or not
-	 */
-	public AbstractBlenderHelper(String blenderVersion, boolean fixUpAxis) {
-		this.blenderVersion = Integer.parseInt(blenderVersion);
-		this.fixUpAxis = fixUpAxis;
-		if(fixUpAxis) {
-			upAxisRotationQuaternion = new Quaternion().fromAngles(-FastMath.HALF_PI, 0, 0);
-		}
-	}
-	
-	/**
-	 * This method clears the state of the helper so that it can be used for different calculations of another feature.
-	 */
-	public void clearState() {}
+    /** The version of the blend file. */
+    protected final int  blenderVersion;
+    /** This variable indicates if the Y asxis is the UP axis or not. */
+    protected boolean    fixUpAxis;
+    /** Quaternion used to rotate data when Y is up axis. */
+    protected Quaternion upAxisRotationQuaternion;
 
-	/**
-	 * This method should be used to check if the text is blank. Avoid using text.trim().length()==0. This causes that more strings are
-	 * being created and stored in the memory. It can be unwise especially inside loops.
-	 * @param text
-	 *        the text to be checked
-	 * @return <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;
-	}
+    /**
+     * This constructor parses the given blender version and stores the result. Some functionalities may differ in different blender
+     * versions.
+     * @param blenderVersion
+     *            the version read from the blend file
+     * @param fixUpAxis
+     *            a variable that indicates if the Y asxis is the UP axis or not
+     */
+    public AbstractBlenderHelper(String blenderVersion, boolean fixUpAxis) {
+        this.blenderVersion = Integer.parseInt(blenderVersion);
+        this.fixUpAxis = fixUpAxis;
+        if (fixUpAxis) {
+            upAxisRotationQuaternion = new Quaternion().fromAngles(-FastMath.HALF_PI, 0, 0);
+        }
+    }
 
-	/**
-	 * This method loads the properties if they are available and defined for the structure.
-	 * @param structure
-	 *        the structure we read the properties from
-	 * @param blenderContext
-	 *        the blender context
-	 * @return loaded properties or null if they are not available
-	 * @throws BlenderFileException
-	 *         an exception is thrown when the blend file is somehow corrupted
-	 */
-	protected Properties loadProperties(Structure structure, BlenderContext blenderContext) throws BlenderFileException {
-		Properties properties = null;
-		Structure id = (Structure) structure.getFieldValue("ID");
-		if (id != null) {
-			Pointer pProperties = (Pointer) id.getFieldValue("properties");
-			if (pProperties.isNotNull()) {
-				Structure propertiesStructure = pProperties.fetchData(blenderContext.getInputStream()).get(0);
-				properties = new Properties();
-				properties.load(propertiesStructure, blenderContext);
-			}
-		}
-		return properties;
-	}
-	
-	/**
-	 * The method applies properties to the given spatial. The Properties
-	 * instance cannot be directly applied because the end-user might not have
-	 * the blender plugin jar file and thus receive ClassNotFoundException. The
-	 * values are set by name instead.
-	 * 
-	 * @param spatial
-	 *            the spatial that is to have properties applied
-	 * @param properties
-	 *            the properties to be applied
-	 */
-	protected void applyProperties(Spatial spatial, Properties properties) {
-		List<String> propertyNames = properties.getSubPropertiesNames();
-		if(propertyNames != null && propertyNames.size() > 0) {
-			for(String propertyName : propertyNames) {
-				Object value = properties.findValue(propertyName);
-				if(value instanceof Savable || value instanceof Boolean || value instanceof String ||
-				   value instanceof Float || value instanceof Integer || value instanceof Long) {
-					spatial.setUserData(propertyName, value);
-				} else if(value instanceof Double) {
-					spatial.setUserData(propertyName, ((Double) value).floatValue());
-				} else if(value instanceof int[]) {
-					spatial.setUserData(propertyName, Arrays.toString((int[])value));
-				} else if(value instanceof float[]) {
-					spatial.setUserData(propertyName, Arrays.toString((float[])value));
-				} else if(value instanceof double[]) {
-					spatial.setUserData(propertyName, Arrays.toString((double[])value));
-				}
-			}
-		}
-	}
-	
-	/**
-	 * This method analyzes the given structure and the data contained within
-	 * blender context and decides if the feature should be loaded.
-	 * @param structure
-	 *        structure to be analyzed
-	 * @param blenderContext
-	 *        the blender context
-	 * @return <b>true</b> if the feature should be loaded and false otherwise
-	 */
-	public abstract boolean shouldBeLoaded(Structure structure, BlenderContext blenderContext);
+    /**
+     * This method clears the state of the helper so that it can be used for different calculations of another feature.
+     */
+    public void clearState() {
+    }
+
+    /**
+     * This method should be used to check if the text is blank. Avoid using text.trim().length()==0. This causes that more strings are
+     * being created and stored in the memory. It can be unwise especially inside loops.
+     * @param text
+     *            the text to be checked
+     * @return <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;
+    }
+
+    /**
+     * This method loads the properties if they are available and defined for the structure.
+     * @param structure
+     *            the structure we read the properties from
+     * @param blenderContext
+     *            the blender context
+     * @return loaded properties or null if they are not available
+     * @throws BlenderFileException
+     *             an exception is thrown when the blend file is somehow corrupted
+     */
+    protected Properties loadProperties(Structure structure, BlenderContext blenderContext) throws BlenderFileException {
+        Properties properties = null;
+        Structure id = (Structure) structure.getFieldValue("ID");
+        if (id != null) {
+            Pointer pProperties = (Pointer) id.getFieldValue("properties");
+            if (pProperties.isNotNull()) {
+                Structure propertiesStructure = pProperties.fetchData(blenderContext.getInputStream()).get(0);
+                properties = new Properties();
+                properties.load(propertiesStructure, blenderContext);
+            }
+        }
+        return properties;
+    }
+
+    /**
+     * The method applies properties to the given spatial. The Properties
+     * instance cannot be directly applied because the end-user might not have
+     * the blender plugin jar file and thus receive ClassNotFoundException. The
+     * values are set by name instead.
+     * 
+     * @param spatial
+     *            the spatial that is to have properties applied
+     * @param properties
+     *            the properties to be applied
+     */
+    protected void applyProperties(Spatial spatial, Properties properties) {
+        List<String> propertyNames = properties.getSubPropertiesNames();
+        if (propertyNames != null && propertyNames.size() > 0) {
+            for (String propertyName : propertyNames) {
+                Object value = properties.findValue(propertyName);
+                if (value instanceof Savable || value instanceof Boolean || value instanceof String || value instanceof Float || value instanceof Integer || value instanceof Long) {
+                    spatial.setUserData(propertyName, value);
+                } else if (value instanceof Double) {
+                    spatial.setUserData(propertyName, ((Double) value).floatValue());
+                } else if (value instanceof int[]) {
+                    spatial.setUserData(propertyName, Arrays.toString((int[]) value));
+                } else if (value instanceof float[]) {
+                    spatial.setUserData(propertyName, Arrays.toString((float[]) value));
+                } else if (value instanceof double[]) {
+                    spatial.setUserData(propertyName, Arrays.toString((double[]) value));
+                }
+            }
+        }
+    }
+
+    /**
+     * This method analyzes the given structure and the data contained within
+     * blender context and decides if the feature should be loaded.
+     * @param structure
+     *            structure to be analyzed
+     * @param blenderContext
+     *            the blender context
+     * @return <b>true</b> if the feature should be loaded and false otherwise
+     */
+    public abstract boolean shouldBeLoaded(Structure structure, BlenderContext blenderContext);
 }

+ 120 - 120
engine/src/blender/com/jme3/scene/plugins/blender/AbstractBlenderLoader.java

@@ -57,134 +57,134 @@ import com.jme3.scene.plugins.blender.objects.ObjectHelper;
  * This class converts blender file blocks into jMonkeyEngine data structures.
  * @author Marcin Roguski (Kaelthas)
  */
-/* package */ abstract class AbstractBlenderLoader implements AssetLoader {
-	private static final Logger LOGGER = Logger.getLogger(AbstractBlenderLoader.class.getName());
-	
-	protected BlenderContext	blenderContext;
+/* package */abstract class AbstractBlenderLoader implements AssetLoader {
+    private static final Logger LOGGER = Logger.getLogger(AbstractBlenderLoader.class.getName());
 
-	/**
-	 * This method converts the given structure to a scene node.
-	 * @param structure
-	 *        structure of a scene
-	 * @return scene's node
-	 */
-	public Node toScene(Structure structure) {
-		if ((blenderContext.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.SCENES) == 0) {
-			return null;
-		}
-		Node result = new Node(structure.getName());
-		try {
-			List<Structure> base = ((Structure)structure.getFieldValue("base")).evaluateListBase(blenderContext);
-			for(Structure b : base) {
-				Pointer pObject = (Pointer) b.getFieldValue("object");
-				if(pObject.isNotNull()) {
-					Structure objectStructure = pObject.fetchData(blenderContext.getInputStream()).get(0);
-					Object object = this.toObject(objectStructure);
-					if(object instanceof LightNode && (blenderContext.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.LIGHTS) != 0) {
-                    	result.addLight(((LightNode)object).getLight());
-                    	result.attachChild((LightNode) object);
-					} else if (object instanceof Node && (blenderContext.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.OBJECTS) != 0) {
-						LOGGER.log(Level.FINE, "{0}: {1}--> {2}", new Object[] { ((Node) object).getName(), ((Node) object).getLocalTranslation().toString(), ((Node) object).getParent() == null ? "null" : ((Node) object).getParent().getName() });
-						if (((Node) object).getParent() == null) {
-							result.attachChild((Spatial) object);
+    protected BlenderContext    blenderContext;
+
+    /**
+     * This method converts the given structure to a scene node.
+     * @param structure
+     *            structure of a scene
+     * @return scene's node
+     */
+    public Node toScene(Structure structure) {
+        if ((blenderContext.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.SCENES) == 0) {
+            return null;
+        }
+        Node result = new Node(structure.getName());
+        try {
+            List<Structure> base = ((Structure) structure.getFieldValue("base")).evaluateListBase(blenderContext);
+            for (Structure b : base) {
+                Pointer pObject = (Pointer) b.getFieldValue("object");
+                if (pObject.isNotNull()) {
+                    Structure objectStructure = pObject.fetchData(blenderContext.getInputStream()).get(0);
+                    Object object = this.toObject(objectStructure);
+                    if (object instanceof LightNode && (blenderContext.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.LIGHTS) != 0) {
+                        result.addLight(((LightNode) object).getLight());
+                        result.attachChild((LightNode) object);
+                    } else if (object instanceof Node && (blenderContext.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.OBJECTS) != 0) {
+                        LOGGER.log(Level.FINE, "{0}: {1}--> {2}", new Object[] { ((Node) object).getName(), ((Node) object).getLocalTranslation().toString(), ((Node) object).getParent() == null ? "null" : ((Node) object).getParent().getName() });
+                        if (((Node) object).getParent() == null) {
+                            result.attachChild((Spatial) object);
                         }
-					}
-				}
-			}
-		} catch (BlenderFileException e) {
-			LOGGER.log(Level.SEVERE, e.getLocalizedMessage(), e);
-		}
-		return result;
-	}
+                    }
+                }
+            }
+        } catch (BlenderFileException e) {
+            LOGGER.log(Level.SEVERE, e.getLocalizedMessage(), e);
+        }
+        return result;
+    }
 
-	/**
-	 * This method converts the given structure to a camera.
-	 * @param structure
-	 *        structure of a camera
-	 * @return camera's node
-	 */
-	public CameraNode toCamera(Structure structure) throws BlenderFileException {
-		CameraHelper cameraHelper = blenderContext.getHelper(CameraHelper.class);
-		if (cameraHelper.shouldBeLoaded(structure, blenderContext)) {
-			return cameraHelper.toCamera(structure, blenderContext);
-		}
-		return null;
-	}
+    /**
+     * This method converts the given structure to a camera.
+     * @param structure
+     *            structure of a camera
+     * @return camera's node
+     */
+    public CameraNode toCamera(Structure structure) throws BlenderFileException {
+        CameraHelper cameraHelper = blenderContext.getHelper(CameraHelper.class);
+        if (cameraHelper.shouldBeLoaded(structure, blenderContext)) {
+            return cameraHelper.toCamera(structure, blenderContext);
+        }
+        return null;
+    }
 
-	/**
-	 * This method converts the given structure to a light.
-	 * @param structure
-	 *        structure of a light
-	 * @return light's node
-	 */
-	public LightNode toLight(Structure structure) throws BlenderFileException {
-		LightHelper lightHelper = blenderContext.getHelper(LightHelper.class);
-		if (lightHelper.shouldBeLoaded(structure, blenderContext)) {
-			return lightHelper.toLight(structure, blenderContext);
-		}
-		return null;
-	}
+    /**
+     * This method converts the given structure to a light.
+     * @param structure
+     *            structure of a light
+     * @return light's node
+     */
+    public LightNode toLight(Structure structure) throws BlenderFileException {
+        LightHelper lightHelper = blenderContext.getHelper(LightHelper.class);
+        if (lightHelper.shouldBeLoaded(structure, blenderContext)) {
+            return lightHelper.toLight(structure, blenderContext);
+        }
+        return null;
+    }
 
-	/**
-	 * This method converts the given structure to a node.
-	 * @param structure
-	 *        structure of an object
-	 * @return object's node
-	 */
-	public Object toObject(Structure structure) throws BlenderFileException {
-		ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class);
-		if (objectHelper.shouldBeLoaded(structure, blenderContext)) {
-			return objectHelper.toObject(structure, blenderContext);
-		}
-		return null;
-	}
+    /**
+     * This method converts the given structure to a node.
+     * @param structure
+     *            structure of an object
+     * @return object's node
+     */
+    public Object toObject(Structure structure) throws BlenderFileException {
+        ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class);
+        if (objectHelper.shouldBeLoaded(structure, blenderContext)) {
+            return objectHelper.toObject(structure, blenderContext);
+        }
+        return null;
+    }
 
-	/**
-	 * This method converts the given structure to a list of geometries.
-	 * @param structure
-	 *        structure of a mesh
-	 * @return list of geometries
-	 */
-	public List<Geometry> toMesh(Structure structure) throws BlenderFileException {
-		MeshHelper meshHelper = blenderContext.getHelper(MeshHelper.class);
-		if (meshHelper.shouldBeLoaded(structure, blenderContext)) {
-			return meshHelper.toMesh(structure, blenderContext);
-		}
-		return null;
-	}
+    /**
+     * This method converts the given structure to a list of geometries.
+     * @param structure
+     *            structure of a mesh
+     * @return list of geometries
+     */
+    public List<Geometry> toMesh(Structure structure) throws BlenderFileException {
+        MeshHelper meshHelper = blenderContext.getHelper(MeshHelper.class);
+        if (meshHelper.shouldBeLoaded(structure, blenderContext)) {
+            return meshHelper.toMesh(structure, blenderContext);
+        }
+        return null;
+    }
 
-//	/**
-//	 * This method converts the given structure to a material.
-//	 * @param structure
-//	 *        structure of a material
-//	 * @return material's node
-//	 */
-//	public Material toMaterial(Structure structure) throws BlenderFileException {
-//		MaterialHelper materialHelper = blenderContext.getHelper(MaterialHelper.class);
-//		if (materialHelper.shouldBeLoaded(structure, blenderContext)) {
-//			return materialHelper.toMaterial(structure, blenderContext);
-//		}
-//		return null;
-//	}
+    // /**
+    // * This method converts the given structure to a material.
+    // * @param structure
+    // * structure of a material
+    // * @return material's node
+    // */
+    // public Material toMaterial(Structure structure) throws BlenderFileException {
+    // MaterialHelper materialHelper = blenderContext.getHelper(MaterialHelper.class);
+    // if (materialHelper.shouldBeLoaded(structure, blenderContext)) {
+    // return materialHelper.toMaterial(structure, blenderContext);
+    // }
+    // return null;
+    // }
 
-	/**
-	 * This method returns the data read from the WORLD file block. The block contains data that can be stored as
-	 * separate jme features and therefore cannot be returned as a single jME scene feature.
-	 * @param structure
-	 *        the structure with WORLD block data
-	 * @return data read from the WORLD block that can be added to the scene
-	 */
-	public WorldData toWorldData(Structure structure) {
-		WorldData result = new WorldData();
+    /**
+     * This method returns the data read from the WORLD file block. The block contains data that can be stored as
+     * separate jme features and therefore cannot be returned as a single jME scene feature.
+     * @param structure
+     *            the structure with WORLD block data
+     * @return data read from the WORLD block that can be added to the scene
+     */
+    public WorldData toWorldData(Structure structure) {
+        WorldData result = new WorldData();
 
-		// reading ambient light
-		AmbientLight ambientLight = new AmbientLight();
-		float ambr = ((Number) structure.getFieldValue("ambr")).floatValue();
-		float ambg = ((Number) structure.getFieldValue("ambg")).floatValue();
-		float ambb = ((Number) structure.getFieldValue("ambb")).floatValue();
-		ambientLight.setColor(new ColorRGBA(ambr, ambg, ambb, 0.0f));
-		result.setAmbientLight(ambientLight);
+        // reading ambient light
+        AmbientLight ambientLight = new AmbientLight();
+        float ambr = ((Number) structure.getFieldValue("ambr")).floatValue();
+        float ambg = ((Number) structure.getFieldValue("ambg")).floatValue();
+        float ambb = ((Number) structure.getFieldValue("ambb")).floatValue();
+        ambientLight.setColor(new ColorRGBA(ambr, ambg, ambb, 0.0f));
+        result.setAmbientLight(ambientLight);
 
-		return result;
-	}
+        return result;
+    }
 }

+ 622 - 621
engine/src/blender/com/jme3/scene/plugins/blender/BlenderContext.java

@@ -65,625 +65,626 @@ import com.jme3.scene.plugins.ogre.AnimData;
  * @author Marcin Roguski (Kaelthas)
  */
 public class BlenderContext {
-	private static final Logger					LOGGER					= Logger.getLogger(BlenderContext.class.getName());
-
-	/** The blender file version. */
-	private int									blenderVersion;
-	/** The blender key. */
-	private BlenderKey							blenderKey;
-	/** The header of the file block. */
-	private DnaBlockData						dnaBlockData;
-	/** The scene structure. */
-	private Structure						sceneStructure;
-	/** The input stream of the blend file. */
-	private BlenderInputStream					inputStream;
-	/** The asset manager. */
-	private AssetManager						assetManager;
-	/**
-	 * A map containing the file block headers. The key is the old pointer
-	 * address.
-	 */
-	private Map<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 list of constraints for the specified object. */
-	protected Map<Long, List<Constraint>>		constraints				= new HashMap<Long, List<Constraint>>();
-	/** Anim data loaded for features. */
-	private Map<Long, AnimData>					animData				= new HashMap<Long, AnimData>();
-	/** Loaded skeletons. */
-	private Map<Long, Skeleton>					skeletons				= new HashMap<Long, Skeleton>();
-	/** A map of mesh contexts. */
-	protected Map<Long, MeshContext>			meshContexts			= new HashMap<Long, MeshContext>();
-	/** A map of bone contexts. */
-	protected Map<Long, BoneContext>			boneContexts			= new HashMap<Long, BoneContext>();
-	/** A map og helpers that perform loading. */
-	private Map<String, AbstractBlenderHelper>	helpers					= new HashMap<String, AbstractBlenderHelper>();
-
-	/**
-	 * This method sets the blender file version.
-	 * 
-	 * @param blenderVersion
-	 *            the blender file version
-	 */
-	public void setBlenderVersion(String blenderVersion) {
-		this.blenderVersion = Integer.parseInt(blenderVersion);
-	}
-
-	/**
-	 * @return the blender file version
-	 */
-	public int getBlenderVersion() {
-		return blenderVersion;
-	}
-
-	/**
-	 * This method sets the blender key.
-	 * 
-	 * @param blenderKey
-	 *            the blender key
-	 */
-	public void setBlenderKey(BlenderKey blenderKey) {
-		this.blenderKey = blenderKey;
-	}
-
-	/**
-	 * This method returns the blender key.
-	 * 
-	 * @return the blender key
-	 */
-	public BlenderKey getBlenderKey() {
-		return blenderKey;
-	}
-
-	/**
-	 * This method sets the dna block data.
-	 * 
-	 * @param dnaBlockData
-	 *            the dna block data
-	 */
-	public void setBlockData(DnaBlockData dnaBlockData) {
-		this.dnaBlockData = dnaBlockData;
-	}
-
-	/**
-	 * This method returns the dna block data.
-	 * 
-	 * @return the dna block data
-	 */
-	public DnaBlockData getDnaBlockData() {
-		return dnaBlockData;
-	}
-	/**
-	 * This method sets the scene structure data.
-	 * 
-	 * @param sceneStructure
-	 *            the scene structure data
-	 */
-	public void setSceneStructure(Structure sceneStructure) {
-		this.sceneStructure = sceneStructure;
-	}
-
-	/**
-	 * This method returns the scene structure data.
-	 * 
-	 * @return the scene structure data
-	 */
-	public Structure getSceneStructure() {
-		return sceneStructure;
-	}
-
-	/**
-	 * This method returns the asset manager.
-	 * 
-	 * @return the asset manager
-	 */
-	public AssetManager getAssetManager() {
-		return assetManager;
-	}
-
-	/**
-	 * This method sets the asset manager.
-	 * 
-	 * @param assetManager
-	 *            the asset manager
-	 */
-	public void setAssetManager(AssetManager assetManager) {
-		this.assetManager = assetManager;
-	}
-
-	/**
-	 * This method returns the input stream of the blend file.
-	 * 
-	 * @return the input stream of the blend file
-	 */
-	public BlenderInputStream getInputStream() {
-		return inputStream;
-	}
-
-	/**
-	 * This method sets the input stream of the blend file.
-	 * 
-	 * @param inputStream
-	 *            the input stream of the blend file
-	 */
-	public void setInputStream(BlenderInputStream inputStream) {
-		this.inputStream = inputStream;
-	}
-
-	/**
-	 * This method adds a file block header to the map. Its old memory address
-	 * is the key.
-	 * 
-	 * @param oldMemoryAddress
-	 *            the address of the block header
-	 * @param fileBlockHeader
-	 *            the block header to store
-	 */
-	public void addFileBlockHeader(Long oldMemoryAddress, FileBlockHeader fileBlockHeader) {
-		fileBlockHeadersByOma.put(oldMemoryAddress, fileBlockHeader);
-		List<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;
-		}
-	}
-
-	/**
-	 * This method adds new ipo curve for the feature.
-	 * 
-	 * @param ownerOMA
-	 *            the OMA of blender feature that owns the ipo
-	 * @param ipo
-	 *            the ipo to be added
-	 */
-	public void addIpo(Long ownerOMA, Ipo ipo) {
-		loadedIpos.put(ownerOMA, ipo);
-	}
-
-	/**
-	 * This method removes the ipo curve from the feature.
-	 * 
-	 * @param ownerOma
-	 *            the OMA of blender feature that owns the ipo
-	 */
-	public Ipo removeIpo(Long ownerOma) {
-		return loadedIpos.remove(ownerOma);
-	}
-
-	/**
-	 * This method returns the ipo curve of the feature.
-	 * 
-	 * @param ownerOMA
-	 *            the OMA of blender feature that owns the ipo
-	 */
-	public Ipo getIpo(Long ownerOMA) {
-		return loadedIpos.get(ownerOMA);
-	}
-
-	/**
-	 * This method adds a new modifier to the list.
-	 * 
-	 * @param ownerOMA
-	 *            the owner's old memory address
-	 * @param modifier
-	 *            the object's modifier
-	 */
-	public void addModifier(Long ownerOMA, Modifier modifier) {
-		List<Modifier> objectModifiers = this.modifiers.get(ownerOMA);
-		if (objectModifiers == null) {
-			objectModifiers = new ArrayList<Modifier>();
-			this.modifiers.put(ownerOMA, objectModifiers);
-		}
-		objectModifiers.add(modifier);
-	}
-
-	/**
-	 * This method returns modifiers for the object specified by its old memory
-	 * address and the modifier type. If no modifiers are found - empty list is
-	 * returned. If the type is null - all modifiers for the object are
-	 * returned.
-	 * 
-	 * @param objectOMA
-	 *            object's old memory address
-	 * @param type
-	 *            the type of the modifier
-	 * @return the list of object's modifiers
-	 */
-	public List<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 method adds a new modifier to the list.
-	 * 
-	 * @param ownerOMA
-	 *            the owner's old memory address
-	 * @param constraints
-	 *            the object's constraints
-	 */
-	public void addConstraints(Long ownerOMA, List<Constraint> constraints) {
-		List<Constraint> objectConstraints = this.constraints.get(ownerOMA);
-		if (objectConstraints == null) {
-			objectConstraints = new ArrayList<Constraint>();
-			this.constraints.put(ownerOMA, objectConstraints);
-		}
-		objectConstraints.addAll(constraints);
-	}
-
-	/**
-	 * This method returns constraints for the object specified by its old
-	 * memory address. If no modifiers are found - <b>null</b> is returned.
-	 * 
-	 * @param objectOMA
-	 *            object's old memory address
-	 * @return the list of object's modifiers or null
-	 */
-	public List<Constraint> getConstraints(Long objectOMA) {
-		return objectOMA == null ? null : constraints.get(objectOMA);
-	}
-	
-	/**
-	 * @return all available constraints
-	 */
-	public List<Constraint> getAllConstraints() {
-		List<Constraint> result = new ArrayList<Constraint>();
-		for(Entry<Long, List<Constraint>> entry : constraints.entrySet()) {
-			result.addAll(entry.getValue());
-		}
-		return result;
-	}
-
-	/**
-	 * This method sets the anim data for the specified OMA of its owner.
-	 * 
-	 * @param ownerOMA
-	 *            the owner's old memory address
-	 * @param animData
-	 *            the animation data for the feature specified by ownerOMA
-	 */
-	public void setAnimData(Long ownerOMA, AnimData animData) {
-		this.animData.put(ownerOMA, animData);
-	}
-
-	/**
-	 * This method returns the animation data for the specified owner.
-	 * 
-	 * @param ownerOMA
-	 *            the old memory address of the animation data owner
-	 * @return the animation data or null if none exists
-	 */
-	public AnimData getAnimData(Long ownerOMA) {
-		return this.animData.get(ownerOMA);
-	}
-
-	/**
-	 * This method sets the skeleton for the specified OMA of its owner.
-	 * 
-	 * @param skeletonOMA
-	 *            the skeleton's old memory address
-	 * @param skeleton
-	 *            the skeleton specified by the given OMA
-	 */
-	public void setSkeleton(Long skeletonOMA, Skeleton skeleton) {
-		this.skeletons.put(skeletonOMA, skeleton);
-	}
-
-	/**
-	 * This method returns the skeleton for the specified OMA of its owner.
-	 * 
-	 * @param skeletonOMA
-	 *            the skeleton's old memory address
-	 * @return the skeleton specified by the given OMA
-	 */
-	public Skeleton getSkeleton(Long skeletonOMA) {
-		return this.skeletons.get(skeletonOMA);
-	}
-
-	/**
-	 * This method sets the mesh context for the given mesh old memory address.
-	 * If the context is already set it will be replaced.
-	 * 
-	 * @param meshOMA
-	 *            the mesh's old memory address
-	 * @param meshContext
-	 *            the mesh's context
-	 */
-	public void setMeshContext(Long meshOMA, MeshContext meshContext) {
-		this.meshContexts.put(meshOMA, meshContext);
-	}
-
-	/**
-	 * This method returns the mesh context for the given mesh old memory
-	 * address. If no context exists then <b>null</b> is returned.
-	 * 
-	 * @param meshOMA
-	 *            the mesh's old memory address
-	 * @return mesh's context
-	 */
-	public MeshContext getMeshContext(Long meshOMA) {
-		return this.meshContexts.get(meshOMA);
-	}
-
-	/**
-	 * This method sets the bone context for the given bone old memory address.
-	 * If the context is already set it will be replaced.
-	 * 
-	 * @param boneOMA
-	 *            the bone's old memory address
-	 * @param boneContext
-	 *            the bones's context
-	 */
-	public void setBoneContext(Long boneOMA, BoneContext boneContext) {
-		this.boneContexts.put(boneOMA, boneContext);
-	}
-
-	/**
-	 * This method returns the bone context for the given bone old memory
-	 * address. If no context exists then <b>null</b> is returned.
-	 * 
-	 * @param boneOMA
-	 *            the bone's old memory address
-	 * @return bone's context
-	 */
-	public BoneContext getBoneContext(Long boneOMA) {
-		return boneContexts.get(boneOMA);
-	}
-	
-	/**
-	 * Returns bone by given name.
-	 * 
-	 * @param name
-	 *            the name of the bone
-	 * @return found bone or null if none bone of a given name exists
-	 */
-	public BoneContext getBoneByName(String name) {
-		for(Entry<Long, BoneContext> entry : boneContexts.entrySet()) {
-			Bone bone = entry.getValue().getBone();
-			if(bone != null && name.equals(bone.getName())) {
-				return entry.getValue();
-			}
-		}
-		return null;
-	}
-	
-	/**
-	 * This metod returns the default material.
-	 * 
-	 * @return the default material
-	 */
-	public synchronized Material getDefaultMaterial() {
-		if (blenderKey.getDefaultMaterial() == null) {
-			Material defaultMaterial = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
-			defaultMaterial.setColor("Color", ColorRGBA.DarkGray);
-			blenderKey.setDefaultMaterial(defaultMaterial);
-		}
-		return blenderKey.getDefaultMaterial();
-	}
-
-	/**
-	 * Clears all sotred resources and closes the blender input stream.
-	 */
-	public void dispose() {
-		LOGGER.fine("Disposing blender context resources.");
-		inputStream.forceClose();
-		loadedFeatures.clear();
-		loadedFeaturesByName.clear();
-		parentStack.clear();
-		loadedIpos.clear();
-		modifiers.clear();
-		constraints.clear();
-		animData.clear();
-		skeletons.clear();
-		meshContexts.clear();
-		boneContexts.clear();
-		helpers.clear();
-	}
-
-	/**
-	 * This enum defines what loaded data type user wants to retreive. It can be
-	 * either filled structure or already converted data.
-	 * 
-	 * @author Marcin Roguski
-	 */
-	public static enum LoadedFeatureDataType {
-
-		LOADED_STRUCTURE(0), LOADED_FEATURE(1);
-		private int	index;
-
-		private LoadedFeatureDataType(int index) {
-			this.index = index;
-		}
-
-		public int getIndex() {
-			return index;
-		}
-	}
+    private static final Logger                 LOGGER                 = Logger.getLogger(BlenderContext.class.getName());
+
+    /** The blender file version. */
+    private int                                 blenderVersion;
+    /** The blender key. */
+    private BlenderKey                          blenderKey;
+    /** The header of the file block. */
+    private DnaBlockData                        dnaBlockData;
+    /** The scene structure. */
+    private Structure                           sceneStructure;
+    /** The input stream of the blend file. */
+    private BlenderInputStream                  inputStream;
+    /** The asset manager. */
+    private AssetManager                        assetManager;
+    /**
+     * A map containing the file block headers. The key is the old pointer
+     * address.
+     */
+    private Map<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 list of constraints for the specified object. */
+    protected Map<Long, List<Constraint>>       constraints            = new HashMap<Long, List<Constraint>>();
+    /** Anim data loaded for features. */
+    private Map<Long, AnimData>                 animData               = new HashMap<Long, AnimData>();
+    /** Loaded skeletons. */
+    private Map<Long, Skeleton>                 skeletons              = new HashMap<Long, Skeleton>();
+    /** A map of mesh contexts. */
+    protected Map<Long, MeshContext>            meshContexts           = new HashMap<Long, MeshContext>();
+    /** A map of bone contexts. */
+    protected Map<Long, BoneContext>            boneContexts           = new HashMap<Long, BoneContext>();
+    /** A map og helpers that perform loading. */
+    private Map<String, AbstractBlenderHelper>  helpers                = new HashMap<String, AbstractBlenderHelper>();
+
+    /**
+     * This method sets the blender file version.
+     * 
+     * @param blenderVersion
+     *            the blender file version
+     */
+    public void setBlenderVersion(String blenderVersion) {
+        this.blenderVersion = Integer.parseInt(blenderVersion);
+    }
+
+    /**
+     * @return the blender file version
+     */
+    public int getBlenderVersion() {
+        return blenderVersion;
+    }
+
+    /**
+     * This method sets the blender key.
+     * 
+     * @param blenderKey
+     *            the blender key
+     */
+    public void setBlenderKey(BlenderKey blenderKey) {
+        this.blenderKey = blenderKey;
+    }
+
+    /**
+     * This method returns the blender key.
+     * 
+     * @return the blender key
+     */
+    public BlenderKey getBlenderKey() {
+        return blenderKey;
+    }
+
+    /**
+     * This method sets the dna block data.
+     * 
+     * @param dnaBlockData
+     *            the dna block data
+     */
+    public void setBlockData(DnaBlockData dnaBlockData) {
+        this.dnaBlockData = dnaBlockData;
+    }
+
+    /**
+     * This method returns the dna block data.
+     * 
+     * @return the dna block data
+     */
+    public DnaBlockData getDnaBlockData() {
+        return dnaBlockData;
+    }
+
+    /**
+     * This method sets the scene structure data.
+     * 
+     * @param sceneStructure
+     *            the scene structure data
+     */
+    public void setSceneStructure(Structure sceneStructure) {
+        this.sceneStructure = sceneStructure;
+    }
+
+    /**
+     * This method returns the scene structure data.
+     * 
+     * @return the scene structure data
+     */
+    public Structure getSceneStructure() {
+        return sceneStructure;
+    }
+
+    /**
+     * This method returns the asset manager.
+     * 
+     * @return the asset manager
+     */
+    public AssetManager getAssetManager() {
+        return assetManager;
+    }
+
+    /**
+     * This method sets the asset manager.
+     * 
+     * @param assetManager
+     *            the asset manager
+     */
+    public void setAssetManager(AssetManager assetManager) {
+        this.assetManager = assetManager;
+    }
+
+    /**
+     * This method returns the input stream of the blend file.
+     * 
+     * @return the input stream of the blend file
+     */
+    public BlenderInputStream getInputStream() {
+        return inputStream;
+    }
+
+    /**
+     * This method sets the input stream of the blend file.
+     * 
+     * @param inputStream
+     *            the input stream of the blend file
+     */
+    public void setInputStream(BlenderInputStream inputStream) {
+        this.inputStream = inputStream;
+    }
+
+    /**
+     * This method adds a file block header to the map. Its old memory address
+     * is the key.
+     * 
+     * @param oldMemoryAddress
+     *            the address of the block header
+     * @param fileBlockHeader
+     *            the block header to store
+     */
+    public void addFileBlockHeader(Long oldMemoryAddress, FileBlockHeader fileBlockHeader) {
+        fileBlockHeadersByOma.put(oldMemoryAddress, fileBlockHeader);
+        List<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;
+        }
+    }
+
+    /**
+     * This method adds new ipo curve for the feature.
+     * 
+     * @param ownerOMA
+     *            the OMA of blender feature that owns the ipo
+     * @param ipo
+     *            the ipo to be added
+     */
+    public void addIpo(Long ownerOMA, Ipo ipo) {
+        loadedIpos.put(ownerOMA, ipo);
+    }
+
+    /**
+     * This method removes the ipo curve from the feature.
+     * 
+     * @param ownerOma
+     *            the OMA of blender feature that owns the ipo
+     */
+    public Ipo removeIpo(Long ownerOma) {
+        return loadedIpos.remove(ownerOma);
+    }
+
+    /**
+     * This method returns the ipo curve of the feature.
+     * 
+     * @param ownerOMA
+     *            the OMA of blender feature that owns the ipo
+     */
+    public Ipo getIpo(Long ownerOMA) {
+        return loadedIpos.get(ownerOMA);
+    }
+
+    /**
+     * This method adds a new modifier to the list.
+     * 
+     * @param ownerOMA
+     *            the owner's old memory address
+     * @param modifier
+     *            the object's modifier
+     */
+    public void addModifier(Long ownerOMA, Modifier modifier) {
+        List<Modifier> objectModifiers = this.modifiers.get(ownerOMA);
+        if (objectModifiers == null) {
+            objectModifiers = new ArrayList<Modifier>();
+            this.modifiers.put(ownerOMA, objectModifiers);
+        }
+        objectModifiers.add(modifier);
+    }
+
+    /**
+     * This method returns modifiers for the object specified by its old memory
+     * address and the modifier type. If no modifiers are found - empty list is
+     * returned. If the type is null - all modifiers for the object are
+     * returned.
+     * 
+     * @param objectOMA
+     *            object's old memory address
+     * @param type
+     *            the type of the modifier
+     * @return the list of object's modifiers
+     */
+    public List<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 method adds a new modifier to the list.
+     * 
+     * @param ownerOMA
+     *            the owner's old memory address
+     * @param constraints
+     *            the object's constraints
+     */
+    public void addConstraints(Long ownerOMA, List<Constraint> constraints) {
+        List<Constraint> objectConstraints = this.constraints.get(ownerOMA);
+        if (objectConstraints == null) {
+            objectConstraints = new ArrayList<Constraint>();
+            this.constraints.put(ownerOMA, objectConstraints);
+        }
+        objectConstraints.addAll(constraints);
+    }
+
+    /**
+     * This method returns constraints for the object specified by its old
+     * memory address. If no modifiers are found - <b>null</b> is returned.
+     * 
+     * @param objectOMA
+     *            object's old memory address
+     * @return the list of object's modifiers or null
+     */
+    public List<Constraint> getConstraints(Long objectOMA) {
+        return objectOMA == null ? null : constraints.get(objectOMA);
+    }
+
+    /**
+     * @return all available constraints
+     */
+    public List<Constraint> getAllConstraints() {
+        List<Constraint> result = new ArrayList<Constraint>();
+        for (Entry<Long, List<Constraint>> entry : constraints.entrySet()) {
+            result.addAll(entry.getValue());
+        }
+        return result;
+    }
+
+    /**
+     * This method sets the anim data for the specified OMA of its owner.
+     * 
+     * @param ownerOMA
+     *            the owner's old memory address
+     * @param animData
+     *            the animation data for the feature specified by ownerOMA
+     */
+    public void setAnimData(Long ownerOMA, AnimData animData) {
+        this.animData.put(ownerOMA, animData);
+    }
+
+    /**
+     * This method returns the animation data for the specified owner.
+     * 
+     * @param ownerOMA
+     *            the old memory address of the animation data owner
+     * @return the animation data or null if none exists
+     */
+    public AnimData getAnimData(Long ownerOMA) {
+        return this.animData.get(ownerOMA);
+    }
+
+    /**
+     * This method sets the skeleton for the specified OMA of its owner.
+     * 
+     * @param skeletonOMA
+     *            the skeleton's old memory address
+     * @param skeleton
+     *            the skeleton specified by the given OMA
+     */
+    public void setSkeleton(Long skeletonOMA, Skeleton skeleton) {
+        this.skeletons.put(skeletonOMA, skeleton);
+    }
+
+    /**
+     * This method returns the skeleton for the specified OMA of its owner.
+     * 
+     * @param skeletonOMA
+     *            the skeleton's old memory address
+     * @return the skeleton specified by the given OMA
+     */
+    public Skeleton getSkeleton(Long skeletonOMA) {
+        return this.skeletons.get(skeletonOMA);
+    }
+
+    /**
+     * This method sets the mesh context for the given mesh old memory address.
+     * If the context is already set it will be replaced.
+     * 
+     * @param meshOMA
+     *            the mesh's old memory address
+     * @param meshContext
+     *            the mesh's context
+     */
+    public void setMeshContext(Long meshOMA, MeshContext meshContext) {
+        this.meshContexts.put(meshOMA, meshContext);
+    }
+
+    /**
+     * This method returns the mesh context for the given mesh old memory
+     * address. If no context exists then <b>null</b> is returned.
+     * 
+     * @param meshOMA
+     *            the mesh's old memory address
+     * @return mesh's context
+     */
+    public MeshContext getMeshContext(Long meshOMA) {
+        return this.meshContexts.get(meshOMA);
+    }
+
+    /**
+     * This method sets the bone context for the given bone old memory address.
+     * If the context is already set it will be replaced.
+     * 
+     * @param boneOMA
+     *            the bone's old memory address
+     * @param boneContext
+     *            the bones's context
+     */
+    public void setBoneContext(Long boneOMA, BoneContext boneContext) {
+        this.boneContexts.put(boneOMA, boneContext);
+    }
+
+    /**
+     * This method returns the bone context for the given bone old memory
+     * address. If no context exists then <b>null</b> is returned.
+     * 
+     * @param boneOMA
+     *            the bone's old memory address
+     * @return bone's context
+     */
+    public BoneContext getBoneContext(Long boneOMA) {
+        return boneContexts.get(boneOMA);
+    }
+
+    /**
+     * Returns bone by given name.
+     * 
+     * @param name
+     *            the name of the bone
+     * @return found bone or null if none bone of a given name exists
+     */
+    public BoneContext getBoneByName(String name) {
+        for (Entry<Long, BoneContext> entry : boneContexts.entrySet()) {
+            Bone bone = entry.getValue().getBone();
+            if (bone != null && name.equals(bone.getName())) {
+                return entry.getValue();
+            }
+        }
+        return null;
+    }
+
+    /**
+     * This metod returns the default material.
+     * 
+     * @return the default material
+     */
+    public synchronized Material getDefaultMaterial() {
+        if (blenderKey.getDefaultMaterial() == null) {
+            Material defaultMaterial = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+            defaultMaterial.setColor("Color", ColorRGBA.DarkGray);
+            blenderKey.setDefaultMaterial(defaultMaterial);
+        }
+        return blenderKey.getDefaultMaterial();
+    }
+
+    /**
+     * Clears all sotred resources and closes the blender input stream.
+     */
+    public void dispose() {
+        LOGGER.fine("Disposing blender context resources.");
+        inputStream.forceClose();
+        loadedFeatures.clear();
+        loadedFeaturesByName.clear();
+        parentStack.clear();
+        loadedIpos.clear();
+        modifiers.clear();
+        constraints.clear();
+        animData.clear();
+        skeletons.clear();
+        meshContexts.clear();
+        boneContexts.clear();
+        helpers.clear();
+    }
+
+    /**
+     * This enum defines what loaded data type user wants to retreive. It can be
+     * either filled structure or already converted data.
+     * 
+     * @author Marcin Roguski
+     */
+    public static enum LoadedFeatureDataType {
+
+        LOADED_STRUCTURE(0), LOADED_FEATURE(1);
+        private int index;
+
+        private LoadedFeatureDataType(int index) {
+            this.index = index;
+        }
+
+        public int getIndex() {
+            return index;
+        }
+    }
 }

+ 153 - 153
engine/src/blender/com/jme3/scene/plugins/blender/BlenderLoader.java

@@ -70,158 +70,158 @@ import com.jme3.scene.plugins.blender.textures.TextureHelper;
  */
 public class BlenderLoader extends AbstractBlenderLoader {
 
-	private static final Logger		LOGGER	= Logger.getLogger(BlenderLoader.class.getName());
-
-	/** The blocks read from the file. */
-	protected List<FileBlockHeader>	blocks;
-
-	public Spatial load(AssetInfo assetInfo) throws IOException {
-		try {
-			this.setup(assetInfo);
-
-			List<FileBlockHeader> sceneBlocks = new ArrayList<FileBlockHeader>();
-			BlenderKey blenderKey = blenderContext.getBlenderKey();
-			LoadingResults loadingResults = blenderKey.prepareLoadingResults();
-			WorldData worldData = null;// a set of data used in different scene aspects
-			for (FileBlockHeader block : blocks) {
-				switch (block.getCode()) {
-					case FileBlockHeader.BLOCK_OB00:// Object
-						Object object = this.toObject(block.getStructure(blenderContext));
-						if(object instanceof LightNode && (blenderKey.getFeaturesToLoad() & FeaturesToLoad.LIGHTS) != 0) {
-							loadingResults.addLight((LightNode) object);
-						} else if (object instanceof CameraNode && (blenderKey.getFeaturesToLoad() & FeaturesToLoad.CAMERAS) != 0) {
-							loadingResults.addCamera((CameraNode) object);
-						} else if (object instanceof Node && (blenderKey.getFeaturesToLoad() & FeaturesToLoad.OBJECTS) != 0) {
-							LOGGER.log(Level.FINE, "{0}: {1}--> {2}", new Object[] { ((Node) object).getName(), ((Node) object).getLocalTranslation().toString(), ((Node) object).getParent() == null ? "null" : ((Node) object).getParent().getName() });
-							if (this.isRootObject(loadingResults, (Node)object)) {
-								loadingResults.addObject((Node) object);
-							}
-						}
-						break;
-//					case FileBlockHeader.BLOCK_MA00:// Material
-//						if (blenderKey.isLoadUnlinkedAssets() && (blenderKey.getFeaturesToLoad() & FeaturesToLoad.MATERIALS) != 0) {
-//							loadingResults.addMaterial(this.toMaterial(block.getStructure(blenderContext)));
-//						}
-//						break;
-					case FileBlockHeader.BLOCK_SC00:// Scene
-						if ((blenderKey.getFeaturesToLoad() & FeaturesToLoad.SCENES) != 0) {
-							sceneBlocks.add(block);
-						}
-						break;
-					case FileBlockHeader.BLOCK_WO00:// World
-						if (blenderKey.isLoadUnlinkedAssets() && worldData == null) {// onlu one world data is used
-							Structure worldStructure = block.getStructure(blenderContext);
-							String worldName = worldStructure.getName();
-							if (blenderKey.getUsedWorld() == null || blenderKey.getUsedWorld().equals(worldName)) {
-								worldData = this.toWorldData(worldStructure);
-								if ((blenderKey.getFeaturesToLoad() & FeaturesToLoad.LIGHTS) != 0) {
-									loadingResults.addLight(worldData.getAmbientLight());
-								}
-							}
-						}
-						break;
-				}
-			}
-			
-			//bake constraints after everything is loaded
-			ConstraintHelper constraintHelper = blenderContext.getHelper(ConstraintHelper.class);
-			constraintHelper.bakeConstraints(blenderContext);
-			
-			//load the scene at the very end so that the root nodes have no parent during loading or constraints applying
-			for(FileBlockHeader sceneBlock : sceneBlocks) {
-				loadingResults.addScene(this.toScene(sceneBlock.getStructure(blenderContext)));
-			}
-			
-			blenderContext.dispose();
-			return loadingResults;
-		} catch (BlenderFileException e) {
-			LOGGER.log(Level.SEVERE, e.getMessage(), e);
-		}
-		return null;
-	}
-
-	/**
-	 * This method indicates if the given spatial is a root object. It means it
-	 * has no parent or is directly attached to one of the already loaded scene
-	 * nodes.
-	 * 
-	 * @param loadingResults
-	 *        loading results containing the scene nodes
-	 * @param spatial
-	 *        spatial object
-	 * @return <b>true</b> if the given spatial is a root object and
-	 *         <b>false</b> otherwise
-	 */
-	protected boolean isRootObject(LoadingResults loadingResults, Spatial spatial) {
-		if(spatial.getParent() == null) {
-			return true;
-		}
-		for(Node scene : loadingResults.getScenes()) {
-			if(spatial.getParent().equals(scene)) {
-				return true;
-			}
-		}
-		return false;
-	}
-	
-	/**
-	 * This method sets up the loader.
-	 * @param assetInfo
-	 *        the asset info
-	 * @throws BlenderFileException
-	 *         an exception is throw when something wrong happens with blender file
-	 */
-	protected void setup(AssetInfo assetInfo) throws BlenderFileException {
-		// registering loaders
-		ModelKey modelKey = (ModelKey) assetInfo.getKey();
-		BlenderKey blenderKey;
-		if (modelKey instanceof BlenderKey) {
-			blenderKey = (BlenderKey) modelKey;
-		} else {
-			blenderKey = new BlenderKey(modelKey.getName());
-			blenderKey.setAssetRootPath(modelKey.getFolder());
-		}
-
-		// opening stream
-		BlenderInputStream inputStream = new BlenderInputStream(assetInfo.openStream());
-
-		// reading blocks
-		blocks = new ArrayList<FileBlockHeader>();
-		FileBlockHeader fileBlock;
-		blenderContext = new BlenderContext();
-		blenderContext.setBlenderVersion(inputStream.getVersionNumber());
-		blenderContext.setAssetManager(assetInfo.getManager());
-		blenderContext.setInputStream(inputStream);
-		blenderContext.setBlenderKey(blenderKey);
-
-		// creating helpers
-		blenderContext.putHelper(ArmatureHelper.class, new ArmatureHelper(inputStream.getVersionNumber(), blenderKey.isFixUpAxis()));
-		blenderContext.putHelper(TextureHelper.class, new TextureHelper(inputStream.getVersionNumber(), blenderKey.isFixUpAxis()));
-		blenderContext.putHelper(MeshHelper.class, new MeshHelper(inputStream.getVersionNumber(), blenderKey.isFixUpAxis()));
-		blenderContext.putHelper(ObjectHelper.class, new ObjectHelper(inputStream.getVersionNumber(), blenderKey.isFixUpAxis()));
-		blenderContext.putHelper(CurvesHelper.class, new CurvesHelper(inputStream.getVersionNumber(), blenderKey.isFixUpAxis()));
-		blenderContext.putHelper(LightHelper.class, new LightHelper(inputStream.getVersionNumber(), blenderKey.isFixUpAxis()));
-		blenderContext.putHelper(CameraHelper.class, new CameraHelper(inputStream.getVersionNumber(), blenderKey.isFixUpAxis()));
-		blenderContext.putHelper(ModifierHelper.class, new ModifierHelper(inputStream.getVersionNumber(), blenderKey.isFixUpAxis()));
-		blenderContext.putHelper(MaterialHelper.class, new MaterialHelper(inputStream.getVersionNumber(), blenderKey.isFixUpAxis()));
-		blenderContext.putHelper(ConstraintHelper.class, new ConstraintHelper(inputStream.getVersionNumber(), blenderContext, blenderKey.isFixUpAxis()));
-		blenderContext.putHelper(IpoHelper.class, new IpoHelper(inputStream.getVersionNumber(), blenderKey.isFixUpAxis()));
-		blenderContext.putHelper(ParticlesHelper.class, new ParticlesHelper(inputStream.getVersionNumber(), blenderKey.isFixUpAxis()));
-
-		// reading the blocks (dna block is automatically saved in the blender context when found)
-		FileBlockHeader sceneFileBlock = null;
-		do {
-			fileBlock = new FileBlockHeader(inputStream, blenderContext);
-			if (!fileBlock.isDnaBlock()) {
-				blocks.add(fileBlock);
-				// save the scene's file block
-				if (fileBlock.getCode() == FileBlockHeader.BLOCK_SC00) {
-					sceneFileBlock = fileBlock;
-				}
-			}
-		} while (!fileBlock.isLastBlock());
-		if (sceneFileBlock != null) {
-			blenderContext.setSceneStructure(sceneFileBlock.getStructure(blenderContext));
+    private static final Logger     LOGGER = Logger.getLogger(BlenderLoader.class.getName());
+
+    /** The blocks read from the file. */
+    protected List<FileBlockHeader> blocks;
+
+    public Spatial load(AssetInfo assetInfo) throws IOException {
+        try {
+            this.setup(assetInfo);
+
+            List<FileBlockHeader> sceneBlocks = new ArrayList<FileBlockHeader>();
+            BlenderKey blenderKey = blenderContext.getBlenderKey();
+            LoadingResults loadingResults = blenderKey.prepareLoadingResults();
+            WorldData worldData = null;// a set of data used in different scene aspects
+            for (FileBlockHeader block : blocks) {
+                switch (block.getCode()) {
+                    case FileBlockHeader.BLOCK_OB00:// Object
+                        Object object = this.toObject(block.getStructure(blenderContext));
+                        if (object instanceof LightNode && (blenderKey.getFeaturesToLoad() & FeaturesToLoad.LIGHTS) != 0) {
+                            loadingResults.addLight((LightNode) object);
+                        } else if (object instanceof CameraNode && (blenderKey.getFeaturesToLoad() & FeaturesToLoad.CAMERAS) != 0) {
+                            loadingResults.addCamera((CameraNode) object);
+                        } else if (object instanceof Node && (blenderKey.getFeaturesToLoad() & FeaturesToLoad.OBJECTS) != 0) {
+                            LOGGER.log(Level.FINE, "{0}: {1}--> {2}", new Object[] { ((Node) object).getName(), ((Node) object).getLocalTranslation().toString(), ((Node) object).getParent() == null ? "null" : ((Node) object).getParent().getName() });
+                            if (this.isRootObject(loadingResults, (Node) object)) {
+                                loadingResults.addObject((Node) object);
+                            }
+                        }
+                        break;
+                    // case FileBlockHeader.BLOCK_MA00:// Material
+                    // if (blenderKey.isLoadUnlinkedAssets() && (blenderKey.getFeaturesToLoad() & FeaturesToLoad.MATERIALS) != 0) {
+                    // loadingResults.addMaterial(this.toMaterial(block.getStructure(blenderContext)));
+                    // }
+                    // break;
+                    case FileBlockHeader.BLOCK_SC00:// Scene
+                        if ((blenderKey.getFeaturesToLoad() & FeaturesToLoad.SCENES) != 0) {
+                            sceneBlocks.add(block);
+                        }
+                        break;
+                    case FileBlockHeader.BLOCK_WO00:// World
+                        if (blenderKey.isLoadUnlinkedAssets() && worldData == null) {// onlu one world data is used
+                            Structure worldStructure = block.getStructure(blenderContext);
+                            String worldName = worldStructure.getName();
+                            if (blenderKey.getUsedWorld() == null || blenderKey.getUsedWorld().equals(worldName)) {
+                                worldData = this.toWorldData(worldStructure);
+                                if ((blenderKey.getFeaturesToLoad() & FeaturesToLoad.LIGHTS) != 0) {
+                                    loadingResults.addLight(worldData.getAmbientLight());
+                                }
+                            }
+                        }
+                        break;
+                }
+            }
+
+            // bake constraints after everything is loaded
+            ConstraintHelper constraintHelper = blenderContext.getHelper(ConstraintHelper.class);
+            constraintHelper.bakeConstraints(blenderContext);
+
+            // load the scene at the very end so that the root nodes have no parent during loading or constraints applying
+            for (FileBlockHeader sceneBlock : sceneBlocks) {
+                loadingResults.addScene(this.toScene(sceneBlock.getStructure(blenderContext)));
+            }
+
+            blenderContext.dispose();
+            return loadingResults;
+        } catch (BlenderFileException e) {
+            LOGGER.log(Level.SEVERE, e.getMessage(), e);
+        }
+        return null;
+    }
+
+    /**
+     * This method indicates if the given spatial is a root object. It means it
+     * has no parent or is directly attached to one of the already loaded scene
+     * nodes.
+     * 
+     * @param loadingResults
+     *            loading results containing the scene nodes
+     * @param spatial
+     *            spatial object
+     * @return <b>true</b> if the given spatial is a root object and
+     *         <b>false</b> otherwise
+     */
+    protected boolean isRootObject(LoadingResults loadingResults, Spatial spatial) {
+        if (spatial.getParent() == null) {
+            return true;
+        }
+        for (Node scene : loadingResults.getScenes()) {
+            if (spatial.getParent().equals(scene)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * This method sets up the loader.
+     * @param assetInfo
+     *            the asset info
+     * @throws BlenderFileException
+     *             an exception is throw when something wrong happens with blender file
+     */
+    protected void setup(AssetInfo assetInfo) throws BlenderFileException {
+        // registering loaders
+        ModelKey modelKey = (ModelKey) assetInfo.getKey();
+        BlenderKey blenderKey;
+        if (modelKey instanceof BlenderKey) {
+            blenderKey = (BlenderKey) modelKey;
+        } else {
+            blenderKey = new BlenderKey(modelKey.getName());
+            blenderKey.setAssetRootPath(modelKey.getFolder());
+        }
+
+        // opening stream
+        BlenderInputStream inputStream = new BlenderInputStream(assetInfo.openStream());
+
+        // reading blocks
+        blocks = new ArrayList<FileBlockHeader>();
+        FileBlockHeader fileBlock;
+        blenderContext = new BlenderContext();
+        blenderContext.setBlenderVersion(inputStream.getVersionNumber());
+        blenderContext.setAssetManager(assetInfo.getManager());
+        blenderContext.setInputStream(inputStream);
+        blenderContext.setBlenderKey(blenderKey);
+
+        // creating helpers
+        blenderContext.putHelper(ArmatureHelper.class, new ArmatureHelper(inputStream.getVersionNumber(), blenderKey.isFixUpAxis()));
+        blenderContext.putHelper(TextureHelper.class, new TextureHelper(inputStream.getVersionNumber(), blenderKey.isFixUpAxis()));
+        blenderContext.putHelper(MeshHelper.class, new MeshHelper(inputStream.getVersionNumber(), blenderKey.isFixUpAxis()));
+        blenderContext.putHelper(ObjectHelper.class, new ObjectHelper(inputStream.getVersionNumber(), blenderKey.isFixUpAxis()));
+        blenderContext.putHelper(CurvesHelper.class, new CurvesHelper(inputStream.getVersionNumber(), blenderKey.isFixUpAxis()));
+        blenderContext.putHelper(LightHelper.class, new LightHelper(inputStream.getVersionNumber(), blenderKey.isFixUpAxis()));
+        blenderContext.putHelper(CameraHelper.class, new CameraHelper(inputStream.getVersionNumber(), blenderKey.isFixUpAxis()));
+        blenderContext.putHelper(ModifierHelper.class, new ModifierHelper(inputStream.getVersionNumber(), blenderKey.isFixUpAxis()));
+        blenderContext.putHelper(MaterialHelper.class, new MaterialHelper(inputStream.getVersionNumber(), blenderKey.isFixUpAxis()));
+        blenderContext.putHelper(ConstraintHelper.class, new ConstraintHelper(inputStream.getVersionNumber(), blenderContext, blenderKey.isFixUpAxis()));
+        blenderContext.putHelper(IpoHelper.class, new IpoHelper(inputStream.getVersionNumber(), blenderKey.isFixUpAxis()));
+        blenderContext.putHelper(ParticlesHelper.class, new ParticlesHelper(inputStream.getVersionNumber(), blenderKey.isFixUpAxis()));
+
+        // reading the blocks (dna block is automatically saved in the blender context when found)
+        FileBlockHeader sceneFileBlock = null;
+        do {
+            fileBlock = new FileBlockHeader(inputStream, blenderContext);
+            if (!fileBlock.isDnaBlock()) {
+                blocks.add(fileBlock);
+                // save the scene's file block
+                if (fileBlock.getCode() == FileBlockHeader.BLOCK_SC00) {
+                    sceneFileBlock = fileBlock;
+                }
+            }
+        } while (!fileBlock.isLastBlock());
+        if (sceneFileBlock != null) {
+            blenderContext.setSceneStructure(sceneFileBlock.getStructure(blenderContext));
         }
-	}
+    }
 }

+ 16 - 16
engine/src/blender/com/jme3/scene/plugins/blender/BlenderModelLoader.java

@@ -57,30 +57,30 @@ public class BlenderModelLoader extends BlenderLoader {
     public Spatial load(AssetInfo assetInfo) throws IOException {
         try {
             this.setup(assetInfo);
-            
+
             BlenderKey blenderKey = blenderContext.getBlenderKey();
             Node modelRoot = new Node(blenderKey.getName());
-            
+
             for (FileBlockHeader block : blocks) {
                 if (block.getCode() == FileBlockHeader.BLOCK_OB00) {
                     Object object = this.toObject(block.getStructure(blenderContext));
-                    
-                    if(object instanceof LightNode && (blenderKey.getFeaturesToLoad() & FeaturesToLoad.LIGHTS) != 0) {
-                    	modelRoot.addLight(((LightNode)object).getLight());
-                    	modelRoot.attachChild((LightNode)object);
-					} else if (object instanceof Node && (blenderKey.getFeaturesToLoad() & FeaturesToLoad.OBJECTS) != 0) {
-						LOGGER.log(Level.FINE, "{0}: {1}--> {2}", new Object[] { ((Node) object).getName(), ((Node) object).getLocalTranslation().toString(), ((Node) object).getParent() == null ? "null" : ((Node) object).getParent().getName() });
-						if (((Node) object).getParent() == null) {
-                            modelRoot.attachChild((Node)object);
+
+                    if (object instanceof LightNode && (blenderKey.getFeaturesToLoad() & FeaturesToLoad.LIGHTS) != 0) {
+                        modelRoot.addLight(((LightNode) object).getLight());
+                        modelRoot.attachChild((LightNode) object);
+                    } else if (object instanceof Node && (blenderKey.getFeaturesToLoad() & FeaturesToLoad.OBJECTS) != 0) {
+                        LOGGER.log(Level.FINE, "{0}: {1}--> {2}", new Object[] { ((Node) object).getName(), ((Node) object).getLocalTranslation().toString(), ((Node) object).getParent() == null ? "null" : ((Node) object).getParent().getName() });
+                        if (((Node) object).getParent() == null) {
+                            modelRoot.attachChild((Node) object);
                         }
-					}
+                    }
                 }
             }
-            
-            //bake constraints after everything is loaded
-			ConstraintHelper constraintHelper = blenderContext.getHelper(ConstraintHelper.class);
-			constraintHelper.bakeConstraints(blenderContext);
-            
+
+            // bake constraints after everything is loaded
+            ConstraintHelper constraintHelper = blenderContext.getHelper(ConstraintHelper.class);
+            constraintHelper.bakeConstraints(blenderContext);
+
             blenderContext.dispose();
             return modelRoot;
         } catch (BlenderFileException e) {

+ 200 - 200
engine/src/blender/com/jme3/scene/plugins/blender/animations/ArmatureHelper.java

@@ -56,215 +56,215 @@ import java.util.logging.Logger;
  * @author Marcin Roguski (Kaelthas)
  */
 public class ArmatureHelper extends AbstractBlenderHelper {
-	private static final Logger	LOGGER		= Logger.getLogger(ArmatureHelper.class.getName());
+    private static final Logger LOGGER               = Logger.getLogger(ArmatureHelper.class.getName());
 
-	public static final String ARMETURE_NODE_MARKER = "armeture-node";
-	
-	/** A map of bones and their old memory addresses. */
-	private Map<Bone, Long>		bonesOMAs	= new HashMap<Bone, Long>();
+    public static final String  ARMETURE_NODE_MARKER = "armeture-node";
 
-	/**
-	 * This constructor parses the given blender version and stores the result.
-	 * Some functionalities may differ in different blender versions.
-	 * 
-	 * @param blenderVersion
-	 *            the version read from the blend file
-	 * @param fixUpAxis
-	 *            a variable that indicates if the Y asxis is the UP axis or not
-	 */
-	public ArmatureHelper(String blenderVersion, boolean fixUpAxis) {
-		super(blenderVersion, fixUpAxis);
-	}
+    /** A map of bones and their old memory addresses. */
+    private Map<Bone, Long>     bonesOMAs            = new HashMap<Bone, Long>();
 
-	/**
-	 * This method builds the object's bones structure.
-	 * 
-	 * @param boneStructure
-	 *            the structure containing the bones' data
-	 * @param parent
-	 *            the parent bone
-	 * @param result
-	 *            the list where the newly created bone will be added
-	 * @param bonesPoseChannels
-	 *            a map of bones poses channels
-	 * @param blenderContext
-	 *            the blender context
-	 * @throws BlenderFileException
-	 *             an exception is thrown when there is problem with the blender
-	 *             file
-	 */
-	public void buildBones(Long armatureObjectOMA, Structure boneStructure, Bone parent, List<Bone> result, Matrix4f arbt, final Map<Long, Structure> bonesPoseChannels, BlenderContext blenderContext) throws BlenderFileException {
-		BoneContext bc = new BoneContext(armatureObjectOMA, boneStructure, arbt, bonesPoseChannels, blenderContext);
-		bc.buildBone(result, bonesOMAs, blenderContext);
-	}
+    /**
+     * This constructor parses the given blender version and stores the result.
+     * Some functionalities may differ in different blender versions.
+     * 
+     * @param blenderVersion
+     *            the version read from the blend file
+     * @param fixUpAxis
+     *            a variable that indicates if the Y asxis is the UP axis or not
+     */
+    public ArmatureHelper(String blenderVersion, boolean fixUpAxis) {
+        super(blenderVersion, fixUpAxis);
+    }
 
-	/**
-	 * This method returns the old memory address of a bone. If the bone does
-	 * not exist in the blend file - zero is returned.
-	 * 
-	 * @param bone
-	 *            the bone whose old memory address we seek
-	 * @return the old memory address of the given bone
-	 */
-	public Long getBoneOMA(Bone bone) {
-		Long result = bonesOMAs.get(bone);
-		if (result == null) {
-			result = Long.valueOf(0);
-		}
-		return result;
-	}
+    /**
+     * This method builds the object's bones structure.
+     * 
+     * @param boneStructure
+     *            the structure containing the bones' data
+     * @param parent
+     *            the parent bone
+     * @param result
+     *            the list where the newly created bone will be added
+     * @param bonesPoseChannels
+     *            a map of bones poses channels
+     * @param blenderContext
+     *            the blender context
+     * @throws BlenderFileException
+     *             an exception is thrown when there is problem with the blender
+     *             file
+     */
+    public void buildBones(Long armatureObjectOMA, Structure boneStructure, Bone parent, List<Bone> result, Matrix4f arbt, final Map<Long, Structure> bonesPoseChannels, BlenderContext blenderContext) throws BlenderFileException {
+        BoneContext bc = new BoneContext(armatureObjectOMA, boneStructure, arbt, bonesPoseChannels, blenderContext);
+        bc.buildBone(result, bonesOMAs, blenderContext);
+    }
 
-	/**
-	 * This method returns a map where the key is the object's group index that
-	 * is used by a bone and the key is the bone index in the armature.
-	 * 
-	 * @param defBaseStructure
-	 *            a bPose structure of the object
-	 * @return bone group-to-index map
-	 * @throws BlenderFileException
-	 *             this exception is thrown when the blender file is somehow
-	 *             corrupted
-	 */
-	public Map<Integer, Integer> getGroupToBoneIndexMap(Structure defBaseStructure, Skeleton skeleton, BlenderContext blenderContext) throws BlenderFileException {
-		Map<Integer, Integer> result = null;
-		if (skeleton.getBoneCount() != 0) {
-			result = new HashMap<Integer, Integer>();
-			List<Structure> deformGroups = defBaseStructure.evaluateListBase(blenderContext);// bDeformGroup
-			int groupIndex = 0;
-			for (Structure deformGroup : deformGroups) {
-				String deformGroupName = deformGroup.getFieldValue("name").toString();
-				int boneIndex = this.getBoneIndex(skeleton, deformGroupName);
-				if (boneIndex >= 0) {
-					result.put(groupIndex, boneIndex);
-				}
-				++groupIndex;
-			}
-		}
-		return result;
-	}
+    /**
+     * This method returns the old memory address of a bone. If the bone does
+     * not exist in the blend file - zero is returned.
+     * 
+     * @param bone
+     *            the bone whose old memory address we seek
+     * @return the old memory address of the given bone
+     */
+    public Long getBoneOMA(Bone bone) {
+        Long result = bonesOMAs.get(bone);
+        if (result == null) {
+            result = Long.valueOf(0);
+        }
+        return result;
+    }
 
-	@Override
-	public boolean shouldBeLoaded(Structure structure, BlenderContext blenderContext) {
-		return true;
-	}
+    /**
+     * This method returns a map where the key is the object's group index that
+     * is used by a bone and the key is the bone index in the armature.
+     * 
+     * @param defBaseStructure
+     *            a bPose structure of the object
+     * @return bone group-to-index map
+     * @throws BlenderFileException
+     *             this exception is thrown when the blender file is somehow
+     *             corrupted
+     */
+    public Map<Integer, Integer> getGroupToBoneIndexMap(Structure defBaseStructure, Skeleton skeleton, BlenderContext blenderContext) throws BlenderFileException {
+        Map<Integer, Integer> result = null;
+        if (skeleton.getBoneCount() != 0) {
+            result = new HashMap<Integer, Integer>();
+            List<Structure> deformGroups = defBaseStructure.evaluateListBase(blenderContext);// bDeformGroup
+            int groupIndex = 0;
+            for (Structure deformGroup : deformGroups) {
+                String deformGroupName = deformGroup.getFieldValue("name").toString();
+                int boneIndex = this.getBoneIndex(skeleton, deformGroupName);
+                if (boneIndex >= 0) {
+                    result.put(groupIndex, boneIndex);
+                }
+                ++groupIndex;
+            }
+        }
+        return result;
+    }
 
-	/**
-	 * This method retuns the bone tracks for animation.
-	 * 
-	 * @param actionStructure
-	 *            the structure containing the tracks
-	 * @param blenderContext
-	 *            the blender context
-	 * @return a list of tracks for the specified animation
-	 * @throws BlenderFileException
-	 *             an exception is thrown when there are problems with the blend
-	 *             file
-	 */
-	public BoneTrack[] getTracks(Structure actionStructure, Skeleton skeleton, BlenderContext blenderContext) throws BlenderFileException {
-		if (blenderVersion < 250) {
-			return this.getTracks249(actionStructure, skeleton, blenderContext);
-		} else {
-			return this.getTracks250(actionStructure, skeleton, blenderContext);
-		}
-	}
+    @Override
+    public boolean shouldBeLoaded(Structure structure, BlenderContext blenderContext) {
+        return true;
+    }
 
-	/**
-	 * This method retuns the bone tracks for animation for blender version 2.50
-	 * and higher.
-	 * 
-	 * @param actionStructure
-	 *            the structure containing the tracks
-	 * @param blenderContext
-	 *            the blender context
-	 * @return a list of tracks for the specified animation
-	 * @throws BlenderFileException
-	 *             an exception is thrown when there are problems with the blend
-	 *             file
-	 */
-	private BoneTrack[] getTracks250(Structure actionStructure, Skeleton skeleton, BlenderContext blenderContext) throws BlenderFileException {
-		LOGGER.log(Level.FINE, "Getting tracks!");
-		IpoHelper ipoHelper = blenderContext.getHelper(IpoHelper.class);
-		int fps = blenderContext.getBlenderKey().getFps();
-		Structure groups = (Structure) actionStructure.getFieldValue("groups");
-		List<Structure> actionGroups = groups.evaluateListBase(blenderContext);// bActionGroup
-		List<BoneTrack> tracks = new ArrayList<BoneTrack>();
-		for (Structure actionGroup : actionGroups) {
-			String name = actionGroup.getFieldValue("name").toString();
-			int boneIndex = this.getBoneIndex(skeleton, name);
-			if (boneIndex >= 0) {
-				List<Structure> channels = ((Structure) actionGroup.getFieldValue("channels")).evaluateListBase(blenderContext);
-				BezierCurve[] bezierCurves = new BezierCurve[channels.size()];
-				int channelCounter = 0;
-				for (Structure c : channels) {
-					int type = ipoHelper.getCurveType(c, blenderContext);
-					Pointer pBezTriple = (Pointer) c.getFieldValue("bezt");
-					List<Structure> bezTriples = pBezTriple.fetchData(blenderContext.getInputStream());
-					bezierCurves[channelCounter++] = new BezierCurve(type, bezTriples, 2);
-				}
+    /**
+     * This method retuns the bone tracks for animation.
+     * 
+     * @param actionStructure
+     *            the structure containing the tracks
+     * @param blenderContext
+     *            the blender context
+     * @return a list of tracks for the specified animation
+     * @throws BlenderFileException
+     *             an exception is thrown when there are problems with the blend
+     *             file
+     */
+    public BoneTrack[] getTracks(Structure actionStructure, Skeleton skeleton, BlenderContext blenderContext) throws BlenderFileException {
+        if (blenderVersion < 250) {
+            return this.getTracks249(actionStructure, skeleton, blenderContext);
+        } else {
+            return this.getTracks250(actionStructure, skeleton, blenderContext);
+        }
+    }
 
-				Bone bone = skeleton.getBone(boneIndex);
-				Ipo ipo = new Ipo(bezierCurves, fixUpAxis, blenderContext.getBlenderVersion());
-				tracks.add((BoneTrack) ipo.calculateTrack(boneIndex, bone.getLocalRotation(), 0, ipo.getLastFrame(), fps, false));
-			}
-		}
-		return tracks.toArray(new BoneTrack[tracks.size()]);
-	}
+    /**
+     * This method retuns the bone tracks for animation for blender version 2.50
+     * and higher.
+     * 
+     * @param actionStructure
+     *            the structure containing the tracks
+     * @param blenderContext
+     *            the blender context
+     * @return a list of tracks for the specified animation
+     * @throws BlenderFileException
+     *             an exception is thrown when there are problems with the blend
+     *             file
+     */
+    private BoneTrack[] getTracks250(Structure actionStructure, Skeleton skeleton, BlenderContext blenderContext) throws BlenderFileException {
+        LOGGER.log(Level.FINE, "Getting tracks!");
+        IpoHelper ipoHelper = blenderContext.getHelper(IpoHelper.class);
+        int fps = blenderContext.getBlenderKey().getFps();
+        Structure groups = (Structure) actionStructure.getFieldValue("groups");
+        List<Structure> actionGroups = groups.evaluateListBase(blenderContext);// bActionGroup
+        List<BoneTrack> tracks = new ArrayList<BoneTrack>();
+        for (Structure actionGroup : actionGroups) {
+            String name = actionGroup.getFieldValue("name").toString();
+            int boneIndex = this.getBoneIndex(skeleton, name);
+            if (boneIndex >= 0) {
+                List<Structure> channels = ((Structure) actionGroup.getFieldValue("channels")).evaluateListBase(blenderContext);
+                BezierCurve[] bezierCurves = new BezierCurve[channels.size()];
+                int channelCounter = 0;
+                for (Structure c : channels) {
+                    int type = ipoHelper.getCurveType(c, blenderContext);
+                    Pointer pBezTriple = (Pointer) c.getFieldValue("bezt");
+                    List<Structure> bezTriples = pBezTriple.fetchData(blenderContext.getInputStream());
+                    bezierCurves[channelCounter++] = new BezierCurve(type, bezTriples, 2);
+                }
 
-	/**
-	 * This method retuns the bone tracks for animation for blender version 2.49
-	 * (and probably several lower versions too).
-	 * 
-	 * @param actionStructure
-	 *            the structure containing the tracks
-	 * @param blenderContext
-	 *            the blender context
-	 * @return a list of tracks for the specified animation
-	 * @throws BlenderFileException
-	 *             an exception is thrown when there are problems with the blend
-	 *             file
-	 */
-	private BoneTrack[] getTracks249(Structure actionStructure, Skeleton skeleton, BlenderContext blenderContext) throws BlenderFileException {
-		LOGGER.log(Level.FINE, "Getting tracks!");
-		IpoHelper ipoHelper = blenderContext.getHelper(IpoHelper.class);
-		int fps = blenderContext.getBlenderKey().getFps();
-		Structure chanbase = (Structure) actionStructure.getFieldValue("chanbase");
-		List<Structure> actionChannels = chanbase.evaluateListBase(blenderContext);// bActionChannel
-		List<BoneTrack> tracks = new ArrayList<BoneTrack>();
-		for (Structure bActionChannel : actionChannels) {
-			String name = bActionChannel.getFieldValue("name").toString();
-			int boneIndex = this.getBoneIndex(skeleton, name);
-			if (boneIndex >= 0) {
-				Pointer p = (Pointer) bActionChannel.getFieldValue("ipo");
-				if (!p.isNull()) {
-					Structure ipoStructure = p.fetchData(blenderContext.getInputStream()).get(0);
-					
-					Bone bone = skeleton.getBone(boneIndex);
-					Ipo ipo = ipoHelper.fromIpoStructure(ipoStructure, blenderContext);
-					if(ipo != null) {
-						tracks.add((BoneTrack) ipo.calculateTrack(boneIndex, bone.getLocalRotation(), 0, ipo.getLastFrame(), fps, false));
-					}
-				}
-			}
-		}
-		return tracks.toArray(new BoneTrack[tracks.size()]);
-	}
+                Bone bone = skeleton.getBone(boneIndex);
+                Ipo ipo = new Ipo(bezierCurves, fixUpAxis, blenderContext.getBlenderVersion());
+                tracks.add((BoneTrack) ipo.calculateTrack(boneIndex, bone.getLocalRotation(), 0, ipo.getLastFrame(), fps, false));
+            }
+        }
+        return tracks.toArray(new BoneTrack[tracks.size()]);
+    }
 
-	/**
-	 * This method returns the index of the bone in the given skeleton.
-	 * 
-	 * @param skeleton
-	 *            the skeleton
-	 * @param boneName
-	 *            the name of the bone
-	 * @return the index of the bone
-	 */
-	private int getBoneIndex(Skeleton skeleton, String boneName) {
-		int result = -1;
-		for (int i = 0; i < skeleton.getBoneCount() && result == -1; ++i) {
-			if (boneName.equals(skeleton.getBone(i).getName())) {
-				result = i;
-			}
-		}
-		return result;
-	}
+    /**
+     * This method retuns the bone tracks for animation for blender version 2.49
+     * (and probably several lower versions too).
+     * 
+     * @param actionStructure
+     *            the structure containing the tracks
+     * @param blenderContext
+     *            the blender context
+     * @return a list of tracks for the specified animation
+     * @throws BlenderFileException
+     *             an exception is thrown when there are problems with the blend
+     *             file
+     */
+    private BoneTrack[] getTracks249(Structure actionStructure, Skeleton skeleton, BlenderContext blenderContext) throws BlenderFileException {
+        LOGGER.log(Level.FINE, "Getting tracks!");
+        IpoHelper ipoHelper = blenderContext.getHelper(IpoHelper.class);
+        int fps = blenderContext.getBlenderKey().getFps();
+        Structure chanbase = (Structure) actionStructure.getFieldValue("chanbase");
+        List<Structure> actionChannels = chanbase.evaluateListBase(blenderContext);// bActionChannel
+        List<BoneTrack> tracks = new ArrayList<BoneTrack>();
+        for (Structure bActionChannel : actionChannels) {
+            String name = bActionChannel.getFieldValue("name").toString();
+            int boneIndex = this.getBoneIndex(skeleton, name);
+            if (boneIndex >= 0) {
+                Pointer p = (Pointer) bActionChannel.getFieldValue("ipo");
+                if (!p.isNull()) {
+                    Structure ipoStructure = p.fetchData(blenderContext.getInputStream()).get(0);
+
+                    Bone bone = skeleton.getBone(boneIndex);
+                    Ipo ipo = ipoHelper.fromIpoStructure(ipoStructure, blenderContext);
+                    if (ipo != null) {
+                        tracks.add((BoneTrack) ipo.calculateTrack(boneIndex, bone.getLocalRotation(), 0, ipo.getLastFrame(), fps, false));
+                    }
+                }
+            }
+        }
+        return tracks.toArray(new BoneTrack[tracks.size()]);
+    }
+
+    /**
+     * This method returns the index of the bone in the given skeleton.
+     * 
+     * @param skeleton
+     *            the skeleton
+     * @param boneName
+     *            the name of the bone
+     * @return the index of the bone
+     */
+    private int getBoneIndex(Skeleton skeleton, String boneName) {
+        int result = -1;
+        for (int i = 0; i < skeleton.getBoneCount() && result == -1; ++i) {
+            if (boneName.equals(skeleton.getBone(i).getName())) {
+                result = i;
+            }
+        }
+        return result;
+    }
 }

+ 211 - 211
engine/src/blender/com/jme3/scene/plugins/blender/animations/BoneContext.java

@@ -21,215 +21,215 @@ import com.jme3.scene.plugins.blender.objects.ObjectHelper;
  * @author Marcin Roguski (Kaelthas)
  */
 public class BoneContext {
-	/** The OMA of the bone's armature object. */
-	private Long 				armatureObjectOMA;
-	/** The structure of the bone. */
-	private Structure			boneStructure;
-	/** Bone's pose channel structure. */
-	private Structure			poseChannel;
-	/** Bone's name. */
-	private String				boneName;
-	/** This variable indicates if the Y axis should be the UP axis. */
-	private boolean				fixUpAxis;
-	/** The bone's armature matrix. */
-	private Matrix4f			armatureMatrix;
-	/** The parent context. */
-	private BoneContext			parent;
-	/** The children of this context. */
-	private List<BoneContext>	children		= new ArrayList<BoneContext>();
-	/** Created bone (available after calling 'buildBone' method). */
-	private Bone				bone;
-	/** Bone's pose transform (available after calling 'buildBone' method). */
-	private Transform			poseTransform	= new Transform();
-	/** The bone's rest matrix. */
-	private Matrix4f			restMatrix;
-	/** Bone's total inverse transformation. */
-	private Matrix4f			inverseTotalTransformation;
-	/** Bone's parent inverse matrix. */
-	private Matrix4f			inverseParentMatrix;
-	/** The length of the bone. */
-	private float				length;
-
-	/**
-	 * Constructor. Creates the basic set of bone's data.
-	 * 
-	 * @param armatureObjectOMA
-	 * 			  the OMA of the bone's armature object
-	 * @param boneStructure
-	 *            the bone's structure
-	 * @param objectToArmatureMatrix
-	 *            object-to-armature transformation matrix
-	 * @param bonesPoseChannels
-	 *            a map of pose channels for each bone OMA
-	 * @param blenderContext
-	 *            the blender context
-	 * @throws BlenderFileException
-	 *             an exception is thrown when problem with blender data reading
-	 *             occurs
-	 */
-	public BoneContext(Long armatureObjectOMA, Structure boneStructure, Matrix4f objectToArmatureMatrix, final Map<Long, Structure> bonesPoseChannels, BlenderContext blenderContext) throws BlenderFileException {
-		this(boneStructure, armatureObjectOMA, null, objectToArmatureMatrix, bonesPoseChannels, blenderContext);
-	}
-
-	/**
-	 * Constructor. Creates the basic set of bone's data.
-	 * 
-	 * @param boneStructure
-	 *            the bone's structure
-	 * @param armatureObjectOMA
-	 *            the OMA of the bone's armature object
-	 * @param parent
-	 *            bone's parent (null if the bone is the root bone)
-	 * @param objectToArmatureMatrix
-	 *            object-to-armature transformation matrix
-	 * @param bonesPoseChannels
-	 *            a map of pose channels for each bone OMA
-	 * @param blenderContext
-	 *            the blender context
-	 * @throws BlenderFileException
-	 *             an exception is thrown when problem with blender data reading
-	 *             occurs
-	 */
-	private BoneContext(Structure boneStructure, Long armatureObjectOMA, BoneContext parent, Matrix4f objectToArmatureMatrix, final Map<Long, Structure> bonesPoseChannels, BlenderContext blenderContext) throws BlenderFileException {
-		this.parent = parent;
-		this.boneStructure = boneStructure;
-		this.armatureObjectOMA = armatureObjectOMA;
-		boneName = boneStructure.getFieldValue("name").toString();
-		length = ((Number)boneStructure.getFieldValue("length")).floatValue();
-		ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class);
-		armatureMatrix = objectHelper.getMatrix(boneStructure, "arm_mat", true);
-
-		fixUpAxis = blenderContext.getBlenderKey().isFixUpAxis();
-		this.computeRestMatrix(objectToArmatureMatrix);
-		List<Structure> childbase = ((Structure) boneStructure.getFieldValue("childbase")).evaluateListBase(blenderContext);
-		for (Structure child : childbase) {
-			this.children.add(new BoneContext(child, armatureObjectOMA, this, objectToArmatureMatrix, bonesPoseChannels, blenderContext));
-		}
-
-		poseChannel = bonesPoseChannels.get(boneStructure.getOldMemoryAddress());
-
-		blenderContext.setBoneContext(boneStructure.getOldMemoryAddress(), this);
-	}
-
-	/**
-	 * This method computes the rest matrix for the bone.
-	 * 
-	 * @param objectToArmatureMatrix
-	 *            object-to-armature transformation matrix
-	 */
-	private void computeRestMatrix(Matrix4f objectToArmatureMatrix) {
-		if (parent != null) {
-			inverseParentMatrix = parent.inverseTotalTransformation.clone();
-		} else if (fixUpAxis) {
-			inverseParentMatrix = objectToArmatureMatrix.clone();
-		} else {
-			inverseParentMatrix = Matrix4f.IDENTITY.clone();
-		}
-
-		restMatrix = armatureMatrix.clone();
-		inverseTotalTransformation = restMatrix.invert();
-
-		restMatrix = inverseParentMatrix.mult(restMatrix);
-
-		for (BoneContext child : this.children) {
-			child.computeRestMatrix(objectToArmatureMatrix);
-		}
-	}
-
-	/**
-	 * This method computes the pose transform for the bone.
-	 */
-	@SuppressWarnings("unchecked")
-	private void computePoseTransform() {
-		DynamicArray<Number> loc = (DynamicArray<Number>) poseChannel.getFieldValue("loc");
-		DynamicArray<Number> size = (DynamicArray<Number>) poseChannel.getFieldValue("size");
-		DynamicArray<Number> quat = (DynamicArray<Number>) poseChannel.getFieldValue("quat");
-		if (fixUpAxis) {
-			poseTransform.setTranslation(loc.get(0).floatValue(), -loc.get(2).floatValue(), loc.get(1).floatValue());
-			poseTransform.setRotation(new Quaternion(quat.get(1).floatValue(), quat.get(3).floatValue(), -quat.get(2).floatValue(), quat.get(0).floatValue()));
-			poseTransform.setScale(size.get(0).floatValue(), size.get(2).floatValue(), size.get(1).floatValue());
-		} else {
-			poseTransform.setTranslation(loc.get(0).floatValue(), loc.get(1).floatValue(), loc.get(2).floatValue());
-			poseTransform.setRotation(new Quaternion(quat.get(0).floatValue(), quat.get(1).floatValue(), quat.get(2).floatValue(), quat.get(3).floatValue()));
-			poseTransform.setScale(size.get(0).floatValue(), size.get(1).floatValue(), size.get(2).floatValue());
-		}
-
-		Transform localTransform = new Transform(bone.getLocalPosition(), bone.getLocalRotation());
-		localTransform.setScale(bone.getLocalScale());
-		localTransform.getTranslation().addLocal(poseTransform.getTranslation());
-		localTransform.getRotation().multLocal(poseTransform.getRotation());
-		localTransform.getScale().multLocal(poseTransform.getScale());
-
-		poseTransform.set(localTransform);
-	}
-
-	/**
-	 * This method builds the bone. It recursively builds the bone's children.
-	 * 
-	 * @param bones
-	 *            a list of bones where the newly created bone will be added
-	 * @param boneOMAs
-	 *            the map between bone and its old memory address
-	 * @param blenderContext
-	 *            the blender context
-	 * @return newly created bone
-	 */
-	public Bone buildBone(List<Bone> bones, Map<Bone, Long> boneOMAs, BlenderContext blenderContext) {
-		Long boneOMA = boneStructure.getOldMemoryAddress();
-		bone = new Bone(boneName);
-		bones.add(bone);
-		boneOMAs.put(bone, boneOMA);
-		blenderContext.addLoadedFeatures(boneOMA, boneName, boneStructure, bone);
-
-		Matrix4f pose = this.restMatrix.clone();
-		ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class);
-
-		Vector3f poseLocation = pose.toTranslationVector();
-		Quaternion rotation = pose.toRotationQuat();
-		Vector3f scale = objectHelper.getScale(pose);
-
-		bone.setBindTransforms(poseLocation, rotation, scale);
-		for (BoneContext child : children) {
-			bone.addChild(child.buildBone(bones, boneOMAs, blenderContext));
-		}
-
-		this.computePoseTransform();
-
-		return bone;
-	}
-
-	/**
-	 * @return bone's pose transformation
-	 */
-	public Transform getPoseTransform() {
-		return poseTransform;
-	}
-
-	/**
-	 * @return built bone (available after calling 'buildBone' method)
-	 */
-	public Bone getBone() {
-		return bone;
-	}
-	
-	/**
-	 * @return the old memory address of the bone
-	 */
-	public Long getBoneOma() {
-		return boneStructure.getOldMemoryAddress();	
-	}
-	
-	/**
-	 * @return the length of the bone
-	 */
-	public float getLength() {
-		return length;
-	}
-	
-	/**
-	 * @return OMA of the bone's armature object
-	 */
-	public Long getArmatureObjectOMA() {
-		return armatureObjectOMA;
-	}
+    /** The OMA of the bone's armature object. */
+    private Long              armatureObjectOMA;
+    /** The structure of the bone. */
+    private Structure         boneStructure;
+    /** Bone's pose channel structure. */
+    private Structure         poseChannel;
+    /** Bone's name. */
+    private String            boneName;
+    /** This variable indicates if the Y axis should be the UP axis. */
+    private boolean           fixUpAxis;
+    /** The bone's armature matrix. */
+    private Matrix4f          armatureMatrix;
+    /** The parent context. */
+    private BoneContext       parent;
+    /** The children of this context. */
+    private List<BoneContext> children      = new ArrayList<BoneContext>();
+    /** Created bone (available after calling 'buildBone' method). */
+    private Bone              bone;
+    /** Bone's pose transform (available after calling 'buildBone' method). */
+    private Transform         poseTransform = new Transform();
+    /** The bone's rest matrix. */
+    private Matrix4f          restMatrix;
+    /** Bone's total inverse transformation. */
+    private Matrix4f          inverseTotalTransformation;
+    /** Bone's parent inverse matrix. */
+    private Matrix4f          inverseParentMatrix;
+    /** The length of the bone. */
+    private float             length;
+
+    /**
+     * Constructor. Creates the basic set of bone's data.
+     * 
+     * @param armatureObjectOMA
+     *            the OMA of the bone's armature object
+     * @param boneStructure
+     *            the bone's structure
+     * @param objectToArmatureMatrix
+     *            object-to-armature transformation matrix
+     * @param bonesPoseChannels
+     *            a map of pose channels for each bone OMA
+     * @param blenderContext
+     *            the blender context
+     * @throws BlenderFileException
+     *             an exception is thrown when problem with blender data reading
+     *             occurs
+     */
+    public BoneContext(Long armatureObjectOMA, Structure boneStructure, Matrix4f objectToArmatureMatrix, final Map<Long, Structure> bonesPoseChannels, BlenderContext blenderContext) throws BlenderFileException {
+        this(boneStructure, armatureObjectOMA, null, objectToArmatureMatrix, bonesPoseChannels, blenderContext);
+    }
+
+    /**
+     * Constructor. Creates the basic set of bone's data.
+     * 
+     * @param boneStructure
+     *            the bone's structure
+     * @param armatureObjectOMA
+     *            the OMA of the bone's armature object
+     * @param parent
+     *            bone's parent (null if the bone is the root bone)
+     * @param objectToArmatureMatrix
+     *            object-to-armature transformation matrix
+     * @param bonesPoseChannels
+     *            a map of pose channels for each bone OMA
+     * @param blenderContext
+     *            the blender context
+     * @throws BlenderFileException
+     *             an exception is thrown when problem with blender data reading
+     *             occurs
+     */
+    private BoneContext(Structure boneStructure, Long armatureObjectOMA, BoneContext parent, Matrix4f objectToArmatureMatrix, final Map<Long, Structure> bonesPoseChannels, BlenderContext blenderContext) throws BlenderFileException {
+        this.parent = parent;
+        this.boneStructure = boneStructure;
+        this.armatureObjectOMA = armatureObjectOMA;
+        boneName = boneStructure.getFieldValue("name").toString();
+        length = ((Number) boneStructure.getFieldValue("length")).floatValue();
+        ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class);
+        armatureMatrix = objectHelper.getMatrix(boneStructure, "arm_mat", true);
+
+        fixUpAxis = blenderContext.getBlenderKey().isFixUpAxis();
+        this.computeRestMatrix(objectToArmatureMatrix);
+        List<Structure> childbase = ((Structure) boneStructure.getFieldValue("childbase")).evaluateListBase(blenderContext);
+        for (Structure child : childbase) {
+            this.children.add(new BoneContext(child, armatureObjectOMA, this, objectToArmatureMatrix, bonesPoseChannels, blenderContext));
+        }
+
+        poseChannel = bonesPoseChannels.get(boneStructure.getOldMemoryAddress());
+
+        blenderContext.setBoneContext(boneStructure.getOldMemoryAddress(), this);
+    }
+
+    /**
+     * This method computes the rest matrix for the bone.
+     * 
+     * @param objectToArmatureMatrix
+     *            object-to-armature transformation matrix
+     */
+    private void computeRestMatrix(Matrix4f objectToArmatureMatrix) {
+        if (parent != null) {
+            inverseParentMatrix = parent.inverseTotalTransformation.clone();
+        } else if (fixUpAxis) {
+            inverseParentMatrix = objectToArmatureMatrix.clone();
+        } else {
+            inverseParentMatrix = Matrix4f.IDENTITY.clone();
+        }
+
+        restMatrix = armatureMatrix.clone();
+        inverseTotalTransformation = restMatrix.invert();
+
+        restMatrix = inverseParentMatrix.mult(restMatrix);
+
+        for (BoneContext child : this.children) {
+            child.computeRestMatrix(objectToArmatureMatrix);
+        }
+    }
+
+    /**
+     * This method computes the pose transform for the bone.
+     */
+    @SuppressWarnings("unchecked")
+    private void computePoseTransform() {
+        DynamicArray<Number> loc = (DynamicArray<Number>) poseChannel.getFieldValue("loc");
+        DynamicArray<Number> size = (DynamicArray<Number>) poseChannel.getFieldValue("size");
+        DynamicArray<Number> quat = (DynamicArray<Number>) poseChannel.getFieldValue("quat");
+        if (fixUpAxis) {
+            poseTransform.setTranslation(loc.get(0).floatValue(), -loc.get(2).floatValue(), loc.get(1).floatValue());
+            poseTransform.setRotation(new Quaternion(quat.get(1).floatValue(), quat.get(3).floatValue(), -quat.get(2).floatValue(), quat.get(0).floatValue()));
+            poseTransform.setScale(size.get(0).floatValue(), size.get(2).floatValue(), size.get(1).floatValue());
+        } else {
+            poseTransform.setTranslation(loc.get(0).floatValue(), loc.get(1).floatValue(), loc.get(2).floatValue());
+            poseTransform.setRotation(new Quaternion(quat.get(0).floatValue(), quat.get(1).floatValue(), quat.get(2).floatValue(), quat.get(3).floatValue()));
+            poseTransform.setScale(size.get(0).floatValue(), size.get(1).floatValue(), size.get(2).floatValue());
+        }
+
+        Transform localTransform = new Transform(bone.getLocalPosition(), bone.getLocalRotation());
+        localTransform.setScale(bone.getLocalScale());
+        localTransform.getTranslation().addLocal(poseTransform.getTranslation());
+        localTransform.getRotation().multLocal(poseTransform.getRotation());
+        localTransform.getScale().multLocal(poseTransform.getScale());
+
+        poseTransform.set(localTransform);
+    }
+
+    /**
+     * This method builds the bone. It recursively builds the bone's children.
+     * 
+     * @param bones
+     *            a list of bones where the newly created bone will be added
+     * @param boneOMAs
+     *            the map between bone and its old memory address
+     * @param blenderContext
+     *            the blender context
+     * @return newly created bone
+     */
+    public Bone buildBone(List<Bone> bones, Map<Bone, Long> boneOMAs, BlenderContext blenderContext) {
+        Long boneOMA = boneStructure.getOldMemoryAddress();
+        bone = new Bone(boneName);
+        bones.add(bone);
+        boneOMAs.put(bone, boneOMA);
+        blenderContext.addLoadedFeatures(boneOMA, boneName, boneStructure, bone);
+
+        Matrix4f pose = this.restMatrix.clone();
+        ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class);
+
+        Vector3f poseLocation = pose.toTranslationVector();
+        Quaternion rotation = pose.toRotationQuat();
+        Vector3f scale = objectHelper.getScale(pose);
+
+        bone.setBindTransforms(poseLocation, rotation, scale);
+        for (BoneContext child : children) {
+            bone.addChild(child.buildBone(bones, boneOMAs, blenderContext));
+        }
+
+        this.computePoseTransform();
+
+        return bone;
+    }
+
+    /**
+     * @return bone's pose transformation
+     */
+    public Transform getPoseTransform() {
+        return poseTransform;
+    }
+
+    /**
+     * @return built bone (available after calling 'buildBone' method)
+     */
+    public Bone getBone() {
+        return bone;
+    }
+
+    /**
+     * @return the old memory address of the bone
+     */
+    public Long getBoneOma() {
+        return boneStructure.getOldMemoryAddress();
+    }
+
+    /**
+     * @return the length of the bone
+     */
+    public float getLength() {
+        return length;
+    }
+
+    /**
+     * @return OMA of the bone's armature object
+     */
+    public Long getArmatureObjectOMA() {
+        return armatureObjectOMA;
+    }
 }

+ 105 - 105
engine/src/blender/com/jme3/scene/plugins/blender/animations/CalculationBone.java

@@ -13,116 +13,116 @@ import java.util.Arrays;
  * @author Marcin Roguski (Kaelthas)
  */
 public class CalculationBone extends Node {
-	private Bone bone;
-	/** The bone's tracks. Will be altered at the end of calculation process. */
-	private BoneTrack track;
-	/** The starting position of the bone. */
-	private Vector3f startTranslation;
-	/** The starting rotation of the bone. */
-	private Quaternion startRotation;
-	/** The starting scale of the bone. */
-	private Vector3f startScale;
-	private Vector3f[] translations;
-	private Quaternion[] rotations;
-	private Vector3f[] scales;
+    private Bone         bone;
+    /** The bone's tracks. Will be altered at the end of calculation process. */
+    private BoneTrack    track;
+    /** The starting position of the bone. */
+    private Vector3f     startTranslation;
+    /** The starting rotation of the bone. */
+    private Quaternion   startRotation;
+    /** The starting scale of the bone. */
+    private Vector3f     startScale;
+    private Vector3f[]   translations;
+    private Quaternion[] rotations;
+    private Vector3f[]   scales;
 
-	public CalculationBone(Bone bone, int boneFramesCount) {
-		this.bone = bone;
-		this.startRotation = bone.getModelSpaceRotation().clone();
-		this.startTranslation = bone.getModelSpacePosition().clone();
-		this.startScale = bone.getModelSpaceScale().clone();
-		this.reset();
-		if(boneFramesCount > 0) {
-			this.translations = new Vector3f[boneFramesCount];
-			this.rotations = new Quaternion[boneFramesCount];
-			this.scales = new Vector3f[boneFramesCount];
-			
-			Arrays.fill(this.translations, 0, boneFramesCount, this.startTranslation);
-			Arrays.fill(this.rotations, 0, boneFramesCount, this.startRotation);
-			Arrays.fill(this.scales, 0, boneFramesCount, this.startScale);
-		}
-	}
-	
-	/**
-	 * Constructor. Stores the track, starting transformation and sets the transformation to the starting positions.
-	 * @param bone
-	 *        the bone this class will imitate
-	 * @param track
-	 *        the bone's tracks
-	 */
-	public CalculationBone(Bone bone, BoneTrack track) {
-		this(bone, 0);
-		this.track = track;
-		this.translations = track.getTranslations();
-		this.rotations = track.getRotations();
-		this.scales = track.getScales();
-	}
+    public CalculationBone(Bone bone, int boneFramesCount) {
+        this.bone = bone;
+        this.startRotation = bone.getModelSpaceRotation().clone();
+        this.startTranslation = bone.getModelSpacePosition().clone();
+        this.startScale = bone.getModelSpaceScale().clone();
+        this.reset();
+        if (boneFramesCount > 0) {
+            this.translations = new Vector3f[boneFramesCount];
+            this.rotations = new Quaternion[boneFramesCount];
+            this.scales = new Vector3f[boneFramesCount];
 
-	public int getBoneFramesCount() {
-		return this.translations==null ? 0 : this.translations.length;
-	}
-	
-	/**
-	 * This method returns the end point of the bone. If the bone has parent it is calculated from the start point
-	 * of parent to the start point of this bone. If the bone doesn't have a parent the end location is considered
-	 * to be 1 point up along Y axis (scale is applied if set to != 1.0);
-	 * @return the end point of this bone
-	 */
-	//TODO: set to Z axis if user defined it this way
-	public Vector3f getEndPoint() {
-		if (this.getParent() == null) {
-			return new Vector3f(0, this.getLocalScale().y, 0);
-		} else {
-			Node parent = this.getParent();
-			return parent.getWorldTranslation().subtract(this.getWorldTranslation()).multLocal(this.getWorldScale());
-		}
-	}
+            Arrays.fill(this.translations, 0, boneFramesCount, this.startTranslation);
+            Arrays.fill(this.rotations, 0, boneFramesCount, this.startRotation);
+            Arrays.fill(this.scales, 0, boneFramesCount, this.startScale);
+        }
+    }
 
-	/**
-	 * This method resets the calculation bone to the starting position.
-	 */
-	public void reset() {
-		this.setLocalTranslation(startTranslation);
-		this.setLocalRotation(startRotation);
-		this.setLocalScale(startScale);
-	}
+    /**
+     * Constructor. Stores the track, starting transformation and sets the transformation to the starting positions.
+     * @param bone
+     *            the bone this class will imitate
+     * @param track
+     *            the bone's tracks
+     */
+    public CalculationBone(Bone bone, BoneTrack track) {
+        this(bone, 0);
+        this.track = track;
+        this.translations = track.getTranslations();
+        this.rotations = track.getRotations();
+        this.scales = track.getScales();
+    }
 
-	@Override
-	public int attachChild(Spatial child) {
-		if (this.getChildren() != null && this.getChildren().size() > 1) {
-			throw new IllegalStateException(this.getClass().getName() + " class instance can only have one child!");
-		}
-		return super.attachChild(child);
-	}
+    public int getBoneFramesCount() {
+        return this.translations == null ? 0 : this.translations.length;
+    }
 
-	public Spatial rotate(Quaternion rot, int frame) {
-		Spatial spatial = super.rotate(rot);
-		this.updateWorldTransforms();
-		if (this.getChildren() != null && this.getChildren().size() > 0) {
-			CalculationBone child = (CalculationBone) this.getChild(0);
-			child.updateWorldTransforms();
-		}
-		rotations[frame].set(this.getLocalRotation());
-		translations[frame].set(this.getLocalTranslation());
-		if (scales != null) {
-			scales[frame].set(this.getLocalScale());
-		}
-		return spatial;
-	}
+    /**
+     * This method returns the end point of the bone. If the bone has parent it is calculated from the start point
+     * of parent to the start point of this bone. If the bone doesn't have a parent the end location is considered
+     * to be 1 point up along Y axis (scale is applied if set to != 1.0);
+     * @return the end point of this bone
+     */
+    // TODO: set to Z axis if user defined it this way
+    public Vector3f getEndPoint() {
+        if (this.getParent() == null) {
+            return new Vector3f(0, this.getLocalScale().y, 0);
+        } else {
+            Node parent = this.getParent();
+            return parent.getWorldTranslation().subtract(this.getWorldTranslation()).multLocal(this.getWorldScale());
+        }
+    }
 
-	public void applyCalculatedTracks() {
-		if(track != null) {
-			track.setKeyframes(track.getTimes(), translations, rotations, scales);
-		} else {
-			bone.setUserControl(true);
-			bone.setUserTransforms(translations[0], rotations[0], scales[0]);
-			bone.setUserControl(false);
-			bone.updateWorldVectors();
-		}
-	}
+    /**
+     * This method resets the calculation bone to the starting position.
+     */
+    public void reset() {
+        this.setLocalTranslation(startTranslation);
+        this.setLocalRotation(startRotation);
+        this.setLocalScale(startScale);
+    }
 
-	@Override
-	public String toString() {
-		return bone.getName() + ": " + this.getLocalRotation() + " " + this.getLocalTranslation();
-	}
+    @Override
+    public int attachChild(Spatial child) {
+        if (this.getChildren() != null && this.getChildren().size() > 1) {
+            throw new IllegalStateException(this.getClass().getName() + " class instance can only have one child!");
+        }
+        return super.attachChild(child);
+    }
+
+    public Spatial rotate(Quaternion rot, int frame) {
+        Spatial spatial = super.rotate(rot);
+        this.updateWorldTransforms();
+        if (this.getChildren() != null && this.getChildren().size() > 0) {
+            CalculationBone child = (CalculationBone) this.getChild(0);
+            child.updateWorldTransforms();
+        }
+        rotations[frame].set(this.getLocalRotation());
+        translations[frame].set(this.getLocalTranslation());
+        if (scales != null) {
+            scales[frame].set(this.getLocalScale());
+        }
+        return spatial;
+    }
+
+    public void applyCalculatedTracks() {
+        if (track != null) {
+            track.setKeyframes(track.getTimes(), translations, rotations, scales);
+        } else {
+            bone.setUserControl(true);
+            bone.setUserTransforms(translations[0], rotations[0], scales[0]);
+            bone.setUserControl(false);
+            bone.updateWorldVectors();
+        }
+    }
+
+    @Override
+    public String toString() {
+        return bone.getName() + ": " + this.getLocalRotation() + " " + this.getLocalTranslation();
+    }
 }

+ 230 - 230
engine/src/blender/com/jme3/scene/plugins/blender/animations/Ipo.java

@@ -18,234 +18,234 @@ import com.jme3.scene.plugins.blender.curves.BezierCurve;
  * @author Marcin Roguski
  */
 public class Ipo {
-	private static final Logger LOGGER = Logger.getLogger(Ipo.class.getName());
-
-	public static final int	AC_LOC_X	= 1;
-	public static final int	AC_LOC_Y	= 2;
-	public static final int	AC_LOC_Z	= 3;
-	public static final int	OB_ROT_X	= 7;
-	public static final int	OB_ROT_Y	= 8;
-	public static final int	OB_ROT_Z	= 9;
-	public static final int	AC_SIZE_X	= 13;
-	public static final int	AC_SIZE_Y	= 14;
-	public static final int	AC_SIZE_Z	= 15;
-	public static final int	AC_QUAT_W	= 25;
-	public static final int	AC_QUAT_X	= 26;
-	public static final int	AC_QUAT_Y	= 27;
-	public static final int	AC_QUAT_Z	= 28;
-
-	/** A list of bezier curves for this interpolation object. */
-	private BezierCurve[]	bezierCurves;
-	/** Each ipo contains one bone track. */
-	private Track			calculatedTrack;
-	/** This variable indicates if the Y asxis is the UP axis or not. */
-	protected boolean		fixUpAxis;
-	/** Depending on the blender version rotations are stored in degrees or radians so we need to know the version that is used. */
-	protected final int		blenderVersion;
-
-	/**
-	 * Constructor. Stores the bezier curves.
-	 * 
-	 * @param bezierCurves
-	 *            a table of bezier curves
-	 * @param fixUpAxis
-	 * 			  indicates if the Y is the up axis or not
-	 * @param blenderVersion
-	 * 			  the blender version that is currently used
-	 */
-	public Ipo(BezierCurve[] bezierCurves, boolean fixUpAxis, int blenderVersion) {
-		this.bezierCurves = bezierCurves;
-		this.fixUpAxis = fixUpAxis;
-		this.blenderVersion = blenderVersion;
-	}
-
-	/**
-	 * This method calculates the ipo value for the first curve.
-	 * 
-	 * @param frame
-	 *            the frame for which the value is calculated
-	 * @return calculated ipo value
-	 */
-	public float calculateValue(int frame) {
-		return this.calculateValue(frame, 0);
-	}
-
-	/**
-	 * This method calculates the ipo value for the curve of the specified
-	 * index. Make sure you do not exceed the curves amount. Alway chech the
-	 * amount of curves before calling this method.
-	 * 
-	 * @param frame
-	 *            the frame for which the value is calculated
-	 * @param curveIndex
-	 *            the index of the curve
-	 * @return calculated ipo value
-	 */
-	public float calculateValue(int frame, int curveIndex) {
-		return bezierCurves[curveIndex].evaluate(frame, BezierCurve.Y_VALUE);
-	}
-
-	/**
-	 * This method returns the curves amount.
-	 * 
-	 * @return the curves amount
-	 */
-	public int getCurvesAmount() {
-		return bezierCurves.length;
-	}
-
-	/**
-	 * This method returns the frame where last bezier triple center point of
-	 * the specified bezier curve is located.
-	 * 
-	 * @return the frame number of the last defined bezier triple point for the
-	 *         specified ipo
-	 */
-	public int getLastFrame() {
-		int result = 1;
-		for (int i = 0; i < bezierCurves.length; ++i) {
-			int tempResult = bezierCurves[i].getLastFrame();
-			if (tempResult > result) {
-				result = tempResult;
-			}
-		}
-		return result;
-	}
-
-	/**
-	 * This method calculates the value of the curves as a bone track between
-	 * the specified frames.
-	 * 
-	 * @param targetIndex
-	 *            the index of the target for which the method calculates the
-	 *            tracks IMPORTANT! Aet to -1 (or any negative number) if you
-	 *            want to load spatial animation.
-	 * @param localQuaternionRotation
-	 *            the local rotation of the object/bone that will be animated by
-	 *            the track
-	 * @param startFrame
-	 *            the firs frame of tracks (inclusive)
-	 * @param stopFrame
-	 *            the last frame of the tracks (inclusive)
-	 * @param fps
-	 *            frame rate (frames per second)
-	 * @param spatialTrack
-	 *            this flag indicates if the track belongs to a spatial or to a
-	 *            bone; the diference is important because it appears that bones
-	 *            in blender have the same type of coordinate system (Y as UP)
-	 *            as jme while other features have different one (Z is UP)
-	 * @return bone track for the specified bone
-	 */
-	public Track calculateTrack(int targetIndex, Quaternion localQuaternionRotation, int startFrame, int stopFrame, int fps, boolean spatialTrack) {
-		if (calculatedTrack == null) {
-			// preparing data for track
-			int framesAmount = stopFrame - startFrame;
-			float timeBetweenFrames = 1.0f / fps;
-
-			float[] times = new float[framesAmount + 1];
-			Vector3f[] translations = new Vector3f[framesAmount + 1];
-			float[] translation = new float[3];
-			Quaternion[] rotations = new Quaternion[framesAmount + 1];
-			float[] quaternionRotation = new float[] { 0, 0, 0, 1 };
-			float[] objectRotation = new float[3];
-			Vector3f[] scales = new Vector3f[framesAmount + 1];
-			float[] scale = new float[] { 1.0f, 1.0f, 1.0f };
-			float degreeToRadiansFactor = 1;
-			if(blenderVersion < 250) {//in blender earlier than 2.50 the values are stored in degrees
-				degreeToRadiansFactor *= FastMath.DEG_TO_RAD * 10;// the values in blender are divided by 10, so we need to mult it here
-			}
-			
-			// calculating track data
-			for (int frame = startFrame; frame <= stopFrame; ++frame) {
-				int index = frame - startFrame;
-				times[index] = index * timeBetweenFrames;//start + (frame - 1) * timeBetweenFrames;
-				for (int j = 0; j < bezierCurves.length; ++j) {
-					double value = bezierCurves[j].evaluate(frame, BezierCurve.Y_VALUE);
-					switch (bezierCurves[j].getType()) {
-						// LOCATION
-						case AC_LOC_X:
-							translation[0] = (float) value;
-							break;
-						case AC_LOC_Y:
-							if (fixUpAxis) {
-								translation[2] = (float) -value;
-							} else {
-								translation[1] = (float) value;
-							}
-							break;
-						case AC_LOC_Z:
-							translation[fixUpAxis ? 1 : 2] = (float) value;
-							break;
-
-						// ROTATION (used with object animation)
-						// the value here is in degrees divided by 10 (so in
-						// example: 9 = PI/2)
-						case OB_ROT_X:
-							objectRotation[0] = (float) value * degreeToRadiansFactor;
-							break;
-						case OB_ROT_Y:
-							if (fixUpAxis) {
-								objectRotation[2] = (float) -value * degreeToRadiansFactor;
-							} else {
-								objectRotation[1] = (float) value * degreeToRadiansFactor;
-							}
-							break;
-						case OB_ROT_Z:
-							objectRotation[fixUpAxis ? 1 : 2] = (float) value * degreeToRadiansFactor;
-							break;
-
-						// SIZE
-						case AC_SIZE_X:
-							scale[0] = (float) value;
-							break;
-						case AC_SIZE_Y:
-							if (fixUpAxis) {
-								scale[2] = (float) value;
-							} else {
-								scale[1] = (float) value;
-							}
-							break;
-						case AC_SIZE_Z:
-							scale[fixUpAxis ? 1 : 2] = (float) value;
-							break;
-
-						// QUATERNION ROTATION (used with bone animation), dunno
-						// why but here we shouldn't check the
-						// spatialTrack flag value
-						case AC_QUAT_W:
-							quaternionRotation[3] = (float) value;
-							break;
-						case AC_QUAT_X:
-							quaternionRotation[0] = (float) value;
-							break;
-						case AC_QUAT_Y:
-							if (fixUpAxis) {
-								quaternionRotation[2] = -(float) value;
-							} else {
-								quaternionRotation[1] = (float) value;
-							}
-							break;
-						case AC_QUAT_Z:
-							if (fixUpAxis) {
-								quaternionRotation[1] = (float) value;
-							} else {
-								quaternionRotation[2] = (float) value;
-							}
-							break;
-						default:
-							LOGGER.warning("Unknown ipo curve type: " + bezierCurves[j].getType());
-					}
-				}
-				translations[index] = localQuaternionRotation.multLocal(new Vector3f(translation[0], translation[1], translation[2]));
-				rotations[index] = spatialTrack ? new Quaternion().fromAngles(objectRotation) : new Quaternion(quaternionRotation[0], quaternionRotation[1], quaternionRotation[2], quaternionRotation[3]);
-				scales[index] = new Vector3f(scale[0], scale[1], scale[2]);
-			}
-			if (spatialTrack) {
-				calculatedTrack = new SpatialTrack(times, translations, rotations, scales);
-			} else {
-				calculatedTrack = new BoneTrack(targetIndex, times, translations, rotations, scales);
-			}
-		}
-		return calculatedTrack;
-	}
+    private static final Logger LOGGER    = Logger.getLogger(Ipo.class.getName());
+
+    public static final int     AC_LOC_X  = 1;
+    public static final int     AC_LOC_Y  = 2;
+    public static final int     AC_LOC_Z  = 3;
+    public static final int     OB_ROT_X  = 7;
+    public static final int     OB_ROT_Y  = 8;
+    public static final int     OB_ROT_Z  = 9;
+    public static final int     AC_SIZE_X = 13;
+    public static final int     AC_SIZE_Y = 14;
+    public static final int     AC_SIZE_Z = 15;
+    public static final int     AC_QUAT_W = 25;
+    public static final int     AC_QUAT_X = 26;
+    public static final int     AC_QUAT_Y = 27;
+    public static final int     AC_QUAT_Z = 28;
+
+    /** A list of bezier curves for this interpolation object. */
+    private BezierCurve[]       bezierCurves;
+    /** Each ipo contains one bone track. */
+    private Track               calculatedTrack;
+    /** This variable indicates if the Y asxis is the UP axis or not. */
+    protected boolean           fixUpAxis;
+    /** Depending on the blender version rotations are stored in degrees or radians so we need to know the version that is used. */
+    protected final int         blenderVersion;
+
+    /**
+     * Constructor. Stores the bezier curves.
+     * 
+     * @param bezierCurves
+     *            a table of bezier curves
+     * @param fixUpAxis
+     *            indicates if the Y is the up axis or not
+     * @param blenderVersion
+     *            the blender version that is currently used
+     */
+    public Ipo(BezierCurve[] bezierCurves, boolean fixUpAxis, int blenderVersion) {
+        this.bezierCurves = bezierCurves;
+        this.fixUpAxis = fixUpAxis;
+        this.blenderVersion = blenderVersion;
+    }
+
+    /**
+     * This method calculates the ipo value for the first curve.
+     * 
+     * @param frame
+     *            the frame for which the value is calculated
+     * @return calculated ipo value
+     */
+    public float calculateValue(int frame) {
+        return this.calculateValue(frame, 0);
+    }
+
+    /**
+     * This method calculates the ipo value for the curve of the specified
+     * index. Make sure you do not exceed the curves amount. Alway chech the
+     * amount of curves before calling this method.
+     * 
+     * @param frame
+     *            the frame for which the value is calculated
+     * @param curveIndex
+     *            the index of the curve
+     * @return calculated ipo value
+     */
+    public float calculateValue(int frame, int curveIndex) {
+        return bezierCurves[curveIndex].evaluate(frame, BezierCurve.Y_VALUE);
+    }
+
+    /**
+     * This method returns the curves amount.
+     * 
+     * @return the curves amount
+     */
+    public int getCurvesAmount() {
+        return bezierCurves.length;
+    }
+
+    /**
+     * This method returns the frame where last bezier triple center point of
+     * the specified bezier curve is located.
+     * 
+     * @return the frame number of the last defined bezier triple point for the
+     *         specified ipo
+     */
+    public int getLastFrame() {
+        int result = 1;
+        for (int i = 0; i < bezierCurves.length; ++i) {
+            int tempResult = bezierCurves[i].getLastFrame();
+            if (tempResult > result) {
+                result = tempResult;
+            }
+        }
+        return result;
+    }
+
+    /**
+     * This method calculates the value of the curves as a bone track between
+     * the specified frames.
+     * 
+     * @param targetIndex
+     *            the index of the target for which the method calculates the
+     *            tracks IMPORTANT! Aet to -1 (or any negative number) if you
+     *            want to load spatial animation.
+     * @param localQuaternionRotation
+     *            the local rotation of the object/bone that will be animated by
+     *            the track
+     * @param startFrame
+     *            the firs frame of tracks (inclusive)
+     * @param stopFrame
+     *            the last frame of the tracks (inclusive)
+     * @param fps
+     *            frame rate (frames per second)
+     * @param spatialTrack
+     *            this flag indicates if the track belongs to a spatial or to a
+     *            bone; the diference is important because it appears that bones
+     *            in blender have the same type of coordinate system (Y as UP)
+     *            as jme while other features have different one (Z is UP)
+     * @return bone track for the specified bone
+     */
+    public Track calculateTrack(int targetIndex, Quaternion localQuaternionRotation, int startFrame, int stopFrame, int fps, boolean spatialTrack) {
+        if (calculatedTrack == null) {
+            // preparing data for track
+            int framesAmount = stopFrame - startFrame;
+            float timeBetweenFrames = 1.0f / fps;
+
+            float[] times = new float[framesAmount + 1];
+            Vector3f[] translations = new Vector3f[framesAmount + 1];
+            float[] translation = new float[3];
+            Quaternion[] rotations = new Quaternion[framesAmount + 1];
+            float[] quaternionRotation = new float[] { 0, 0, 0, 1 };
+            float[] objectRotation = new float[3];
+            Vector3f[] scales = new Vector3f[framesAmount + 1];
+            float[] scale = new float[] { 1.0f, 1.0f, 1.0f };
+            float degreeToRadiansFactor = 1;
+            if (blenderVersion < 250) {// in blender earlier than 2.50 the values are stored in degrees
+                degreeToRadiansFactor *= FastMath.DEG_TO_RAD * 10;// the values in blender are divided by 10, so we need to mult it here
+            }
+
+            // calculating track data
+            for (int frame = startFrame; frame <= stopFrame; ++frame) {
+                int index = frame - startFrame;
+                times[index] = index * timeBetweenFrames;// start + (frame - 1) * timeBetweenFrames;
+                for (int j = 0; j < bezierCurves.length; ++j) {
+                    double value = bezierCurves[j].evaluate(frame, BezierCurve.Y_VALUE);
+                    switch (bezierCurves[j].getType()) {
+                    // LOCATION
+                        case AC_LOC_X:
+                            translation[0] = (float) value;
+                            break;
+                        case AC_LOC_Y:
+                            if (fixUpAxis) {
+                                translation[2] = (float) -value;
+                            } else {
+                                translation[1] = (float) value;
+                            }
+                            break;
+                        case AC_LOC_Z:
+                            translation[fixUpAxis ? 1 : 2] = (float) value;
+                            break;
+
+                        // ROTATION (used with object animation)
+                        // the value here is in degrees divided by 10 (so in
+                        // example: 9 = PI/2)
+                        case OB_ROT_X:
+                            objectRotation[0] = (float) value * degreeToRadiansFactor;
+                            break;
+                        case OB_ROT_Y:
+                            if (fixUpAxis) {
+                                objectRotation[2] = (float) -value * degreeToRadiansFactor;
+                            } else {
+                                objectRotation[1] = (float) value * degreeToRadiansFactor;
+                            }
+                            break;
+                        case OB_ROT_Z:
+                            objectRotation[fixUpAxis ? 1 : 2] = (float) value * degreeToRadiansFactor;
+                            break;
+
+                        // SIZE
+                        case AC_SIZE_X:
+                            scale[0] = (float) value;
+                            break;
+                        case AC_SIZE_Y:
+                            if (fixUpAxis) {
+                                scale[2] = (float) value;
+                            } else {
+                                scale[1] = (float) value;
+                            }
+                            break;
+                        case AC_SIZE_Z:
+                            scale[fixUpAxis ? 1 : 2] = (float) value;
+                            break;
+
+                        // QUATERNION ROTATION (used with bone animation), dunno
+                        // why but here we shouldn't check the
+                        // spatialTrack flag value
+                        case AC_QUAT_W:
+                            quaternionRotation[3] = (float) value;
+                            break;
+                        case AC_QUAT_X:
+                            quaternionRotation[0] = (float) value;
+                            break;
+                        case AC_QUAT_Y:
+                            if (fixUpAxis) {
+                                quaternionRotation[2] = -(float) value;
+                            } else {
+                                quaternionRotation[1] = (float) value;
+                            }
+                            break;
+                        case AC_QUAT_Z:
+                            if (fixUpAxis) {
+                                quaternionRotation[1] = (float) value;
+                            } else {
+                                quaternionRotation[2] = (float) value;
+                            }
+                            break;
+                        default:
+                            LOGGER.warning("Unknown ipo curve type: " + bezierCurves[j].getType());
+                    }
+                }
+                translations[index] = localQuaternionRotation.multLocal(new Vector3f(translation[0], translation[1], translation[2]));
+                rotations[index] = spatialTrack ? new Quaternion().fromAngles(objectRotation) : new Quaternion(quaternionRotation[0], quaternionRotation[1], quaternionRotation[2], quaternionRotation[3]);
+                scales[index] = new Vector3f(scale[0], scale[1], scale[2]);
+            }
+            if (spatialTrack) {
+                calculatedTrack = new SpatialTrack(times, translations, rotations, scales);
+            } else {
+                calculatedTrack = new BoneTrack(targetIndex, times, translations, rotations, scales);
+            }
+        }
+        return calculatedTrack;
+    }
 }

+ 178 - 178
engine/src/blender/com/jme3/scene/plugins/blender/animations/IpoHelper.java

@@ -21,182 +21,182 @@ import java.util.logging.Logger;
  * @author Marcin Roguski
  */
 public class IpoHelper extends AbstractBlenderHelper {
-	private static final Logger LOGGER = Logger.getLogger(IpoHelper.class.getName());
-
-	/**
-	 * This constructor parses the given blender version and stores the result.
-	 * Some functionalities may differ in different blender versions.
-	 * 
-	 * @param blenderVersion
-	 *            the version read from the blend file
-	 * @param fixUpAxis
-	 *            a variable that indicates if the Y asxis is the UP axis or not
-	 */
-	public IpoHelper(String blenderVersion, boolean fixUpAxis) {
-		super(blenderVersion, fixUpAxis);
-	}
-
-	/**
-	 * This method creates an ipo object used for interpolation calculations.
-	 * 
-	 * @param ipoStructure
-	 *            the structure with ipo definition
-	 * @param blenderContext
-	 *            the blender context
-	 * @return the ipo object
-	 * @throws BlenderFileException
-	 *             this exception is thrown when the blender file is somehow
-	 *             corrupted
-	 */
-	public Ipo fromIpoStructure(Structure ipoStructure, BlenderContext blenderContext) throws BlenderFileException {
-		Structure curvebase = (Structure) ipoStructure.getFieldValue("curve");
-
-		// preparing bezier curves
-		Ipo result = null;
-		List<Structure> curves = curvebase.evaluateListBase(blenderContext);// IpoCurve
-		if (curves.size() > 0) {
-			BezierCurve[] bezierCurves = new BezierCurve[curves.size()];
-			int frame = 0;
-			for (Structure curve : curves) {
-				Pointer pBezTriple = (Pointer) curve.getFieldValue("bezt");
-				List<Structure> bezTriples = pBezTriple.fetchData(blenderContext.getInputStream());
-				int type = ((Number) curve.getFieldValue("adrcode")).intValue();
-				bezierCurves[frame++] = new BezierCurve(type, bezTriples, 2);
-			}
-			curves.clear();
-			result = new Ipo(bezierCurves, fixUpAxis, blenderContext.getBlenderVersion());
-			blenderContext.addLoadedFeatures(ipoStructure.getOldMemoryAddress(), ipoStructure.getName(), ipoStructure, result);
-		}
-		return result;
-	}
-
-	/**
-	 * This method creates an ipo object used for interpolation calculations. It
-	 * should be called for blender version 2.50 and higher.
-	 * 
-	 * @param actionStructure
-	 *            the structure with action definition
-	 * @param blenderContext
-	 *            the blender context
-	 * @return the ipo object
-	 * @throws BlenderFileException
-	 *             this exception is thrown when the blender file is somehow
-	 *             corrupted
-	 */
-	public Ipo fromAction(Structure actionStructure, BlenderContext blenderContext) throws BlenderFileException {
-		Ipo result = null;
-		List<Structure> curves = ((Structure) actionStructure.getFieldValue("curves")).evaluateListBase(blenderContext);// FCurve
-		if (curves.size() > 0) {
-			BezierCurve[] bezierCurves = new BezierCurve[curves.size()];
-			int frame = 0;
-			for (Structure curve : curves) {
-				Pointer pBezTriple = (Pointer) curve.getFieldValue("bezt");
-				List<Structure> bezTriples = pBezTriple.fetchData(blenderContext.getInputStream());
-				int type = this.getCurveType(curve, blenderContext);
-				bezierCurves[frame++] = new BezierCurve(type, bezTriples, 2);
-			}
-			curves.clear();
-			result = new Ipo(bezierCurves, fixUpAxis, blenderContext.getBlenderVersion());
-		}
-		return result;
-	}
-
-	/**
-	 * This method returns the type of the ipo curve.
-	 * 
-	 * @param structure
-	 *            the structure must contain the 'rna_path' field and
-	 *            'array_index' field (the type is not important here)
-	 * @param blenderContext
-	 *            the blender context
-	 * @return the type of the curve
-	 */
-	public int getCurveType(Structure structure, BlenderContext blenderContext) {
-		// reading rna path first
-		BlenderInputStream bis = blenderContext.getInputStream();
-		int currentPosition = bis.getPosition();
-		Pointer pRnaPath = (Pointer) structure.getFieldValue("rna_path");
-		FileBlockHeader dataFileBlock = blenderContext.getFileBlock(pRnaPath.getOldMemoryAddress());
-		bis.setPosition(dataFileBlock.getBlockPosition());
-		String rnaPath = bis.readString();
-		bis.setPosition(currentPosition);
-		int arrayIndex = ((Number) structure.getFieldValue("array_index")).intValue();
-
-		// determining the curve type
-		if (rnaPath.endsWith("location")) {
-			return Ipo.AC_LOC_X + arrayIndex;
-		}
-		if (rnaPath.endsWith("rotation_quaternion")) {
-			return Ipo.AC_QUAT_W + arrayIndex;
-		}
-		if (rnaPath.endsWith("scale")) {
-			return Ipo.AC_SIZE_X + arrayIndex;
-		}
-		if (rnaPath.endsWith("rotation") || rnaPath.endsWith("rotation_euler")) {
-			return Ipo.OB_ROT_X + arrayIndex;
-		}
-		LOGGER.warning("Unknown curve rna path: " + rnaPath);
-		return -1;
-	}
-
-	/**
-	 * This method creates an ipo with only a single value. No track type is
-	 * specified so do not use it for calculating tracks.
-	 * 
-	 * @param constValue
-	 *            the value of this ipo
-	 * @return constant ipo
-	 */
-	public Ipo fromValue(float constValue) {
-		return new ConstIpo(constValue);
-	}
-
-	@Override
-	public boolean shouldBeLoaded(Structure structure, BlenderContext blenderContext) {
-		return true;
-	}
-
-	/**
-	 * Ipo constant curve. This is a curve with only one value and no specified
-	 * type. This type of ipo cannot be used to calculate tracks. It should only
-	 * be used to calculate single value for a given frame.
-	 * 
-	 * @author Marcin Roguski
-	 */
-	private class ConstIpo extends Ipo {
-
-		/** The constant value of this ipo. */
-		private float	constValue;
-
-		/**
-		 * Constructor. Stores the constant value of this ipo.
-		 * 
-		 * @param constValue
-		 *            the constant value of this ipo
-		 */
-		public ConstIpo(float constValue) {
-			super(null, false, 0);//the version is not important here
-			this.constValue = constValue;
-		}
-
-		@Override
-		public float calculateValue(int frame) {
-			return constValue;
-		}
-
-		@Override
-		public float calculateValue(int frame, int curveIndex) {
-			return constValue;
-		}
-
-		@Override
-		public int getCurvesAmount() {
-			return 0;
-		}
-
-		@Override
-		public BoneTrack calculateTrack(int boneIndex, Quaternion localQuaternionRotation, int startFrame, int stopFrame, int fps, boolean boneTrack) {
-			throw new IllegalStateException("Constatnt ipo object cannot be used for calculating bone tracks!");
-		}
-	}
+    private static final Logger LOGGER = Logger.getLogger(IpoHelper.class.getName());
+
+    /**
+     * This constructor parses the given blender version and stores the result.
+     * Some functionalities may differ in different blender versions.
+     * 
+     * @param blenderVersion
+     *            the version read from the blend file
+     * @param fixUpAxis
+     *            a variable that indicates if the Y asxis is the UP axis or not
+     */
+    public IpoHelper(String blenderVersion, boolean fixUpAxis) {
+        super(blenderVersion, fixUpAxis);
+    }
+
+    /**
+     * This method creates an ipo object used for interpolation calculations.
+     * 
+     * @param ipoStructure
+     *            the structure with ipo definition
+     * @param blenderContext
+     *            the blender context
+     * @return the ipo object
+     * @throws BlenderFileException
+     *             this exception is thrown when the blender file is somehow
+     *             corrupted
+     */
+    public Ipo fromIpoStructure(Structure ipoStructure, BlenderContext blenderContext) throws BlenderFileException {
+        Structure curvebase = (Structure) ipoStructure.getFieldValue("curve");
+
+        // preparing bezier curves
+        Ipo result = null;
+        List<Structure> curves = curvebase.evaluateListBase(blenderContext);// IpoCurve
+        if (curves.size() > 0) {
+            BezierCurve[] bezierCurves = new BezierCurve[curves.size()];
+            int frame = 0;
+            for (Structure curve : curves) {
+                Pointer pBezTriple = (Pointer) curve.getFieldValue("bezt");
+                List<Structure> bezTriples = pBezTriple.fetchData(blenderContext.getInputStream());
+                int type = ((Number) curve.getFieldValue("adrcode")).intValue();
+                bezierCurves[frame++] = new BezierCurve(type, bezTriples, 2);
+            }
+            curves.clear();
+            result = new Ipo(bezierCurves, fixUpAxis, blenderContext.getBlenderVersion());
+            blenderContext.addLoadedFeatures(ipoStructure.getOldMemoryAddress(), ipoStructure.getName(), ipoStructure, result);
+        }
+        return result;
+    }
+
+    /**
+     * This method creates an ipo object used for interpolation calculations. It
+     * should be called for blender version 2.50 and higher.
+     * 
+     * @param actionStructure
+     *            the structure with action definition
+     * @param blenderContext
+     *            the blender context
+     * @return the ipo object
+     * @throws BlenderFileException
+     *             this exception is thrown when the blender file is somehow
+     *             corrupted
+     */
+    public Ipo fromAction(Structure actionStructure, BlenderContext blenderContext) throws BlenderFileException {
+        Ipo result = null;
+        List<Structure> curves = ((Structure) actionStructure.getFieldValue("curves")).evaluateListBase(blenderContext);// FCurve
+        if (curves.size() > 0) {
+            BezierCurve[] bezierCurves = new BezierCurve[curves.size()];
+            int frame = 0;
+            for (Structure curve : curves) {
+                Pointer pBezTriple = (Pointer) curve.getFieldValue("bezt");
+                List<Structure> bezTriples = pBezTriple.fetchData(blenderContext.getInputStream());
+                int type = this.getCurveType(curve, blenderContext);
+                bezierCurves[frame++] = new BezierCurve(type, bezTriples, 2);
+            }
+            curves.clear();
+            result = new Ipo(bezierCurves, fixUpAxis, blenderContext.getBlenderVersion());
+        }
+        return result;
+    }
+
+    /**
+     * This method returns the type of the ipo curve.
+     * 
+     * @param structure
+     *            the structure must contain the 'rna_path' field and
+     *            'array_index' field (the type is not important here)
+     * @param blenderContext
+     *            the blender context
+     * @return the type of the curve
+     */
+    public int getCurveType(Structure structure, BlenderContext blenderContext) {
+        // reading rna path first
+        BlenderInputStream bis = blenderContext.getInputStream();
+        int currentPosition = bis.getPosition();
+        Pointer pRnaPath = (Pointer) structure.getFieldValue("rna_path");
+        FileBlockHeader dataFileBlock = blenderContext.getFileBlock(pRnaPath.getOldMemoryAddress());
+        bis.setPosition(dataFileBlock.getBlockPosition());
+        String rnaPath = bis.readString();
+        bis.setPosition(currentPosition);
+        int arrayIndex = ((Number) structure.getFieldValue("array_index")).intValue();
+
+        // determining the curve type
+        if (rnaPath.endsWith("location")) {
+            return Ipo.AC_LOC_X + arrayIndex;
+        }
+        if (rnaPath.endsWith("rotation_quaternion")) {
+            return Ipo.AC_QUAT_W + arrayIndex;
+        }
+        if (rnaPath.endsWith("scale")) {
+            return Ipo.AC_SIZE_X + arrayIndex;
+        }
+        if (rnaPath.endsWith("rotation") || rnaPath.endsWith("rotation_euler")) {
+            return Ipo.OB_ROT_X + arrayIndex;
+        }
+        LOGGER.warning("Unknown curve rna path: " + rnaPath);
+        return -1;
+    }
+
+    /**
+     * This method creates an ipo with only a single value. No track type is
+     * specified so do not use it for calculating tracks.
+     * 
+     * @param constValue
+     *            the value of this ipo
+     * @return constant ipo
+     */
+    public Ipo fromValue(float constValue) {
+        return new ConstIpo(constValue);
+    }
+
+    @Override
+    public boolean shouldBeLoaded(Structure structure, BlenderContext blenderContext) {
+        return true;
+    }
+
+    /**
+     * Ipo constant curve. This is a curve with only one value and no specified
+     * type. This type of ipo cannot be used to calculate tracks. It should only
+     * be used to calculate single value for a given frame.
+     * 
+     * @author Marcin Roguski
+     */
+    private class ConstIpo extends Ipo {
+
+        /** The constant value of this ipo. */
+        private float constValue;
+
+        /**
+         * Constructor. Stores the constant value of this ipo.
+         * 
+         * @param constValue
+         *            the constant value of this ipo
+         */
+        public ConstIpo(float constValue) {
+            super(null, false, 0);// the version is not important here
+            this.constValue = constValue;
+        }
+
+        @Override
+        public float calculateValue(int frame) {
+            return constValue;
+        }
+
+        @Override
+        public float calculateValue(int frame, int curveIndex) {
+            return constValue;
+        }
+
+        @Override
+        public int getCurvesAmount() {
+            return 0;
+        }
+
+        @Override
+        public BoneTrack calculateTrack(int boneIndex, Quaternion localQuaternionRotation, int startFrame, int stopFrame, int fps, boolean boneTrack) {
+            throw new IllegalStateException("Constatnt ipo object cannot be used for calculating bone tracks!");
+        }
+    }
 }

+ 52 - 52
engine/src/blender/com/jme3/scene/plugins/blender/cameras/CameraHelper.java

@@ -17,59 +17,59 @@ import java.util.logging.Logger;
  */
 public class CameraHelper extends AbstractBlenderHelper {
 
-    private static final Logger LOGGER = Logger.getLogger(CameraHelper.class.getName());
-    protected static final int DEFAULT_CAM_WIDTH = 640;
-    protected static final int DEFAULT_CAM_HEIGHT = 480;
-    
+    private static final Logger LOGGER             = Logger.getLogger(CameraHelper.class.getName());
+    protected static final int  DEFAULT_CAM_WIDTH  = 640;
+    protected static final int  DEFAULT_CAM_HEIGHT = 480;
+
     /**
      * This constructor parses the given blender version and stores the result. Some functionalities may differ in
      * different blender versions.
      * @param blenderVersion
-     *        the version read from the blend file
+     *            the version read from the blend file
      * @param fixUpAxis
-     *        a variable that indicates if the Y asxis is the UP axis or not
+     *            a variable that indicates if the Y asxis is the UP axis or not
      */
     public CameraHelper(String blenderVersion, boolean fixUpAxis) {
         super(blenderVersion, fixUpAxis);
     }
-    
-	/**
-	 * This method converts the given structure to jme camera.
-	 * 
-	 * @param structure
-	 *            camera structure
-	 * @return jme camera object
-	 * @throws BlenderFileException
-	 *             an exception is thrown when there are problems with the
-	 *             blender file
-	 */
+
+    /**
+     * This method converts the given structure to jme camera.
+     * 
+     * @param structure
+     *            camera structure
+     * @return jme camera object
+     * @throws BlenderFileException
+     *             an exception is thrown when there are problems with the
+     *             blender file
+     */
     public CameraNode toCamera(Structure structure, BlenderContext blenderContext) throws BlenderFileException {
-    	if (blenderVersion >= 250) {
+        if (blenderVersion >= 250) {
             return this.toCamera250(structure, blenderContext.getSceneStructure());
         } else {
-        	return this.toCamera249(structure);
+            return this.toCamera249(structure);
         }
     }
 
-	/**
-	 * This method converts the given structure to jme camera. Should be used form blender 2.5+.
-	 * 
-	 * @param structure
-	 *            camera structure
-	 * @param sceneStructure
-	 *            scene structure
-	 * @return jme camera object
-	 * @throws BlenderFileException
-	 *             an exception is thrown when there are problems with the
-	 *             blender file
-	 */
+    /**
+     * This method converts the given structure to jme camera. Should be used form blender 2.5+.
+     * 
+     * @param structure
+     *            camera structure
+     * @param sceneStructure
+     *            scene structure
+     * @return jme camera object
+     * @throws BlenderFileException
+     *             an exception is thrown when there are problems with the
+     *             blender file
+     */
     private CameraNode toCamera250(Structure structure, Structure sceneStructure) throws BlenderFileException {
         int width = DEFAULT_CAM_WIDTH;
         int height = DEFAULT_CAM_HEIGHT;
         if (sceneStructure != null) {
-            Structure renderData = (Structure)sceneStructure.getFieldValue("r");
-            width = ((Number)renderData.getFieldValue("xsch")).shortValue();
-            height = ((Number)renderData.getFieldValue("ysch")).shortValue();
+            Structure renderData = (Structure) sceneStructure.getFieldValue("r");
+            width = ((Number) renderData.getFieldValue("xsch")).shortValue();
+            height = ((Number) renderData.getFieldValue("ysch")).shortValue();
         }
         Camera camera = new Camera(width, height);
         int type = ((Number) structure.getFieldValue("type")).intValue();
@@ -77,9 +77,9 @@ public class CameraHelper extends AbstractBlenderHelper {
             LOGGER.log(Level.WARNING, "Unknown camera type: {0}. Perspective camera is being used!", type);
             type = 0;
         }
-        //type==0 - perspective; type==1 - orthographic; perspective is used as default
+        // type==0 - perspective; type==1 - orthographic; perspective is used as default
         camera.setParallelProjection(type == 1);
-        float aspect = width / (float)height;
+        float aspect = width / (float) height;
         float fovY; // Vertical field of view in degrees
         float clipsta = ((Number) structure.getFieldValue("clipsta")).floatValue();
         float clipend = ((Number) structure.getFieldValue("clipend")).floatValue();
@@ -88,7 +88,7 @@ public class CameraHelper extends AbstractBlenderHelper {
             // Default sensor size prior to 2.60 was 32.
             float sensor = 32.0f;
             boolean sensorVertical = false;
-            Number sensorFit = (Number)structure.getFieldValue("sensor_fit");
+            Number sensorFit = (Number) structure.getFieldValue("sensor_fit");
             if (sensorFit != null) {
                 // If sensor_fit is vert (2), then sensor_y is used
                 sensorVertical = sensorFit.byteValue() == 2;
@@ -113,17 +113,17 @@ public class CameraHelper extends AbstractBlenderHelper {
         camera.setFrustumPerspective(fovY, aspect, clipsta, clipend);
         return new CameraNode(null, camera);
     }
-    
+
     /**
-	 * This method converts the given structure to jme camera. Should be used form blender 2.49.
-	 * 
-	 * @param structure
-	 *            camera structure
-	 * @return jme camera object
-	 * @throws BlenderFileException
-	 *             an exception is thrown when there are problems with the
-	 *             blender file
-	 */
+     * This method converts the given structure to jme camera. Should be used form blender 2.49.
+     * 
+     * @param structure
+     *            camera structure
+     * @return jme camera object
+     * @throws BlenderFileException
+     *             an exception is thrown when there are problems with the
+     *             blender file
+     */
     private CameraNode toCamera249(Structure structure) throws BlenderFileException {
         Camera camera = new Camera(DEFAULT_CAM_WIDTH, DEFAULT_CAM_HEIGHT);
         int type = ((Number) structure.getFieldValue("type")).intValue();
@@ -131,7 +131,7 @@ public class CameraHelper extends AbstractBlenderHelper {
             LOGGER.log(Level.WARNING, "Unknown camera type: {0}. Perspective camera is being used!", type);
             type = 0;
         }
-        //type==0 - perspective; type==1 - orthographic; perspective is used as default
+        // type==0 - perspective; type==1 - orthographic; perspective is used as default
         camera.setParallelProjection(type == 1);
         float aspect = 0;
         float clipsta = ((Number) structure.getFieldValue("clipsta")).floatValue();
@@ -145,8 +145,8 @@ public class CameraHelper extends AbstractBlenderHelper {
         return new CameraNode(null, camera);
     }
 
-	@Override
-	public boolean shouldBeLoaded(Structure structure, BlenderContext blenderContext) {
-		return (blenderContext.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.CAMERAS) != 0;
-	}
+    @Override
+    public boolean shouldBeLoaded(Structure structure, BlenderContext blenderContext) {
+        return (blenderContext.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.CAMERAS) != 0;
+    }
 }

+ 189 - 190
engine/src/blender/com/jme3/scene/plugins/blender/constraints/BoneConstraint.java

@@ -26,194 +26,193 @@ import com.jme3.scene.plugins.ogre.AnimData;
  * Constraint applied on the bone.
  * @author Marcin Roguski (Kaelthas)
  */
-/*package*/ class BoneConstraint extends Constraint {
-	private static final Logger LOGGER = Logger.getLogger(BoneConstraint.class.getName());
-	
-	protected boolean isNodeTarget;
-	
-	/**
-	 * The bone constraint constructor.
-	 * 
-	 * @param constraintStructure
-	 *            the constraint's structure
-	 * @param ownerOMA
-	 *            the OMA of the bone that owns the constraint
-	 * @param influenceIpo
-	 *            the influence interpolation curve
-	 * @param blenderContext
-	 *            the blender context
-	 * @throws BlenderFileException
-	 *             exception thrown when problems with blender file occur
-	 */
-	public BoneConstraint(Structure constraintStructure, Long ownerOMA, Ipo influenceIpo, BlenderContext blenderContext)
-			throws BlenderFileException {
-		super(constraintStructure, ownerOMA, influenceIpo, blenderContext);
-	}
-
-	@Override
-	protected boolean validate() {
-		if(targetOMA != null) {
-			Spatial nodeTarget = (Spatial)blenderContext.getLoadedFeature(targetOMA, LoadedFeatureDataType.LOADED_FEATURE);
-			//the second part of the if expression verifies if the found node (if any) is an armature node
-			if(nodeTarget == null || nodeTarget.getUserData(ArmatureHelper.ARMETURE_NODE_MARKER) != null) {
-				//if the target is not an object node then it is an Armature, so make sure the bone is in the current skeleton
-				BoneContext boneContext = blenderContext.getBoneContext(ownerOMA);
-				if(targetOMA.longValue() != boneContext.getArmatureObjectOMA().longValue()) {
-					LOGGER.log(Level.WARNING, "Bone constraint {0} must target bone in the its own skeleton! Targeting bone in another skeleton is not supported!", name);
-					return false;
-				}
-			} else {
-				isNodeTarget = true;
-			}
-		}
-		
-		return true;
-	}
-	
-	@Override
-	public void performBakingOperation() {
-		Bone owner = blenderContext.getBoneContext(ownerOMA).getBone();
-		
-		if(targetOMA != null) {
-			if(isNodeTarget) {
-				Spatial target = (Spatial) blenderContext.getLoadedFeature(targetOMA, LoadedFeatureDataType.LOADED_FEATURE);
-				this.prepareTracksForApplyingConstraints();
-				AnimData animData = blenderContext.getAnimData(ownerOMA);
-				if(animData != null) {
-					for(Animation animation : animData.anims) {
-						Transform ownerTransform = constraintHelper.getBoneTransform(ownerSpace, owner);
-						Transform targetTransform = constraintHelper.getNodeObjectTransform(targetSpace, targetOMA, blenderContext);
-						
-						Track boneTrack = constraintHelper.getTrack(owner, animData.skeleton, animation);
-						Track targetTrack = constraintHelper.getTrack(target, animation);
-						
-						constraintDefinition.bake(ownerTransform, targetTransform, boneTrack, targetTrack, this.ipo);
-					}
-				}
-			} else {
-				BoneContext boneContext = blenderContext.getBoneByName(subtargetName);
-				Bone target = boneContext.getBone();
-				this.targetOMA = boneContext.getBoneOma();
-				
-				this.prepareTracksForApplyingConstraints();
-				AnimData animData = blenderContext.getAnimData(ownerOMA);
-				if(animData != null) {
-					for(Animation animation : animData.anims) {
-						Transform ownerTransform = constraintHelper.getBoneTransform(ownerSpace, owner);
-						Transform targetTransform = constraintHelper.getBoneTransform(targetSpace, target);
-						
-						Track boneTrack = constraintHelper.getTrack(owner, animData.skeleton, animation);
-						Track targetTrack = constraintHelper.getTrack(target, animData.skeleton, animation);
-						
-						constraintDefinition.bake(ownerTransform, targetTransform, boneTrack, targetTrack, this.ipo);
-					}
-				}
-			}
-		} else {
-			this.prepareTracksForApplyingConstraints();
-			AnimData animData = blenderContext.getAnimData(ownerOMA);
-			if(animData != null) {
-				for(Animation animation : animData.anims) {
-					Transform ownerTransform = constraintHelper.getBoneTransform(ownerSpace, owner);
-					Track boneTrack = constraintHelper.getTrack(owner, animData.skeleton, animation);
-					
-					constraintDefinition.bake(ownerTransform, null, boneTrack, null, this.ipo);
-				}
-			}
-		}
-	}
-	
-	@Override
-	protected void prepareTracksForApplyingConstraints() {
-		Long[] bonesOMAs = new Long[] { ownerOMA, targetOMA };
-		Space[] spaces = new Space[] { ownerSpace, targetSpace };
-		
-		//creating animations for current objects if at least on of their parents have an animation
-		for (int i = 0; i < bonesOMAs.length; ++i) {
-			Long oma = bonesOMAs[i];
-			if(this.hasAnimation(oma)) {
-				Bone currentBone = blenderContext.getBoneContext(oma).getBone();
-				Bone parent = currentBone.getParent();
-				boolean foundAnimation = false;
-				AnimData animData = null;
-				while(parent != null && !foundAnimation) {
-					BoneContext boneContext = blenderContext.getBoneByName(parent.getName());
-					foundAnimation = this.hasAnimation(boneContext.getBoneOma());
-					animData = blenderContext.getAnimData(boneContext.getBoneOma());
-					parent = parent.getParent();
-				}
-				
-				if(foundAnimation) {
-					this.applyAnimData(blenderContext.getBoneContext(oma), spaces[i], animData);
-				}
-			}
-		}
-		
-		//creating animation for owner if it doesn't have one already and if the target has it
-		if(!this.hasAnimation(ownerOMA) && this.hasAnimation(targetOMA)) {
-			AnimData targetAnimData = blenderContext.getAnimData(targetOMA);
-			this.applyAnimData(blenderContext.getBoneContext(ownerOMA), ownerSpace, targetAnimData);
-		}
-	}
-	
-	/**
-	 * The method determines if the bone has animations.
-	 * 
-	 * @param animOwnerOMA
-	 *            OMA of the animation's owner
-	 * @return <b>true</b> if the target has animations and <b>false</b> otherwise
-	 */
-	protected boolean hasAnimation(Long animOwnerOMA) {
-		AnimData animData = blenderContext.getAnimData(animOwnerOMA);
-		if(animData != null) {
-			if(!isNodeTarget) {
-				Bone bone = blenderContext.getBoneContext(animOwnerOMA).getBone();
-				int boneIndex = animData.skeleton.getBoneIndex(bone);
-				for(Animation animation : animData.anims) {
-					for(Track track : animation.getTracks()) {
-						if(track instanceof BoneTrack && ((BoneTrack) track).getTargetBoneIndex() == boneIndex) {
-							return true;
-						}
-					}
-				}
-			} else {
-				return true;
-			}
-		}
-		return false;
-	}
-	
-	/**
-	 * The method applies bone's current position to all of the traces of the
-	 * given animations.
-	 * 
-	 * @param boneContext
-	 *            the bone context
-	 * @param space
-	 *            the bone's evaluation space
-	 * @param referenceAnimData
-	 *            the object containing the animations
-	 */
-	protected void applyAnimData(BoneContext boneContext, Space space, AnimData referenceAnimData) {
-		ConstraintHelper constraintHelper = blenderContext.getHelper(ConstraintHelper.class);
-		Transform transform = constraintHelper.getBoneTransform(space, boneContext.getBone());
-		
-		AnimData animData = blenderContext.getAnimData(boneContext.getBoneOma());
-		
-		for(Animation animation : referenceAnimData.anims) {
-			BoneTrack parentTrack = (BoneTrack) animation.getTracks()[0];
-			
-			float[] times = parentTrack.getTimes();
-			Vector3f[] translations = new Vector3f[times.length];
-			Quaternion[] rotations = new Quaternion[times.length];
-			Vector3f[] scales = new Vector3f[times.length];
-			Arrays.fill(translations, transform.getTranslation());
-			Arrays.fill(rotations, transform.getRotation());
-			Arrays.fill(scales, transform.getScale());
-			for(Animation anim : animData.anims) {
-				anim.addTrack(new BoneTrack(animData.skeleton.getBoneIndex(boneContext.getBone()), times, translations, rotations, scales));
-			}
-		}
-		blenderContext.setAnimData(boneContext.getBoneOma(), animData);
-	}
+/* package */class BoneConstraint extends Constraint {
+    private static final Logger LOGGER = Logger.getLogger(BoneConstraint.class.getName());
+
+    protected boolean           isNodeTarget;
+
+    /**
+     * The bone constraint constructor.
+     * 
+     * @param constraintStructure
+     *            the constraint's structure
+     * @param ownerOMA
+     *            the OMA of the bone that owns the constraint
+     * @param influenceIpo
+     *            the influence interpolation curve
+     * @param blenderContext
+     *            the blender context
+     * @throws BlenderFileException
+     *             exception thrown when problems with blender file occur
+     */
+    public BoneConstraint(Structure constraintStructure, Long ownerOMA, Ipo influenceIpo, BlenderContext blenderContext) throws BlenderFileException {
+        super(constraintStructure, ownerOMA, influenceIpo, blenderContext);
+    }
+
+    @Override
+    protected boolean validate() {
+        if (targetOMA != null) {
+            Spatial nodeTarget = (Spatial) blenderContext.getLoadedFeature(targetOMA, LoadedFeatureDataType.LOADED_FEATURE);
+            // the second part of the if expression verifies if the found node (if any) is an armature node
+            if (nodeTarget == null || nodeTarget.getUserData(ArmatureHelper.ARMETURE_NODE_MARKER) != null) {
+                // if the target is not an object node then it is an Armature, so make sure the bone is in the current skeleton
+                BoneContext boneContext = blenderContext.getBoneContext(ownerOMA);
+                if (targetOMA.longValue() != boneContext.getArmatureObjectOMA().longValue()) {
+                    LOGGER.log(Level.WARNING, "Bone constraint {0} must target bone in the its own skeleton! Targeting bone in another skeleton is not supported!", name);
+                    return false;
+                }
+            } else {
+                isNodeTarget = true;
+            }
+        }
+
+        return true;
+    }
+
+    @Override
+    public void performBakingOperation() {
+        Bone owner = blenderContext.getBoneContext(ownerOMA).getBone();
+
+        if (targetOMA != null) {
+            if (isNodeTarget) {
+                Spatial target = (Spatial) blenderContext.getLoadedFeature(targetOMA, LoadedFeatureDataType.LOADED_FEATURE);
+                this.prepareTracksForApplyingConstraints();
+                AnimData animData = blenderContext.getAnimData(ownerOMA);
+                if (animData != null) {
+                    for (Animation animation : animData.anims) {
+                        Transform ownerTransform = constraintHelper.getBoneTransform(ownerSpace, owner);
+                        Transform targetTransform = constraintHelper.getNodeObjectTransform(targetSpace, targetOMA, blenderContext);
+
+                        Track boneTrack = constraintHelper.getTrack(owner, animData.skeleton, animation);
+                        Track targetTrack = constraintHelper.getTrack(target, animation);
+
+                        constraintDefinition.bake(ownerTransform, targetTransform, boneTrack, targetTrack, this.ipo);
+                    }
+                }
+            } else {
+                BoneContext boneContext = blenderContext.getBoneByName(subtargetName);
+                Bone target = boneContext.getBone();
+                this.targetOMA = boneContext.getBoneOma();
+
+                this.prepareTracksForApplyingConstraints();
+                AnimData animData = blenderContext.getAnimData(ownerOMA);
+                if (animData != null) {
+                    for (Animation animation : animData.anims) {
+                        Transform ownerTransform = constraintHelper.getBoneTransform(ownerSpace, owner);
+                        Transform targetTransform = constraintHelper.getBoneTransform(targetSpace, target);
+
+                        Track boneTrack = constraintHelper.getTrack(owner, animData.skeleton, animation);
+                        Track targetTrack = constraintHelper.getTrack(target, animData.skeleton, animation);
+
+                        constraintDefinition.bake(ownerTransform, targetTransform, boneTrack, targetTrack, this.ipo);
+                    }
+                }
+            }
+        } else {
+            this.prepareTracksForApplyingConstraints();
+            AnimData animData = blenderContext.getAnimData(ownerOMA);
+            if (animData != null) {
+                for (Animation animation : animData.anims) {
+                    Transform ownerTransform = constraintHelper.getBoneTransform(ownerSpace, owner);
+                    Track boneTrack = constraintHelper.getTrack(owner, animData.skeleton, animation);
+
+                    constraintDefinition.bake(ownerTransform, null, boneTrack, null, this.ipo);
+                }
+            }
+        }
+    }
+
+    @Override
+    protected void prepareTracksForApplyingConstraints() {
+        Long[] bonesOMAs = new Long[] { ownerOMA, targetOMA };
+        Space[] spaces = new Space[] { ownerSpace, targetSpace };
+
+        // creating animations for current objects if at least on of their parents have an animation
+        for (int i = 0; i < bonesOMAs.length; ++i) {
+            Long oma = bonesOMAs[i];
+            if (this.hasAnimation(oma)) {
+                Bone currentBone = blenderContext.getBoneContext(oma).getBone();
+                Bone parent = currentBone.getParent();
+                boolean foundAnimation = false;
+                AnimData animData = null;
+                while (parent != null && !foundAnimation) {
+                    BoneContext boneContext = blenderContext.getBoneByName(parent.getName());
+                    foundAnimation = this.hasAnimation(boneContext.getBoneOma());
+                    animData = blenderContext.getAnimData(boneContext.getBoneOma());
+                    parent = parent.getParent();
+                }
+
+                if (foundAnimation) {
+                    this.applyAnimData(blenderContext.getBoneContext(oma), spaces[i], animData);
+                }
+            }
+        }
+
+        // creating animation for owner if it doesn't have one already and if the target has it
+        if (!this.hasAnimation(ownerOMA) && this.hasAnimation(targetOMA)) {
+            AnimData targetAnimData = blenderContext.getAnimData(targetOMA);
+            this.applyAnimData(blenderContext.getBoneContext(ownerOMA), ownerSpace, targetAnimData);
+        }
+    }
+
+    /**
+     * The method determines if the bone has animations.
+     * 
+     * @param animOwnerOMA
+     *            OMA of the animation's owner
+     * @return <b>true</b> if the target has animations and <b>false</b> otherwise
+     */
+    protected boolean hasAnimation(Long animOwnerOMA) {
+        AnimData animData = blenderContext.getAnimData(animOwnerOMA);
+        if (animData != null) {
+            if (!isNodeTarget) {
+                Bone bone = blenderContext.getBoneContext(animOwnerOMA).getBone();
+                int boneIndex = animData.skeleton.getBoneIndex(bone);
+                for (Animation animation : animData.anims) {
+                    for (Track track : animation.getTracks()) {
+                        if (track instanceof BoneTrack && ((BoneTrack) track).getTargetBoneIndex() == boneIndex) {
+                            return true;
+                        }
+                    }
+                }
+            } else {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * The method applies bone's current position to all of the traces of the
+     * given animations.
+     * 
+     * @param boneContext
+     *            the bone context
+     * @param space
+     *            the bone's evaluation space
+     * @param referenceAnimData
+     *            the object containing the animations
+     */
+    protected void applyAnimData(BoneContext boneContext, Space space, AnimData referenceAnimData) {
+        ConstraintHelper constraintHelper = blenderContext.getHelper(ConstraintHelper.class);
+        Transform transform = constraintHelper.getBoneTransform(space, boneContext.getBone());
+
+        AnimData animData = blenderContext.getAnimData(boneContext.getBoneOma());
+
+        for (Animation animation : referenceAnimData.anims) {
+            BoneTrack parentTrack = (BoneTrack) animation.getTracks()[0];
+
+            float[] times = parentTrack.getTimes();
+            Vector3f[] translations = new Vector3f[times.length];
+            Quaternion[] rotations = new Quaternion[times.length];
+            Vector3f[] scales = new Vector3f[times.length];
+            Arrays.fill(translations, transform.getTranslation());
+            Arrays.fill(rotations, transform.getRotation());
+            Arrays.fill(scales, transform.getScale());
+            for (Animation anim : animData.anims) {
+                anim.addTrack(new BoneTrack(animData.skeleton.getBoneIndex(boneContext.getBone()), times, translations, rotations, scales));
+            }
+        }
+        blenderContext.setAnimData(boneContext.getBoneOma(), animData);
+    }
 }

+ 105 - 105
engine/src/blender/com/jme3/scene/plugins/blender/constraints/Constraint.java

@@ -19,109 +19,109 @@ import com.jme3.scene.plugins.blender.file.Structure;
  * @author Marcin Roguski (Kaelthas)
  */
 public abstract class Constraint {
-	private static final Logger LOGGER = Logger.getLogger(Constraint.class.getName());
-	
-	/** The name of this constraint. */
-	protected final String name;
-	/** Indicates if the constraint is already baked or not. */
-	protected boolean baked;
-	
-	protected Space ownerSpace;
-	protected final ConstraintDefinition constraintDefinition;
-	protected Long ownerOMA;
-	
-	protected Long targetOMA;
-	protected Space targetSpace;
-	protected String subtargetName;
-	
-	/** The ipo object defining influence. */
-	protected final Ipo ipo;
-	/** The blender context. */
-	protected final BlenderContext blenderContext;
-	protected final ConstraintHelper constraintHelper;
-	
-	/**
-	 * This constructor creates the constraint instance.
-	 * 
-	 * @param constraintStructure
-	 *            the constraint's structure (bConstraint clss in blender 2.49).
-	 * @param ownerOMA
-	 *            the old memory address of the constraint owner
-	 * @param influenceIpo
-	 *            the ipo curve of the influence factor
-	 * @param blenderContext
-	 *            the blender context
-	 * @throws BlenderFileException
-	 *             this exception is thrown when the blender file is somehow
-	 *             corrupted
-	 */
-	public Constraint(Structure constraintStructure, Long ownerOMA, Ipo influenceIpo, BlenderContext blenderContext) throws BlenderFileException {
-		this.blenderContext = blenderContext;
-		this.name = constraintStructure.getFieldValue("name").toString();
-		Pointer pData = (Pointer) constraintStructure.getFieldValue("data");
-		if (pData.isNotNull()) {
-			Structure data = pData.fetchData(blenderContext.getInputStream()).get(0);
-			constraintDefinition = ConstraintDefinitionFactory.createConstraintDefinition(data, blenderContext);
-			Pointer pTar = (Pointer)data.getFieldValue("tar");
-			if(pTar!= null && pTar.isNotNull()) {
-				this.targetOMA = pTar.getOldMemoryAddress();
-				this.targetSpace = Space.valueOf(((Number) constraintStructure.getFieldValue("tarspace")).byteValue());
-				Object subtargetValue = data.getFieldValue("subtarget");
-				if(subtargetValue != null) {//not all constraint data have the subtarget field
-					subtargetName = subtargetValue.toString();
-				}
-			}
-		} else {
-			//Null constraint has no data, so create it here
-			constraintDefinition = ConstraintDefinitionFactory.createConstraintDefinition(null, blenderContext);
-		}
-		this.ownerSpace = Space.valueOf(((Number) constraintStructure.getFieldValue("ownspace")).byteValue());
-		this.ipo = influenceIpo;
-		this.ownerOMA = ownerOMA;
-		this.constraintHelper = blenderContext.getHelper(ConstraintHelper.class);
-	}
-	
-	/**
-	 * This method bakes the required sontraints into its owner. It checks if the constraint is invalid
-	 * or if it isn't yet baked. It also performs baking of its target constraints so that the proper baking
-	 * order is kept.
-	 */
-	public void bake() {
-		if(!this.validate()) {
-			LOGGER.warning("The constraint " + name + " is invalid and will not be applied.");
-		} else if(!baked) {
-			if(targetOMA != null) {
-				List<Constraint> targetConstraints = blenderContext.getConstraints(targetOMA);
-				if(targetConstraints != null && targetConstraints.size() > 0) {
-					LOGGER.log(Level.FINE, "Baking target constraints of constraint: {0}", name);
-					for(Constraint targetConstraint : targetConstraints) {
-						targetConstraint.bake();
-					}
-				}
-			}
-			
-			LOGGER.log(Level.FINE, "Performing baking of constraint: {0}", name);
-			this.performBakingOperation();
-			baked = true;
-		}
-	}
-	
-	/**
-	 * Performs validation before baking. Checks factors that can prevent constraint from baking that could not be
-	 * checked during constraint loading.
-	 */
-	protected abstract boolean validate();
-	
-	/**
-	 * This method should be overwridden and perform the baking opertion.
-	 */
-	protected abstract void performBakingOperation();
-	
-	/**
-	 * This method prepares the tracks for both owner and parent. If either owner or parent have no track while its parent has - 
-	 * the tracks are created. The tracks will not modify the owner/target movement but will be there ready for applying constraints.
-	 * For example if the owner is a spatial and has no animation but its parent is moving then the track is created for the owner
-	 * that will have non modifying values for translation, rotation and scale and will have the same amount of frames as its parent has.
-	 */
-	protected abstract void prepareTracksForApplyingConstraints();
+    private static final Logger          LOGGER = Logger.getLogger(Constraint.class.getName());
+
+    /** The name of this constraint. */
+    protected final String               name;
+    /** Indicates if the constraint is already baked or not. */
+    protected boolean                    baked;
+
+    protected Space                      ownerSpace;
+    protected final ConstraintDefinition constraintDefinition;
+    protected Long                       ownerOMA;
+
+    protected Long                       targetOMA;
+    protected Space                      targetSpace;
+    protected String                     subtargetName;
+
+    /** The ipo object defining influence. */
+    protected final Ipo                  ipo;
+    /** The blender context. */
+    protected final BlenderContext       blenderContext;
+    protected final ConstraintHelper     constraintHelper;
+
+    /**
+     * This constructor creates the constraint instance.
+     * 
+     * @param constraintStructure
+     *            the constraint's structure (bConstraint clss in blender 2.49).
+     * @param ownerOMA
+     *            the old memory address of the constraint owner
+     * @param influenceIpo
+     *            the ipo curve of the influence factor
+     * @param blenderContext
+     *            the blender context
+     * @throws BlenderFileException
+     *             this exception is thrown when the blender file is somehow
+     *             corrupted
+     */
+    public Constraint(Structure constraintStructure, Long ownerOMA, Ipo influenceIpo, BlenderContext blenderContext) throws BlenderFileException {
+        this.blenderContext = blenderContext;
+        this.name = constraintStructure.getFieldValue("name").toString();
+        Pointer pData = (Pointer) constraintStructure.getFieldValue("data");
+        if (pData.isNotNull()) {
+            Structure data = pData.fetchData(blenderContext.getInputStream()).get(0);
+            constraintDefinition = ConstraintDefinitionFactory.createConstraintDefinition(data, blenderContext);
+            Pointer pTar = (Pointer) data.getFieldValue("tar");
+            if (pTar != null && pTar.isNotNull()) {
+                this.targetOMA = pTar.getOldMemoryAddress();
+                this.targetSpace = Space.valueOf(((Number) constraintStructure.getFieldValue("tarspace")).byteValue());
+                Object subtargetValue = data.getFieldValue("subtarget");
+                if (subtargetValue != null) {// not all constraint data have the subtarget field
+                    subtargetName = subtargetValue.toString();
+                }
+            }
+        } else {
+            // Null constraint has no data, so create it here
+            constraintDefinition = ConstraintDefinitionFactory.createConstraintDefinition(null, blenderContext);
+        }
+        this.ownerSpace = Space.valueOf(((Number) constraintStructure.getFieldValue("ownspace")).byteValue());
+        this.ipo = influenceIpo;
+        this.ownerOMA = ownerOMA;
+        this.constraintHelper = blenderContext.getHelper(ConstraintHelper.class);
+    }
+
+    /**
+     * This method bakes the required sontraints into its owner. It checks if the constraint is invalid
+     * or if it isn't yet baked. It also performs baking of its target constraints so that the proper baking
+     * order is kept.
+     */
+    public void bake() {
+        if (!this.validate()) {
+            LOGGER.warning("The constraint " + name + " is invalid and will not be applied.");
+        } else if (!baked) {
+            if (targetOMA != null) {
+                List<Constraint> targetConstraints = blenderContext.getConstraints(targetOMA);
+                if (targetConstraints != null && targetConstraints.size() > 0) {
+                    LOGGER.log(Level.FINE, "Baking target constraints of constraint: {0}", name);
+                    for (Constraint targetConstraint : targetConstraints) {
+                        targetConstraint.bake();
+                    }
+                }
+            }
+
+            LOGGER.log(Level.FINE, "Performing baking of constraint: {0}", name);
+            this.performBakingOperation();
+            baked = true;
+        }
+    }
+
+    /**
+     * Performs validation before baking. Checks factors that can prevent constraint from baking that could not be
+     * checked during constraint loading.
+     */
+    protected abstract boolean validate();
+
+    /**
+     * This method should be overwridden and perform the baking opertion.
+     */
+    protected abstract void performBakingOperation();
+
+    /**
+     * This method prepares the tracks for both owner and parent. If either owner or parent have no track while its parent has -
+     * the tracks are created. The tracks will not modify the owner/target movement but will be there ready for applying constraints.
+     * For example if the owner is a spatial and has no animation but its parent is moving then the track is created for the owner
+     * that will have non modifying values for translation, rotation and scale and will have the same amount of frames as its parent has.
+     */
+    protected abstract void prepareTracksForApplyingConstraints();
 }

+ 436 - 436
engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintHelper.java

@@ -32,444 +32,444 @@ import com.jme3.scene.plugins.blender.file.Structure;
  * @author Marcin Roguski (Kaelthas)
  */
 public class ConstraintHelper extends AbstractBlenderHelper {
-	private static final Logger LOGGER = Logger.getLogger(ConstraintHelper.class.getName());
-	
-	/**
-	 * Helper constructor. It's main task is to generate the affection functions. These functions are common to all
-	 * ConstraintHelper instances. Unfortunately this constructor might grow large. If it becomes too large - I shall
-	 * consider refactoring. The constructor parses the given blender version and stores the result. Some
-	 * functionalities may differ in different blender versions.
-	 * @param blenderVersion
-	 *        the version read from the blend file
-	 * @param fixUpAxis
-     *        a variable that indicates if the Y asxis is the UP axis or not
-	 */
-	public ConstraintHelper(String blenderVersion, BlenderContext blenderContext, boolean fixUpAxis) {
-		super(blenderVersion, fixUpAxis);
-	}
-
-	/**
-	 * This method reads constraints for for the given structure. The
-	 * constraints are loaded only once for object/bone.
-	 * 
-	 * @param objectStructure
-	 *            the structure we read constraint's for
-	 * @param blenderContext
-	 *            the blender context
-	 * @throws BlenderFileException
-	 */
-	public void loadConstraints(Structure objectStructure, BlenderContext blenderContext) throws BlenderFileException {
-		LOGGER.fine("Loading constraints.");
-		// reading influence ipos for the constraints
-		IpoHelper ipoHelper = blenderContext.getHelper(IpoHelper.class);
-		Map<String, Map<String, Ipo>> constraintsIpos = new HashMap<String, Map<String, Ipo>>();
-		Pointer pActions = (Pointer) objectStructure.getFieldValue("action");
-		if (pActions.isNotNull()) {
-			List<Structure> actions = pActions.fetchData(blenderContext.getInputStream());
-			for (Structure action : actions) {
-				Structure chanbase = (Structure) action.getFieldValue("chanbase");
-				List<Structure> actionChannels = chanbase.evaluateListBase(blenderContext);
-				for (Structure actionChannel : actionChannels) {
-					Map<String, Ipo> ipos = new HashMap<String, Ipo>();
-					Structure constChannels = (Structure) actionChannel.getFieldValue("constraintChannels");
-					List<Structure> constraintChannels = constChannels.evaluateListBase(blenderContext);
-					for (Structure constraintChannel : constraintChannels) {
-						Pointer pIpo = (Pointer) constraintChannel.getFieldValue("ipo");
-						if (pIpo.isNotNull()) {
-							String constraintName = constraintChannel.getFieldValue("name").toString();
-							Ipo ipo = ipoHelper.fromIpoStructure(pIpo.fetchData(blenderContext.getInputStream()).get(0), blenderContext);
-							ipos.put(constraintName, ipo);
-						}
-					}
-					String actionName = actionChannel.getFieldValue("name").toString();
-					constraintsIpos.put(actionName, ipos);
-				}
-			}
-		}
-		
-		//loading constraints connected with the object's bones
-		Pointer pPose = (Pointer) objectStructure.getFieldValue("pose");
-		if (pPose.isNotNull()) {
-			List<Structure> poseChannels = ((Structure) pPose.fetchData(blenderContext.getInputStream()).get(0).getFieldValue("chanbase")).evaluateListBase(blenderContext);
-			for (Structure poseChannel : poseChannels) {
-				List<Constraint> constraintsList = new ArrayList<Constraint>();
-				Long boneOMA = Long.valueOf(((Pointer) poseChannel.getFieldValue("bone")).getOldMemoryAddress());
-				
-				//the name is read directly from structure because bone might not yet be loaded
-				String name = blenderContext.getFileBlock(boneOMA).getStructure(blenderContext).getFieldValue("name").toString();
-				List<Structure> constraints = ((Structure) poseChannel.getFieldValue("constraints")).evaluateListBase(blenderContext);
-				for (Structure constraint : constraints) {
-					String constraintName = constraint.getFieldValue("name").toString();
-					Map<String, Ipo> ipoMap = constraintsIpos.get(name);
-					Ipo ipo = ipoMap==null ? null : ipoMap.get(constraintName);
-					if (ipo == null) {
-						float enforce = ((Number) constraint.getFieldValue("enforce")).floatValue();
-						ipo = ipoHelper.fromValue(enforce);
-					}
-					constraintsList.add(new BoneConstraint(constraint, boneOMA, ipo, blenderContext));
-				}
-				blenderContext.addConstraints(boneOMA, constraintsList);
-			}
-		}
-
-		//loading constraints connected with the object itself
-		List<Structure> constraints = ((Structure)objectStructure.getFieldValue("constraints")).evaluateListBase(blenderContext);
-		if(constraints != null && constraints.size() > 0) {
-			Pointer pData = (Pointer) objectStructure.getFieldValue("data");
-			String dataType = pData.isNotNull() ? pData.fetchData(blenderContext.getInputStream()).get(0).getType() : null;
-			List<Constraint> constraintsList = new ArrayList<Constraint>(constraints.size());
-			
-			for(Structure constraint : constraints) {
-				String constraintName = constraint.getFieldValue("name").toString();
-				String objectName = objectStructure.getName();
-				
-				Map<String, Ipo> objectConstraintsIpos = constraintsIpos.get(objectName);
-				Ipo ipo = objectConstraintsIpos!=null ? objectConstraintsIpos.get(constraintName) : null;
-				if (ipo == null) {
-					float enforce = ((Number) constraint.getFieldValue("enforce")).floatValue();
-					ipo = ipoHelper.fromValue(enforce);
-				}
-				
-				constraintsList.add(this.getConstraint(dataType, constraint, objectStructure.getOldMemoryAddress(), ipo, blenderContext));
-			}
-			blenderContext.addConstraints(objectStructure.getOldMemoryAddress(), constraintsList);
-		}
-	}
-	
-	/**
-	 * This method creates a proper constraint object depending on the object's
-	 * data type. Supported data types: <li>Mesh <li>Armature <li>Camera <li>
-	 * Lamp Bone constraints are created in a different place.
-	 * 
-	 * @param dataType
-	 *            the type of the object's data
-	 * @param constraintStructure
-	 *            the constraint structure
-	 * @param ownerOMA
-	 *            the owner OMA
-	 * @param influenceIpo
-	 *            the influence interpolation curve
-	 * @param blenderContext
-	 *            the blender context
-	 * @return constraint object for the required type
-	 * @throws BlenderFileException
-	 *             thrown when problems with blender file occured
-	 */
-	private Constraint getConstraint(String dataType, Structure constraintStructure, Long ownerOMA, Ipo influenceIpo, BlenderContext blenderContext) throws BlenderFileException {
-		if(dataType == null || "Mesh".equalsIgnoreCase(dataType) || "Camera".equalsIgnoreCase(dataType) || "Lamp".equalsIgnoreCase(dataType)) {
-			return new SpatialConstraint(constraintStructure, ownerOMA, influenceIpo, blenderContext);
-		} else if("Armature".equalsIgnoreCase(dataType)) {
-			return new SkeletonConstraint(constraintStructure, ownerOMA, influenceIpo, blenderContext);
-		} else {
-			throw new IllegalArgumentException("Unsupported data type for applying constraints: " + dataType);
-		}
-	}
-	
-	/**
-	 * The method bakes all available and valid constraints.
-	 * 
-	 * @param blenderContext
-	 *            the blender context
-	 */
-	public void bakeConstraints(BlenderContext blenderContext) {
-		for(Constraint constraint : blenderContext.getAllConstraints()) {
-			constraint.bake();
-		}
-	}
-	
-	/**
-	 * The method returns track for bone.
-	 * 
-	 * @param bone
-	 *            the bone
-	 * @param skeleton
-	 *            the bone's skeleton
-	 * @param animation
-	 *            the bone's animation
-	 * @return track for the given bone that was found among the given
-	 *         animations or null if none is found
-	 */
-	/*package*/ BoneTrack getTrack(Bone bone, Skeleton skeleton, Animation animation) {
-		int boneIndex = skeleton.getBoneIndex(bone);
-		for (Track track : animation.getTracks()) {
+    private static final Logger LOGGER = Logger.getLogger(ConstraintHelper.class.getName());
+
+    /**
+     * Helper constructor. It's main task is to generate the affection functions. These functions are common to all
+     * ConstraintHelper instances. Unfortunately this constructor might grow large. If it becomes too large - I shall
+     * consider refactoring. The constructor parses the given blender version and stores the result. Some
+     * functionalities may differ in different blender versions.
+     * @param blenderVersion
+     *            the version read from the blend file
+     * @param fixUpAxis
+     *            a variable that indicates if the Y asxis is the UP axis or not
+     */
+    public ConstraintHelper(String blenderVersion, BlenderContext blenderContext, boolean fixUpAxis) {
+        super(blenderVersion, fixUpAxis);
+    }
+
+    /**
+     * This method reads constraints for for the given structure. The
+     * constraints are loaded only once for object/bone.
+     * 
+     * @param objectStructure
+     *            the structure we read constraint's for
+     * @param blenderContext
+     *            the blender context
+     * @throws BlenderFileException
+     */
+    public void loadConstraints(Structure objectStructure, BlenderContext blenderContext) throws BlenderFileException {
+        LOGGER.fine("Loading constraints.");
+        // reading influence ipos for the constraints
+        IpoHelper ipoHelper = blenderContext.getHelper(IpoHelper.class);
+        Map<String, Map<String, Ipo>> constraintsIpos = new HashMap<String, Map<String, Ipo>>();
+        Pointer pActions = (Pointer) objectStructure.getFieldValue("action");
+        if (pActions.isNotNull()) {
+            List<Structure> actions = pActions.fetchData(blenderContext.getInputStream());
+            for (Structure action : actions) {
+                Structure chanbase = (Structure) action.getFieldValue("chanbase");
+                List<Structure> actionChannels = chanbase.evaluateListBase(blenderContext);
+                for (Structure actionChannel : actionChannels) {
+                    Map<String, Ipo> ipos = new HashMap<String, Ipo>();
+                    Structure constChannels = (Structure) actionChannel.getFieldValue("constraintChannels");
+                    List<Structure> constraintChannels = constChannels.evaluateListBase(blenderContext);
+                    for (Structure constraintChannel : constraintChannels) {
+                        Pointer pIpo = (Pointer) constraintChannel.getFieldValue("ipo");
+                        if (pIpo.isNotNull()) {
+                            String constraintName = constraintChannel.getFieldValue("name").toString();
+                            Ipo ipo = ipoHelper.fromIpoStructure(pIpo.fetchData(blenderContext.getInputStream()).get(0), blenderContext);
+                            ipos.put(constraintName, ipo);
+                        }
+                    }
+                    String actionName = actionChannel.getFieldValue("name").toString();
+                    constraintsIpos.put(actionName, ipos);
+                }
+            }
+        }
+
+        // loading constraints connected with the object's bones
+        Pointer pPose = (Pointer) objectStructure.getFieldValue("pose");
+        if (pPose.isNotNull()) {
+            List<Structure> poseChannels = ((Structure) pPose.fetchData(blenderContext.getInputStream()).get(0).getFieldValue("chanbase")).evaluateListBase(blenderContext);
+            for (Structure poseChannel : poseChannels) {
+                List<Constraint> constraintsList = new ArrayList<Constraint>();
+                Long boneOMA = Long.valueOf(((Pointer) poseChannel.getFieldValue("bone")).getOldMemoryAddress());
+
+                // the name is read directly from structure because bone might not yet be loaded
+                String name = blenderContext.getFileBlock(boneOMA).getStructure(blenderContext).getFieldValue("name").toString();
+                List<Structure> constraints = ((Structure) poseChannel.getFieldValue("constraints")).evaluateListBase(blenderContext);
+                for (Structure constraint : constraints) {
+                    String constraintName = constraint.getFieldValue("name").toString();
+                    Map<String, Ipo> ipoMap = constraintsIpos.get(name);
+                    Ipo ipo = ipoMap == null ? null : ipoMap.get(constraintName);
+                    if (ipo == null) {
+                        float enforce = ((Number) constraint.getFieldValue("enforce")).floatValue();
+                        ipo = ipoHelper.fromValue(enforce);
+                    }
+                    constraintsList.add(new BoneConstraint(constraint, boneOMA, ipo, blenderContext));
+                }
+                blenderContext.addConstraints(boneOMA, constraintsList);
+            }
+        }
+
+        // loading constraints connected with the object itself
+        List<Structure> constraints = ((Structure) objectStructure.getFieldValue("constraints")).evaluateListBase(blenderContext);
+        if (constraints != null && constraints.size() > 0) {
+            Pointer pData = (Pointer) objectStructure.getFieldValue("data");
+            String dataType = pData.isNotNull() ? pData.fetchData(blenderContext.getInputStream()).get(0).getType() : null;
+            List<Constraint> constraintsList = new ArrayList<Constraint>(constraints.size());
+
+            for (Structure constraint : constraints) {
+                String constraintName = constraint.getFieldValue("name").toString();
+                String objectName = objectStructure.getName();
+
+                Map<String, Ipo> objectConstraintsIpos = constraintsIpos.get(objectName);
+                Ipo ipo = objectConstraintsIpos != null ? objectConstraintsIpos.get(constraintName) : null;
+                if (ipo == null) {
+                    float enforce = ((Number) constraint.getFieldValue("enforce")).floatValue();
+                    ipo = ipoHelper.fromValue(enforce);
+                }
+
+                constraintsList.add(this.getConstraint(dataType, constraint, objectStructure.getOldMemoryAddress(), ipo, blenderContext));
+            }
+            blenderContext.addConstraints(objectStructure.getOldMemoryAddress(), constraintsList);
+        }
+    }
+
+    /**
+     * This method creates a proper constraint object depending on the object's
+     * data type. Supported data types: <li>Mesh <li>Armature <li>Camera <li>
+     * Lamp Bone constraints are created in a different place.
+     * 
+     * @param dataType
+     *            the type of the object's data
+     * @param constraintStructure
+     *            the constraint structure
+     * @param ownerOMA
+     *            the owner OMA
+     * @param influenceIpo
+     *            the influence interpolation curve
+     * @param blenderContext
+     *            the blender context
+     * @return constraint object for the required type
+     * @throws BlenderFileException
+     *             thrown when problems with blender file occured
+     */
+    private Constraint getConstraint(String dataType, Structure constraintStructure, Long ownerOMA, Ipo influenceIpo, BlenderContext blenderContext) throws BlenderFileException {
+        if (dataType == null || "Mesh".equalsIgnoreCase(dataType) || "Camera".equalsIgnoreCase(dataType) || "Lamp".equalsIgnoreCase(dataType)) {
+            return new SpatialConstraint(constraintStructure, ownerOMA, influenceIpo, blenderContext);
+        } else if ("Armature".equalsIgnoreCase(dataType)) {
+            return new SkeletonConstraint(constraintStructure, ownerOMA, influenceIpo, blenderContext);
+        } else {
+            throw new IllegalArgumentException("Unsupported data type for applying constraints: " + dataType);
+        }
+    }
+
+    /**
+     * The method bakes all available and valid constraints.
+     * 
+     * @param blenderContext
+     *            the blender context
+     */
+    public void bakeConstraints(BlenderContext blenderContext) {
+        for (Constraint constraint : blenderContext.getAllConstraints()) {
+            constraint.bake();
+        }
+    }
+
+    /**
+     * The method returns track for bone.
+     * 
+     * @param bone
+     *            the bone
+     * @param skeleton
+     *            the bone's skeleton
+     * @param animation
+     *            the bone's animation
+     * @return track for the given bone that was found among the given
+     *         animations or null if none is found
+     */
+    /* package */BoneTrack getTrack(Bone bone, Skeleton skeleton, Animation animation) {
+        int boneIndex = skeleton.getBoneIndex(bone);
+        for (Track track : animation.getTracks()) {
             if (((BoneTrack) track).getTargetBoneIndex() == boneIndex) {
                 return (BoneTrack) track;
             }
         }
-		return null;
-	}
-	
-	/**
-	 * The method returns track for spatial.
-	 * 
-	 * @param bone
-	 *            the spatial
-	 * @param animation
-	 *            the spatial's animation
-	 * @return track for the given spatial that was found among the given
-	 *         animations or null if none is found
-	 */
-	/*package*/ SpatialTrack getTrack(Spatial spatial, Animation animation) {
-		Track[] tracks = animation.getTracks();
-		if(tracks != null && tracks.length == 1) {
-			return (SpatialTrack)tracks[0];
-		}
-		return null;
-	}
-	
-	/**
-	 * This method returns the transform read directly from the blender
-	 * structure. This can be used to read transforms from one of the object
-	 * types: <li>Spatial <li>Camera <li>Light
-	 * 
-	 * @param space
-	 *            the space where transform is evaluated
-	 * @param spatialOMA
-	 *            the OMA of the object
-	 * @param blenderContext
-	 *            the blender context
-	 * @return the object's transform in a given space
-	 */
-	@SuppressWarnings("unchecked")
-	/*package*/ Transform getNodeObjectTransform(Space space, Long spatialOMA, BlenderContext blenderContext) {
-		switch (space) {
-			case CONSTRAINT_SPACE_LOCAL:
-				Structure targetStructure = (Structure) blenderContext.getLoadedFeature(spatialOMA, LoadedFeatureDataType.LOADED_STRUCTURE);
-	
-				DynamicArray<Number> locArray = ((DynamicArray<Number>) targetStructure.getFieldValue("loc"));
-				Vector3f loc = new Vector3f(locArray.get(0).floatValue(), locArray.get(1).floatValue(), locArray.get(2).floatValue());
-				DynamicArray<Number> rotArray = ((DynamicArray<Number>) targetStructure.getFieldValue("rot"));
-				Quaternion rot = new Quaternion(new float[] { rotArray.get(0).floatValue(), rotArray.get(1).floatValue(), rotArray.get(2).floatValue() });
-				DynamicArray<Number> sizeArray = ((DynamicArray<Number>) targetStructure.getFieldValue("size"));
-				Vector3f size = new Vector3f(sizeArray.get(0).floatValue(), sizeArray.get(1).floatValue(), sizeArray.get(2).floatValue());
-	
-				if (blenderContext.getBlenderKey().isFixUpAxis()) {
-					float y = loc.y;
-					loc.y = loc.z;
-					loc.z = -y;
-	
-					y = rot.getY();
-					float z = rot.getZ();
-					rot.set(rot.getX(), z, -y, rot.getW());
-	
-					y = size.y;
-					size.y = size.z;
-					size.z = y;
-				}
-	
-				Transform result = new Transform(loc, rot);
-				result.setScale(size);
-				return result;
-			case CONSTRAINT_SPACE_WORLD://TODO: get it from the object structure ???
-				Object feature = blenderContext.getLoadedFeature(spatialOMA, LoadedFeatureDataType.LOADED_FEATURE);
-				if(feature instanceof Spatial) {
-					return ((Spatial) feature).getWorldTransform();
-				}  else if(feature instanceof Skeleton) {
-					LOGGER.warning("Trying to get transformation for skeleton. This is not supported. Returning null.");
-					return null;
-				} else {
-					throw new IllegalArgumentException("Given old memory address does not point to a valid object type (spatial, camera or light).");
-				}
-			default:
-				throw new IllegalStateException("Invalid space type for target object: " + space.toString());
-		}
-	}
-	
-	/**
-	 * The method returns the transform for the given bone computed in the given
-	 * space.
-	 * 
-	 * @param space
-	 *            the computation space
-	 * @param bone
-	 *            the bone we get the transform from
-	 * @return the transform of the given bone
-	 */
-	/*package*/ Transform getBoneTransform(Space space, Bone bone) {
-		switch (space) {
-			case CONSTRAINT_SPACE_LOCAL:
-				Transform localTransform = new Transform(bone.getLocalPosition(), bone.getLocalRotation());
-				localTransform.setScale(bone.getLocalScale());
-				return localTransform;
-			case CONSTRAINT_SPACE_WORLD:
-				Transform worldTransform = new Transform(bone.getWorldBindPosition(), bone.getWorldBindRotation());
-				worldTransform.setScale(bone.getWorldBindScale());
-				return worldTransform;
-			case CONSTRAINT_SPACE_POSE:
-				Transform poseTransform = new Transform(bone.getLocalPosition(), bone.getLocalRotation());
-				poseTransform.setScale(bone.getLocalScale());
-				return poseTransform;
-			case CONSTRAINT_SPACE_PARLOCAL:
-				Transform parentLocalTransform = new Transform(bone.getLocalPosition(), bone.getLocalRotation());
-				parentLocalTransform.setScale(bone.getLocalScale());
-				return parentLocalTransform;
-			default:
-				throw new IllegalStateException("Invalid space type for target object: " + space.toString());
-		}
-	}
-	
-	/**
-	 * The method applies the transform for the given spatial, computed in the
-	 * given space.
-	 * 
-	 * @param spatial
-	 *            the spatial we apply the transform for
-	 * @param space
-	 *            the computation space
-	 * @param transform
-	 *            the transform being applied
-	 */
-	/*package*/ void applyTransform(Spatial spatial, Space space, Transform transform) {
-		switch (space) {
-			case CONSTRAINT_SPACE_LOCAL:
-				Transform ownerLocalTransform = spatial.getLocalTransform();
-				ownerLocalTransform.getTranslation().addLocal(transform.getTranslation());
-				ownerLocalTransform.getRotation().multLocal(transform.getRotation());
-				ownerLocalTransform.getScale().multLocal(transform.getScale());
-				break;
-			case CONSTRAINT_SPACE_WORLD:
-				Matrix4f m = this.getParentWorldTransformMatrix(spatial);
-				m.invertLocal();
-				Matrix4f matrix = this.toMatrix(transform);
-				m.multLocal(matrix);
-	
-				float scaleX = (float) Math.sqrt(m.m00 * m.m00 + m.m10 * m.m10 + m.m20 * m.m20);
-				float scaleY = (float) Math.sqrt(m.m01 * m.m01 + m.m11 * m.m11 + m.m21 * m.m21);
-				float scaleZ = (float) Math.sqrt(m.m02 * m.m02 + m.m12 * m.m12 + m.m22 * m.m22);
-	
-				transform.setTranslation(m.toTranslationVector());
-				transform.setRotation(m.toRotationQuat());
-				transform.setScale(scaleX, scaleY, scaleZ);
-				spatial.setLocalTransform(transform);
-				break;
-			case CONSTRAINT_SPACE_PARLOCAL:
-			case CONSTRAINT_SPACE_POSE:
-				throw new IllegalStateException("Invalid space type (" + space.toString() + ") for owner object.");
-			default:
-				throw new IllegalStateException("Invalid space type for target object: " + space.toString());
-		}
-	}
-	
-	/**
-	 * The method applies the transform for the given bone, computed in the
-	 * given space.
-	 * 
-	 * @param bone
-	 *            the bone we apply the transform for
-	 * @param space
-	 *            the computation space
-	 * @param transform
-	 *            the transform being applied
-	 */
-	/*package*/ void applyTransform(Bone bone, Space space, Transform transform) {
-		switch (space) {
-			case CONSTRAINT_SPACE_LOCAL:
-				bone.setBindTransforms(transform.getTranslation(), transform.getRotation(), transform.getScale());
-				break;
-			case CONSTRAINT_SPACE_WORLD:
-				Matrix4f m = this.getParentWorldTransformMatrix(bone);
-		//			m.invertLocal();
-				transform.setTranslation(m.mult(transform.getTranslation()));
-				transform.setRotation(m.mult(transform.getRotation(), null));
-				transform.setScale(transform.getScale());
-				bone.setBindTransforms(transform.getTranslation(), transform.getRotation(), transform.getScale());
-		//			float x = FastMath.HALF_PI/2;
-		//			float y = -FastMath.HALF_PI;
-		//			float z = -FastMath.HALF_PI/2;
-		//			bone.setBindTransforms(new Vector3f(0,0,0), new Quaternion().fromAngles(x, y, z), new Vector3f(1,1,1));
-				break;
-			case CONSTRAINT_SPACE_PARLOCAL:
-				Vector3f parentLocalTranslation = bone.getLocalPosition().add(transform.getTranslation());
-				Quaternion parentLocalRotation = bone.getLocalRotation().mult(transform.getRotation());
-				bone.setBindTransforms(parentLocalTranslation, parentLocalRotation, transform.getScale());
-				break;
-			case CONSTRAINT_SPACE_POSE:
-				bone.setBindTransforms(transform.getTranslation(), transform.getRotation(), transform.getScale());
-				break;
-			default:
-				throw new IllegalStateException("Invalid space type for target object: " + space.toString());
-		}
-	}
-	
-	/**
-	 * @return world transform matrix of the feature's parent or identity matrix
-	 *         if the feature has no parent
-	 */
-	private Matrix4f getParentWorldTransformMatrix(Spatial spatial) {
-		Matrix4f result = new Matrix4f();
-		if (spatial.getParent() != null) {
-			Transform t = spatial.getParent().getWorldTransform();
-			result.setTransform(t.getTranslation(), t.getScale(), t.getRotation().toRotationMatrix());
-		}
-		return result;
-	}
-	
-	/**
-	 * @return world transform matrix of the feature's parent or identity matrix
-	 *         if the feature has no parent
-	 */
-	private Matrix4f getParentWorldTransformMatrix(Bone bone) {
-		Matrix4f result = new Matrix4f();
-		Bone parent = bone.getParent();
-		if (parent != null) {
-			result.setTransform(parent.getWorldBindPosition(), parent.getWorldBindScale(), parent.getWorldBindRotation().toRotationMatrix());
-		}
-		return result;
-	}
-
-	/**
-	 * Converts given transform to the matrix.
-	 * 
-	 * @param transform
-	 *            the transform to be converted
-	 * @return 4x4 matri that represents the given transform
-	 */
-	private Matrix4f toMatrix(Transform transform) {
-		Matrix4f result = Matrix4f.IDENTITY;
-		if (transform != null) {
-			result = new Matrix4f();
-			result.setTranslation(transform.getTranslation());
-			result.setRotationQuaternion(transform.getRotation());
-			result.setScale(transform.getScale());
-		}
-		return result;
-	}
-	
-	@Override
-	public boolean shouldBeLoaded(Structure structure, BlenderContext blenderContext) {
-		return true;
-	}
-	
-	/**
-	 * The space of target or owner transformation.
-	 * 
-	 * @author Marcin Roguski (Kaelthas)
-	 */
-	public static enum Space {
-
-		CONSTRAINT_SPACE_WORLD, CONSTRAINT_SPACE_LOCAL, CONSTRAINT_SPACE_POSE, CONSTRAINT_SPACE_PARLOCAL, CONSTRAINT_SPACE_INVALID;
-
-		/**
-		 * This method returns the enum instance when given the appropriate
-		 * value from the blend file.
-		 * 
-		 * @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;
-			}
-		}
-	}
+        return null;
+    }
+
+    /**
+     * The method returns track for spatial.
+     * 
+     * @param bone
+     *            the spatial
+     * @param animation
+     *            the spatial's animation
+     * @return track for the given spatial that was found among the given
+     *         animations or null if none is found
+     */
+    /* package */SpatialTrack getTrack(Spatial spatial, Animation animation) {
+        Track[] tracks = animation.getTracks();
+        if (tracks != null && tracks.length == 1) {
+            return (SpatialTrack) tracks[0];
+        }
+        return null;
+    }
+
+    /**
+     * This method returns the transform read directly from the blender
+     * structure. This can be used to read transforms from one of the object
+     * types: <li>Spatial <li>Camera <li>Light
+     * 
+     * @param space
+     *            the space where transform is evaluated
+     * @param spatialOMA
+     *            the OMA of the object
+     * @param blenderContext
+     *            the blender context
+     * @return the object's transform in a given space
+     */
+    @SuppressWarnings("unchecked")
+    /* package */Transform getNodeObjectTransform(Space space, Long spatialOMA, BlenderContext blenderContext) {
+        switch (space) {
+            case CONSTRAINT_SPACE_LOCAL:
+                Structure targetStructure = (Structure) blenderContext.getLoadedFeature(spatialOMA, LoadedFeatureDataType.LOADED_STRUCTURE);
+
+                DynamicArray<Number> locArray = ((DynamicArray<Number>) targetStructure.getFieldValue("loc"));
+                Vector3f loc = new Vector3f(locArray.get(0).floatValue(), locArray.get(1).floatValue(), locArray.get(2).floatValue());
+                DynamicArray<Number> rotArray = ((DynamicArray<Number>) targetStructure.getFieldValue("rot"));
+                Quaternion rot = new Quaternion(new float[] { rotArray.get(0).floatValue(), rotArray.get(1).floatValue(), rotArray.get(2).floatValue() });
+                DynamicArray<Number> sizeArray = ((DynamicArray<Number>) targetStructure.getFieldValue("size"));
+                Vector3f size = new Vector3f(sizeArray.get(0).floatValue(), sizeArray.get(1).floatValue(), sizeArray.get(2).floatValue());
+
+                if (blenderContext.getBlenderKey().isFixUpAxis()) {
+                    float y = loc.y;
+                    loc.y = loc.z;
+                    loc.z = -y;
+
+                    y = rot.getY();
+                    float z = rot.getZ();
+                    rot.set(rot.getX(), z, -y, rot.getW());
+
+                    y = size.y;
+                    size.y = size.z;
+                    size.z = y;
+                }
+
+                Transform result = new Transform(loc, rot);
+                result.setScale(size);
+                return result;
+            case CONSTRAINT_SPACE_WORLD:// TODO: get it from the object structure ???
+                Object feature = blenderContext.getLoadedFeature(spatialOMA, LoadedFeatureDataType.LOADED_FEATURE);
+                if (feature instanceof Spatial) {
+                    return ((Spatial) feature).getWorldTransform();
+                } else if (feature instanceof Skeleton) {
+                    LOGGER.warning("Trying to get transformation for skeleton. This is not supported. Returning null.");
+                    return null;
+                } else {
+                    throw new IllegalArgumentException("Given old memory address does not point to a valid object type (spatial, camera or light).");
+                }
+            default:
+                throw new IllegalStateException("Invalid space type for target object: " + space.toString());
+        }
+    }
+
+    /**
+     * The method returns the transform for the given bone computed in the given
+     * space.
+     * 
+     * @param space
+     *            the computation space
+     * @param bone
+     *            the bone we get the transform from
+     * @return the transform of the given bone
+     */
+    /* package */Transform getBoneTransform(Space space, Bone bone) {
+        switch (space) {
+            case CONSTRAINT_SPACE_LOCAL:
+                Transform localTransform = new Transform(bone.getLocalPosition(), bone.getLocalRotation());
+                localTransform.setScale(bone.getLocalScale());
+                return localTransform;
+            case CONSTRAINT_SPACE_WORLD:
+                Transform worldTransform = new Transform(bone.getWorldBindPosition(), bone.getWorldBindRotation());
+                worldTransform.setScale(bone.getWorldBindScale());
+                return worldTransform;
+            case CONSTRAINT_SPACE_POSE:
+                Transform poseTransform = new Transform(bone.getLocalPosition(), bone.getLocalRotation());
+                poseTransform.setScale(bone.getLocalScale());
+                return poseTransform;
+            case CONSTRAINT_SPACE_PARLOCAL:
+                Transform parentLocalTransform = new Transform(bone.getLocalPosition(), bone.getLocalRotation());
+                parentLocalTransform.setScale(bone.getLocalScale());
+                return parentLocalTransform;
+            default:
+                throw new IllegalStateException("Invalid space type for target object: " + space.toString());
+        }
+    }
+
+    /**
+     * The method applies the transform for the given spatial, computed in the
+     * given space.
+     * 
+     * @param spatial
+     *            the spatial we apply the transform for
+     * @param space
+     *            the computation space
+     * @param transform
+     *            the transform being applied
+     */
+    /* package */void applyTransform(Spatial spatial, Space space, Transform transform) {
+        switch (space) {
+            case CONSTRAINT_SPACE_LOCAL:
+                Transform ownerLocalTransform = spatial.getLocalTransform();
+                ownerLocalTransform.getTranslation().addLocal(transform.getTranslation());
+                ownerLocalTransform.getRotation().multLocal(transform.getRotation());
+                ownerLocalTransform.getScale().multLocal(transform.getScale());
+                break;
+            case CONSTRAINT_SPACE_WORLD:
+                Matrix4f m = this.getParentWorldTransformMatrix(spatial);
+                m.invertLocal();
+                Matrix4f matrix = this.toMatrix(transform);
+                m.multLocal(matrix);
+
+                float scaleX = (float) Math.sqrt(m.m00 * m.m00 + m.m10 * m.m10 + m.m20 * m.m20);
+                float scaleY = (float) Math.sqrt(m.m01 * m.m01 + m.m11 * m.m11 + m.m21 * m.m21);
+                float scaleZ = (float) Math.sqrt(m.m02 * m.m02 + m.m12 * m.m12 + m.m22 * m.m22);
+
+                transform.setTranslation(m.toTranslationVector());
+                transform.setRotation(m.toRotationQuat());
+                transform.setScale(scaleX, scaleY, scaleZ);
+                spatial.setLocalTransform(transform);
+                break;
+            case CONSTRAINT_SPACE_PARLOCAL:
+            case CONSTRAINT_SPACE_POSE:
+                throw new IllegalStateException("Invalid space type (" + space.toString() + ") for owner object.");
+            default:
+                throw new IllegalStateException("Invalid space type for target object: " + space.toString());
+        }
+    }
+
+    /**
+     * The method applies the transform for the given bone, computed in the
+     * given space.
+     * 
+     * @param bone
+     *            the bone we apply the transform for
+     * @param space
+     *            the computation space
+     * @param transform
+     *            the transform being applied
+     */
+    /* package */void applyTransform(Bone bone, Space space, Transform transform) {
+        switch (space) {
+            case CONSTRAINT_SPACE_LOCAL:
+                bone.setBindTransforms(transform.getTranslation(), transform.getRotation(), transform.getScale());
+                break;
+            case CONSTRAINT_SPACE_WORLD:
+                Matrix4f m = this.getParentWorldTransformMatrix(bone);
+                // m.invertLocal();
+                transform.setTranslation(m.mult(transform.getTranslation()));
+                transform.setRotation(m.mult(transform.getRotation(), null));
+                transform.setScale(transform.getScale());
+                bone.setBindTransforms(transform.getTranslation(), transform.getRotation(), transform.getScale());
+                // float x = FastMath.HALF_PI/2;
+                // float y = -FastMath.HALF_PI;
+                // float z = -FastMath.HALF_PI/2;
+                // bone.setBindTransforms(new Vector3f(0,0,0), new Quaternion().fromAngles(x, y, z), new Vector3f(1,1,1));
+                break;
+            case CONSTRAINT_SPACE_PARLOCAL:
+                Vector3f parentLocalTranslation = bone.getLocalPosition().add(transform.getTranslation());
+                Quaternion parentLocalRotation = bone.getLocalRotation().mult(transform.getRotation());
+                bone.setBindTransforms(parentLocalTranslation, parentLocalRotation, transform.getScale());
+                break;
+            case CONSTRAINT_SPACE_POSE:
+                bone.setBindTransforms(transform.getTranslation(), transform.getRotation(), transform.getScale());
+                break;
+            default:
+                throw new IllegalStateException("Invalid space type for target object: " + space.toString());
+        }
+    }
+
+    /**
+     * @return world transform matrix of the feature's parent or identity matrix
+     *         if the feature has no parent
+     */
+    private Matrix4f getParentWorldTransformMatrix(Spatial spatial) {
+        Matrix4f result = new Matrix4f();
+        if (spatial.getParent() != null) {
+            Transform t = spatial.getParent().getWorldTransform();
+            result.setTransform(t.getTranslation(), t.getScale(), t.getRotation().toRotationMatrix());
+        }
+        return result;
+    }
+
+    /**
+     * @return world transform matrix of the feature's parent or identity matrix
+     *         if the feature has no parent
+     */
+    private Matrix4f getParentWorldTransformMatrix(Bone bone) {
+        Matrix4f result = new Matrix4f();
+        Bone parent = bone.getParent();
+        if (parent != null) {
+            result.setTransform(parent.getWorldBindPosition(), parent.getWorldBindScale(), parent.getWorldBindRotation().toRotationMatrix());
+        }
+        return result;
+    }
+
+    /**
+     * Converts given transform to the matrix.
+     * 
+     * @param transform
+     *            the transform to be converted
+     * @return 4x4 matri that represents the given transform
+     */
+    private Matrix4f toMatrix(Transform transform) {
+        Matrix4f result = Matrix4f.IDENTITY;
+        if (transform != null) {
+            result = new Matrix4f();
+            result.setTranslation(transform.getTranslation());
+            result.setRotationQuaternion(transform.getRotation());
+            result.setScale(transform.getScale());
+        }
+        return result;
+    }
+
+    @Override
+    public boolean shouldBeLoaded(Structure structure, BlenderContext blenderContext) {
+        return true;
+    }
+
+    /**
+     * The space of target or owner transformation.
+     * 
+     * @author Marcin Roguski (Kaelthas)
+     */
+    public static enum Space {
+
+        CONSTRAINT_SPACE_WORLD, CONSTRAINT_SPACE_LOCAL, CONSTRAINT_SPACE_POSE, CONSTRAINT_SPACE_PARLOCAL, CONSTRAINT_SPACE_INVALID;
+
+        /**
+         * This method returns the enum instance when given the appropriate
+         * value from the blend file.
+         * 
+         * @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;
+            }
+        }
+    }
 }

+ 18 - 17
engine/src/blender/com/jme3/scene/plugins/blender/constraints/SkeletonConstraint.java

@@ -15,23 +15,24 @@ import com.jme3.scene.plugins.blender.file.Structure;
  * 
  * @author Marcin Roguski (Kaelthas)
  */
-/*package*/ class SkeletonConstraint extends Constraint {
-	private static final Logger LOGGER = Logger.getLogger(SkeletonConstraint.class.getName());
-	
-	public SkeletonConstraint(Structure constraintStructure, Long ownerOMA, Ipo influenceIpo, BlenderContext blenderContext) throws BlenderFileException {
-		super(constraintStructure, ownerOMA, influenceIpo, blenderContext);
-	}
+/* package */class SkeletonConstraint extends Constraint {
+    private static final Logger LOGGER = Logger.getLogger(SkeletonConstraint.class.getName());
 
-	@Override
-	public void performBakingOperation() {
-		LOGGER.warning("Applying constraints to skeleton is not supported.");
-	}
+    public SkeletonConstraint(Structure constraintStructure, Long ownerOMA, Ipo influenceIpo, BlenderContext blenderContext) throws BlenderFileException {
+        super(constraintStructure, ownerOMA, influenceIpo, blenderContext);
+    }
 
-	@Override
-	protected boolean validate() {
-		return true;
-	}
-	
-	@Override
-	protected void prepareTracksForApplyingConstraints() { }
+    @Override
+    public void performBakingOperation() {
+        LOGGER.warning("Applying constraints to skeleton is not supported.");
+    }
+
+    @Override
+    protected boolean validate() {
+        return true;
+    }
+
+    @Override
+    protected void prepareTracksForApplyingConstraints() {
+    }
 }

+ 140 - 142
engine/src/blender/com/jme3/scene/plugins/blender/constraints/SpatialConstraint.java

@@ -28,146 +28,144 @@ import com.jme3.scene.plugins.ogre.AnimData;
  * This includes: nodes, cameras nodes and light nodes.
  * @author Marcin Roguski (Kaelthas)
  */
-/*package*/ class SpatialConstraint extends BoneConstraint {
-	private static final Logger LOGGER = Logger.getLogger(SpatialConstraint.class.getName());
-	
-	/** The owner of the constraint. */
-	private Spatial owner;
-	/** The target of the constraint. */
-	private Object target;
-	
-	public SpatialConstraint(Structure constraintStructure, Long ownerOMA, Ipo influenceIpo, BlenderContext blenderContext)
-			throws BlenderFileException {
-		super(constraintStructure, ownerOMA, influenceIpo, blenderContext);
-	}
-	
-	@Override
-	public void performBakingOperation() {
-		this.owner = (Spatial) blenderContext.getLoadedFeature(ownerOMA, LoadedFeatureDataType.LOADED_FEATURE);
-		this.target = targetOMA != null ? blenderContext.getLoadedFeature(targetOMA, LoadedFeatureDataType.LOADED_FEATURE) : null;
-		this.prepareTracksForApplyingConstraints();
-		
-		//apply static constraint
-		Transform ownerTransform = constraintHelper.getNodeObjectTransform(ownerSpace, ownerOMA, blenderContext);
-		Transform targetTransform = targetOMA != null ? constraintHelper.getNodeObjectTransform(targetSpace, targetOMA, blenderContext) : null;
-		constraintDefinition.bake(ownerTransform, targetTransform, null, null, this.ipo);
-		constraintHelper.applyTransform(owner, ownerSpace, ownerTransform);
-		
-		//apply dynamic constraint
-		AnimData animData = blenderContext.getAnimData(ownerOMA);
-		if(animData != null) {
-			for(Animation animation : animData.anims) {
-				SpatialTrack ownerTrack = constraintHelper.getTrack(owner, animation);
-				
-				AnimData targetAnimData = blenderContext.getAnimData(targetOMA);
-				SpatialTrack targetTrack = null;
-				if(targetAnimData != null) {
-					targetTrack = constraintHelper.getTrack((Spatial)target, targetAnimData.anims.get(0));
-				}
-				
-				constraintDefinition.bake(ownerTransform, targetTransform, ownerTrack, targetTrack, this.ipo);
-			}
-		}
-	}
-	
-	@Override
-	protected void prepareTracksForApplyingConstraints() {
-		Long[] spatialsOMAs = new Long[] { ownerOMA, targetOMA };
-		Space[] spaces = new Space[] { ownerSpace, targetSpace };
-		
-		//creating animations for current objects if at least on of their parents have an animation
-		for (int i = 0; i < spatialsOMAs.length; ++i) {
-			Long oma = spatialsOMAs[i];
-			if(oma != null && oma > 0L) {
-				AnimData animData = blenderContext.getAnimData(oma);
-				if(animData == null) {
-					Spatial currentSpatial = (Spatial)blenderContext.getLoadedFeature(oma, LoadedFeatureDataType.LOADED_FEATURE);
-					if(currentSpatial != null) {
-						if(currentSpatial.getUserData(ArmatureHelper.ARMETURE_NODE_MARKER) == Boolean.TRUE) {//look for it among bones
-							BoneContext currentBoneContext = blenderContext.getBoneByName(subtargetName);
-							Bone currentBone = currentBoneContext.getBone();
-							Bone parent = currentBone.getParent();
-							boolean foundAnimation = false;
-							while(parent != null && !foundAnimation) {
-								BoneContext boneContext = blenderContext.getBoneByName(parent.getName());
-								foundAnimation = this.hasAnimation(boneContext.getBoneOma());
-								animData = blenderContext.getAnimData(boneContext.getBoneOma());
-								parent = parent.getParent();
-							}
-							if(foundAnimation) {
-								this.applyAnimData(currentBoneContext, spaces[i], animData);
-							}
-						} else {
-							Spatial parent = currentSpatial.getParent();
-							while(parent != null && animData == null) {
-								Structure parentStructure = (Structure)blenderContext.getLoadedFeature(parent.getName(), LoadedFeatureDataType.LOADED_STRUCTURE);
-								if(parentStructure == null) {
-									parent = null;
-								} else {
-									Long parentOma = parentStructure.getOldMemoryAddress();
-									animData = blenderContext.getAnimData(parentOma);
-									parent = parent.getParent();
-								}
-							}
-							
-							if(animData != null) {//create anim data for the current object
-								this.applyAnimData(currentSpatial, oma, spaces[i], animData.anims.get(0));
-							}
-						}
-					} else {
-						LOGGER.warning("Couldn't find target object for constraint: " + name + 
-									   ". Make sure that the target is on layer that is defined to be loaded in blender key!");
-					}
-				}
-			}
-		}
-		
-		//creating animation for owner if it doesn't have one already and if the target has it
-		AnimData animData = blenderContext.getAnimData(ownerOMA);
-		if(animData == null) {
-			AnimData targetAnimData = blenderContext.getAnimData(targetOMA);
-			if(targetAnimData != null) {
-				this.applyAnimData(owner, ownerOMA, ownerSpace, targetAnimData.anims.get(0));
-			}
-		}
-	}
-	
-	/**
-	 * This method applies spatial transform on each frame of the given
-	 * animations.
-	 * 
-	 * @param spatial
-	 *            the spatial
-	 * @param spatialOma
-	 *            the OMA of the given spatial
-	 * @param space
-	 *            the space we compute the transform in
-	 * @param referenceAnimation
-	 *            the object containing the animations
-	 */
-	private void applyAnimData(Spatial spatial, Long spatialOma, Space space, Animation referenceAnimation) {
-		ConstraintHelper constraintHelper = blenderContext.getHelper(ConstraintHelper.class);
-		Transform transform = constraintHelper.getNodeObjectTransform(space, spatialOma, blenderContext);
-		
-		SpatialTrack parentTrack = (SpatialTrack) referenceAnimation.getTracks()[0];
-		
-		HashMap<String, Animation> anims = new HashMap<String, Animation>(1);
-		Animation animation = new Animation(spatial.getName(), referenceAnimation.getLength());
-		anims.put(spatial.getName(), animation);
-		
-		float[] times = parentTrack.getTimes();
-		Vector3f[] translations = new Vector3f[times.length];
-		Quaternion[] rotations = new Quaternion[times.length];
-		Vector3f[] scales = new Vector3f[times.length];
-		Arrays.fill(translations, transform.getTranslation());
-		Arrays.fill(rotations, transform.getRotation());
-		Arrays.fill(scales, transform.getScale());
-		animation.addTrack(new SpatialTrack(times, translations, rotations, scales));
-		
-		AnimControl control = new AnimControl(null);
-		control.setAnimations(anims);
-		spatial.addControl(control);
-		
-		blenderContext.setAnimData(spatialOma, new AnimData(null, new ArrayList<Animation>(anims.values())));
-	}
+/* package */class SpatialConstraint extends BoneConstraint {
+    private static final Logger LOGGER = Logger.getLogger(SpatialConstraint.class.getName());
+
+    /** The owner of the constraint. */
+    private Spatial             owner;
+    /** The target of the constraint. */
+    private Object              target;
+
+    public SpatialConstraint(Structure constraintStructure, Long ownerOMA, Ipo influenceIpo, BlenderContext blenderContext) throws BlenderFileException {
+        super(constraintStructure, ownerOMA, influenceIpo, blenderContext);
+    }
+
+    @Override
+    public void performBakingOperation() {
+        this.owner = (Spatial) blenderContext.getLoadedFeature(ownerOMA, LoadedFeatureDataType.LOADED_FEATURE);
+        this.target = targetOMA != null ? blenderContext.getLoadedFeature(targetOMA, LoadedFeatureDataType.LOADED_FEATURE) : null;
+        this.prepareTracksForApplyingConstraints();
+
+        // apply static constraint
+        Transform ownerTransform = constraintHelper.getNodeObjectTransform(ownerSpace, ownerOMA, blenderContext);
+        Transform targetTransform = targetOMA != null ? constraintHelper.getNodeObjectTransform(targetSpace, targetOMA, blenderContext) : null;
+        constraintDefinition.bake(ownerTransform, targetTransform, null, null, this.ipo);
+        constraintHelper.applyTransform(owner, ownerSpace, ownerTransform);
+
+        // apply dynamic constraint
+        AnimData animData = blenderContext.getAnimData(ownerOMA);
+        if (animData != null) {
+            for (Animation animation : animData.anims) {
+                SpatialTrack ownerTrack = constraintHelper.getTrack(owner, animation);
+
+                AnimData targetAnimData = blenderContext.getAnimData(targetOMA);
+                SpatialTrack targetTrack = null;
+                if (targetAnimData != null) {
+                    targetTrack = constraintHelper.getTrack((Spatial) target, targetAnimData.anims.get(0));
+                }
+
+                constraintDefinition.bake(ownerTransform, targetTransform, ownerTrack, targetTrack, this.ipo);
+            }
+        }
+    }
+
+    @Override
+    protected void prepareTracksForApplyingConstraints() {
+        Long[] spatialsOMAs = new Long[] { ownerOMA, targetOMA };
+        Space[] spaces = new Space[] { ownerSpace, targetSpace };
+
+        // creating animations for current objects if at least on of their parents have an animation
+        for (int i = 0; i < spatialsOMAs.length; ++i) {
+            Long oma = spatialsOMAs[i];
+            if (oma != null && oma > 0L) {
+                AnimData animData = blenderContext.getAnimData(oma);
+                if (animData == null) {
+                    Spatial currentSpatial = (Spatial) blenderContext.getLoadedFeature(oma, LoadedFeatureDataType.LOADED_FEATURE);
+                    if (currentSpatial != null) {
+                        if (currentSpatial.getUserData(ArmatureHelper.ARMETURE_NODE_MARKER) == Boolean.TRUE) {// look for it among bones
+                            BoneContext currentBoneContext = blenderContext.getBoneByName(subtargetName);
+                            Bone currentBone = currentBoneContext.getBone();
+                            Bone parent = currentBone.getParent();
+                            boolean foundAnimation = false;
+                            while (parent != null && !foundAnimation) {
+                                BoneContext boneContext = blenderContext.getBoneByName(parent.getName());
+                                foundAnimation = this.hasAnimation(boneContext.getBoneOma());
+                                animData = blenderContext.getAnimData(boneContext.getBoneOma());
+                                parent = parent.getParent();
+                            }
+                            if (foundAnimation) {
+                                this.applyAnimData(currentBoneContext, spaces[i], animData);
+                            }
+                        } else {
+                            Spatial parent = currentSpatial.getParent();
+                            while (parent != null && animData == null) {
+                                Structure parentStructure = (Structure) blenderContext.getLoadedFeature(parent.getName(), LoadedFeatureDataType.LOADED_STRUCTURE);
+                                if (parentStructure == null) {
+                                    parent = null;
+                                } else {
+                                    Long parentOma = parentStructure.getOldMemoryAddress();
+                                    animData = blenderContext.getAnimData(parentOma);
+                                    parent = parent.getParent();
+                                }
+                            }
+
+                            if (animData != null) {// create anim data for the current object
+                                this.applyAnimData(currentSpatial, oma, spaces[i], animData.anims.get(0));
+                            }
+                        }
+                    } else {
+                        LOGGER.warning("Couldn't find target object for constraint: " + name + ". Make sure that the target is on layer that is defined to be loaded in blender key!");
+                    }
+                }
+            }
+        }
+
+        // creating animation for owner if it doesn't have one already and if the target has it
+        AnimData animData = blenderContext.getAnimData(ownerOMA);
+        if (animData == null) {
+            AnimData targetAnimData = blenderContext.getAnimData(targetOMA);
+            if (targetAnimData != null) {
+                this.applyAnimData(owner, ownerOMA, ownerSpace, targetAnimData.anims.get(0));
+            }
+        }
+    }
+
+    /**
+     * This method applies spatial transform on each frame of the given
+     * animations.
+     * 
+     * @param spatial
+     *            the spatial
+     * @param spatialOma
+     *            the OMA of the given spatial
+     * @param space
+     *            the space we compute the transform in
+     * @param referenceAnimation
+     *            the object containing the animations
+     */
+    private void applyAnimData(Spatial spatial, Long spatialOma, Space space, Animation referenceAnimation) {
+        ConstraintHelper constraintHelper = blenderContext.getHelper(ConstraintHelper.class);
+        Transform transform = constraintHelper.getNodeObjectTransform(space, spatialOma, blenderContext);
+
+        SpatialTrack parentTrack = (SpatialTrack) referenceAnimation.getTracks()[0];
+
+        HashMap<String, Animation> anims = new HashMap<String, Animation>(1);
+        Animation animation = new Animation(spatial.getName(), referenceAnimation.getLength());
+        anims.put(spatial.getName(), animation);
+
+        float[] times = parentTrack.getTimes();
+        Vector3f[] translations = new Vector3f[times.length];
+        Quaternion[] rotations = new Quaternion[times.length];
+        Vector3f[] scales = new Vector3f[times.length];
+        Arrays.fill(translations, transform.getTranslation());
+        Arrays.fill(rotations, transform.getRotation());
+        Arrays.fill(scales, transform.getScale());
+        animation.addTrack(new SpatialTrack(times, translations, rotations, scales));
+
+        AnimControl control = new AnimControl(null);
+        control.setAnimations(anims);
+        spatial.addControl(control);
+
+        blenderContext.setAnimData(spatialOma, new AnimData(null, new ArrayList<Animation>(anims.values())));
+    }
 }

+ 221 - 226
engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinition.java

@@ -19,230 +19,225 @@ import com.jme3.scene.plugins.blender.file.Structure;
 import com.jme3.util.TempVars;
 
 public abstract class ConstraintDefinition {
-	protected int flag;
-	
-	public ConstraintDefinition(Structure constraintData, BlenderContext blenderContext) {
-		if(constraintData != null) {//Null constraint has no data
-			Number flag = (Number)constraintData.getFieldValue("flag");
-			if(flag != null) {
-				this.flag = flag.intValue();
-			}
-		}
-	}
-	
-	public void bake(Transform ownerTransform, Transform targetTransform, Track ownerTrack, Track targetTrack, Ipo influenceIpo) {
-		TrackWrapper ownerWrapperTrack = ownerTrack != null ? new TrackWrapper(ownerTrack) : null;
-		TrackWrapper targetWrapperTrack = targetTrack != null ? new TrackWrapper(targetTrack) : null;
-		
-		//uruchamiamy bake dla transformat zalenie od tego, ktre argumenty s nullami, a ktre - nie
-		this.bake(ownerTransform, targetTransform, influenceIpo.calculateValue(0));
-		if(ownerWrapperTrack != null) {
-			float[] ownerTimes = ownerWrapperTrack.getTimes();
-			Vector3f[] translations = ownerWrapperTrack.getTranslations();
-			Quaternion[] rotations = ownerWrapperTrack.getRotations();
-			Vector3f[] scales = ownerWrapperTrack.getScales();
-			
-			float[] targetTimes = targetWrapperTrack == null ? null : targetWrapperTrack.getTimes();
-			Vector3f[] targetTranslations = targetWrapperTrack == null ? null : targetWrapperTrack.getTranslations();
-			Quaternion[] targetRotations = targetWrapperTrack == null ? null : targetWrapperTrack.getRotations();
-			Vector3f[] targetScales = targetWrapperTrack == null ? null : targetWrapperTrack.getScales();
-			Vector3f translation = new Vector3f(), scale = new Vector3f();
-			Quaternion rotation = new Quaternion();
-			
-			Transform ownerTemp = new Transform(), targetTemp = new Transform();
-			for (int i = 0; i <ownerTimes.length; ++i) {
-				float t = ownerTimes[i];
-				ownerTemp.setTranslation(translations[i]);
-				ownerTemp.setRotation(rotations[i]);
-				ownerTemp.setScale(scales[i]);
-				if(targetWrapperTrack == null) {
-					this.bake(ownerTemp, targetTransform, influenceIpo.calculateValue(i));
-				} else {
-					//getting the values that are the interpolation of the target track for the time 't'
-					this.interpolate(targetTranslations, targetTimes, t, translation);
-					this.interpolate(targetRotations, targetTimes, t, rotation);
-					this.interpolate(targetScales, targetTimes, t, scale);
-					
-					targetTemp.setTranslation(translation);
-					targetTemp.setRotation(rotation);
-					targetTemp.setScale(scale);
-					
-					this.bake(ownerTemp, targetTemp, influenceIpo.calculateValue(i));
-				}
-				//need to clone here because each of the arrays will reference the same instance if they hold the same value in the compact array
-				translations[i] = ownerTemp.getTranslation().clone();
-				rotations[i] = ownerTemp.getRotation().clone();
-				scales[i] = ownerTemp.getScale().clone();
-			}
-			ownerWrapperTrack.setKeyframes(ownerTimes, translations, rotations, scales);
-		}
-	}
-	
-	protected abstract void bake(Transform ownerTransform, Transform targetTransform, float influence);
-	
-	private void interpolate(Vector3f[] targetVectors, float[] targetTimes, float currentTime, Vector3f result) {
-		int index = 0;
-		for (int i = 1; i < targetTimes.length; ++i) {
-			if(targetTimes[i] < currentTime) {
-				++index;
-			} else {
-				break;
-			}
-		}
-		if(index >= targetTimes.length - 1) {
-			result.set(targetVectors[targetTimes.length - 1]);
-		} else {
-			float delta = targetTimes[index + 1] - targetTimes[index];
-			if(delta == 0.0f) {
-				result.set(targetVectors[index + 1]);
-			} else {
-				float scale = (currentTime - targetTimes[index])/(targetTimes[index + 1] - targetTimes[index]);
-				FastMath.interpolateLinear(scale, targetVectors[index], targetVectors[index + 1], result);
-			}
-		}
-	}
-	
-	private void interpolate(Quaternion[] targetQuaternions, float[] targetTimes, float currentTime, Quaternion result) {
-		int index = 0;
-		for (int i = 1; i < targetTimes.length; ++i) {
-			if(targetTimes[i] < currentTime) {
-				++index;
-			} else {
-				break;
-			}
-		}
-		if(index >= targetTimes.length - 1) {
-			result.set(targetQuaternions[targetTimes.length - 1]);
-		} else {
-			float delta = targetTimes[index + 1] - targetTimes[index];
-			if(delta == 0.0f) {
-				result.set(targetQuaternions[index + 1]);
-			} else {
-				float scale = (currentTime - targetTimes[index])/(targetTimes[index + 1] - targetTimes[index]);
-				result.slerp(targetQuaternions[index], targetQuaternions[index + 1], scale);
-			}
-		}
-	}
-	
-	/**
-	 * This class holds either the bone track or spatial track. Is made to improve
-	 * code readability.
-	 * 
-	 * @author Marcin Roguski (Kaelthas)
-	 */
-	private static class TrackWrapper implements Track {
-		/** The spatial track. */
-		private SpatialTrack spatialTrack;
-		/** The bone track. */
-		private BoneTrack boneTrack;
-
-		/**
-		 * Constructs the object using the given track. The track must be of one of the types:
-		 * <li> BoneTrack
-		 * <li> SpatialTrack
-		 * 
-		 * @param track
-		 *            the animation track
-		 */
-		public TrackWrapper(Track track) {
-			if(track instanceof SpatialTrack) {
-				this.spatialTrack = (SpatialTrack)track;
-			} else if(track instanceof BoneTrack) {
-				this.boneTrack = (BoneTrack)track;
-			} else {
-				throw new IllegalStateException("Unknown track type!");
-			}
-		}
-
-		/**
-		 * @return the array of rotations of this track
-		 */
-		public Quaternion[] getRotations() {
-			if (boneTrack != null) {
-				return boneTrack.getRotations();
-			}
-			return spatialTrack.getRotations();
-		}
-
-		/**
-		 * @return the array of scales for this track
-		 */
-		public Vector3f[] getScales() {
-			if (boneTrack != null) {
-				return boneTrack.getScales();
-			}
-			return spatialTrack.getScales();
-		}
-
-		/**
-		 * @return the arrays of time for this track
-		 */
-		public float[] getTimes() {
-			if (boneTrack != null) {
-				return boneTrack.getTimes();
-			}
-			return spatialTrack.getTimes();
-		}
-
-		/**
-		 * @return the array of translations of this track
-		 */
-		public Vector3f[] getTranslations() {
-			if (boneTrack != null) {
-				return boneTrack.getTranslations();
-			}
-			return spatialTrack.getTranslations();
-		}
-
-		/**
-		 * Set the translations, rotations and scales for this bone track
-		 * 
-		 * @param times
-		 *            a float array with the time of each frame
-		 * @param translations
-		 *            the translation of the bone for each frame
-		 * @param rotations
-		 *            the rotation of the bone for each frame
-		 * @param scales
-		 *            the scale of the bone for each frame
-		 */
-		public void setKeyframes(float[] times, Vector3f[] translations,
-				Quaternion[] rotations, Vector3f[] scales) {
-			if (boneTrack != null) {
-				boneTrack.setKeyframes(times, translations, rotations, scales);
-			} else {
-				spatialTrack.setKeyframes(times, translations, rotations, scales);
-			}
-		}
-
-		public void write(JmeExporter ex) throws IOException { 
-			//no need to implement this one (the TrackWrapper is used internally and never serialized)
-		}
-
-		public void read(JmeImporter im) throws IOException {
-			//no need to implement this one (the TrackWrapper is used internally and never serialized)
-		}
-
-		public void setTime(float time, float weight, AnimControl control,
-				AnimChannel channel, TempVars vars) {
-			if (boneTrack != null) {
-				boneTrack.setTime(time, weight, control, channel, vars);
-			} else {
-				spatialTrack.setTime(time, weight, control, channel, vars);
-			}
-		}
-
-		public float getLength() {
-			return spatialTrack == null ? boneTrack.getLength() : spatialTrack
-					.getLength();
-		}
-
-		@Override
-		public TrackWrapper clone() {
-			if (boneTrack != null) {
-				return new TrackWrapper(boneTrack.clone());
-			}
-			return new TrackWrapper(spatialTrack.clone());
-		}
-	}
+    protected int flag;
+
+    public ConstraintDefinition(Structure constraintData, BlenderContext blenderContext) {
+        if (constraintData != null) {// Null constraint has no data
+            Number flag = (Number) constraintData.getFieldValue("flag");
+            if (flag != null) {
+                this.flag = flag.intValue();
+            }
+        }
+    }
+
+    public void bake(Transform ownerTransform, Transform targetTransform, Track ownerTrack, Track targetTrack, Ipo influenceIpo) {
+        TrackWrapper ownerWrapperTrack = ownerTrack != null ? new TrackWrapper(ownerTrack) : null;
+        TrackWrapper targetWrapperTrack = targetTrack != null ? new TrackWrapper(targetTrack) : null;
+
+        // uruchamiamy bake dla transformat zalenie od tego, ktre argumenty s nullami, a ktre - nie
+        this.bake(ownerTransform, targetTransform, influenceIpo.calculateValue(0));
+        if (ownerWrapperTrack != null) {
+            float[] ownerTimes = ownerWrapperTrack.getTimes();
+            Vector3f[] translations = ownerWrapperTrack.getTranslations();
+            Quaternion[] rotations = ownerWrapperTrack.getRotations();
+            Vector3f[] scales = ownerWrapperTrack.getScales();
+
+            float[] targetTimes = targetWrapperTrack == null ? null : targetWrapperTrack.getTimes();
+            Vector3f[] targetTranslations = targetWrapperTrack == null ? null : targetWrapperTrack.getTranslations();
+            Quaternion[] targetRotations = targetWrapperTrack == null ? null : targetWrapperTrack.getRotations();
+            Vector3f[] targetScales = targetWrapperTrack == null ? null : targetWrapperTrack.getScales();
+            Vector3f translation = new Vector3f(), scale = new Vector3f();
+            Quaternion rotation = new Quaternion();
+
+            Transform ownerTemp = new Transform(), targetTemp = new Transform();
+            for (int i = 0; i < ownerTimes.length; ++i) {
+                float t = ownerTimes[i];
+                ownerTemp.setTranslation(translations[i]);
+                ownerTemp.setRotation(rotations[i]);
+                ownerTemp.setScale(scales[i]);
+                if (targetWrapperTrack == null) {
+                    this.bake(ownerTemp, targetTransform, influenceIpo.calculateValue(i));
+                } else {
+                    // getting the values that are the interpolation of the target track for the time 't'
+                    this.interpolate(targetTranslations, targetTimes, t, translation);
+                    this.interpolate(targetRotations, targetTimes, t, rotation);
+                    this.interpolate(targetScales, targetTimes, t, scale);
+
+                    targetTemp.setTranslation(translation);
+                    targetTemp.setRotation(rotation);
+                    targetTemp.setScale(scale);
+
+                    this.bake(ownerTemp, targetTemp, influenceIpo.calculateValue(i));
+                }
+                // need to clone here because each of the arrays will reference the same instance if they hold the same value in the compact array
+                translations[i] = ownerTemp.getTranslation().clone();
+                rotations[i] = ownerTemp.getRotation().clone();
+                scales[i] = ownerTemp.getScale().clone();
+            }
+            ownerWrapperTrack.setKeyframes(ownerTimes, translations, rotations, scales);
+        }
+    }
+
+    protected abstract void bake(Transform ownerTransform, Transform targetTransform, float influence);
+
+    private void interpolate(Vector3f[] targetVectors, float[] targetTimes, float currentTime, Vector3f result) {
+        int index = 0;
+        for (int i = 1; i < targetTimes.length; ++i) {
+            if (targetTimes[i] < currentTime) {
+                ++index;
+            } else {
+                break;
+            }
+        }
+        if (index >= targetTimes.length - 1) {
+            result.set(targetVectors[targetTimes.length - 1]);
+        } else {
+            float delta = targetTimes[index + 1] - targetTimes[index];
+            if (delta == 0.0f) {
+                result.set(targetVectors[index + 1]);
+            } else {
+                float scale = (currentTime - targetTimes[index]) / (targetTimes[index + 1] - targetTimes[index]);
+                FastMath.interpolateLinear(scale, targetVectors[index], targetVectors[index + 1], result);
+            }
+        }
+    }
+
+    private void interpolate(Quaternion[] targetQuaternions, float[] targetTimes, float currentTime, Quaternion result) {
+        int index = 0;
+        for (int i = 1; i < targetTimes.length; ++i) {
+            if (targetTimes[i] < currentTime) {
+                ++index;
+            } else {
+                break;
+            }
+        }
+        if (index >= targetTimes.length - 1) {
+            result.set(targetQuaternions[targetTimes.length - 1]);
+        } else {
+            float delta = targetTimes[index + 1] - targetTimes[index];
+            if (delta == 0.0f) {
+                result.set(targetQuaternions[index + 1]);
+            } else {
+                float scale = (currentTime - targetTimes[index]) / (targetTimes[index + 1] - targetTimes[index]);
+                result.slerp(targetQuaternions[index], targetQuaternions[index + 1], scale);
+            }
+        }
+    }
+
+    /**
+     * This class holds either the bone track or spatial track. Is made to improve
+     * code readability.
+     * 
+     * @author Marcin Roguski (Kaelthas)
+     */
+    private static class TrackWrapper implements Track {
+        /** The spatial track. */
+        private SpatialTrack spatialTrack;
+        /** The bone track. */
+        private BoneTrack    boneTrack;
+
+        /**
+         * Constructs the object using the given track. The track must be of one of the types: <li>BoneTrack <li>SpatialTrack
+         * 
+         * @param track
+         *            the animation track
+         */
+        public TrackWrapper(Track track) {
+            if (track instanceof SpatialTrack) {
+                this.spatialTrack = (SpatialTrack) track;
+            } else if (track instanceof BoneTrack) {
+                this.boneTrack = (BoneTrack) track;
+            } else {
+                throw new IllegalStateException("Unknown track type!");
+            }
+        }
+
+        /**
+         * @return the array of rotations of this track
+         */
+        public Quaternion[] getRotations() {
+            if (boneTrack != null) {
+                return boneTrack.getRotations();
+            }
+            return spatialTrack.getRotations();
+        }
+
+        /**
+         * @return the array of scales for this track
+         */
+        public Vector3f[] getScales() {
+            if (boneTrack != null) {
+                return boneTrack.getScales();
+            }
+            return spatialTrack.getScales();
+        }
+
+        /**
+         * @return the arrays of time for this track
+         */
+        public float[] getTimes() {
+            if (boneTrack != null) {
+                return boneTrack.getTimes();
+            }
+            return spatialTrack.getTimes();
+        }
+
+        /**
+         * @return the array of translations of this track
+         */
+        public Vector3f[] getTranslations() {
+            if (boneTrack != null) {
+                return boneTrack.getTranslations();
+            }
+            return spatialTrack.getTranslations();
+        }
+
+        /**
+         * Set the translations, rotations and scales for this bone track
+         * 
+         * @param times
+         *            a float array with the time of each frame
+         * @param translations
+         *            the translation of the bone for each frame
+         * @param rotations
+         *            the rotation of the bone for each frame
+         * @param scales
+         *            the scale of the bone for each frame
+         */
+        public void setKeyframes(float[] times, Vector3f[] translations, Quaternion[] rotations, Vector3f[] scales) {
+            if (boneTrack != null) {
+                boneTrack.setKeyframes(times, translations, rotations, scales);
+            } else {
+                spatialTrack.setKeyframes(times, translations, rotations, scales);
+            }
+        }
+
+        public void write(JmeExporter ex) throws IOException {
+            // no need to implement this one (the TrackWrapper is used internally and never serialized)
+        }
+
+        public void read(JmeImporter im) throws IOException {
+            // no need to implement this one (the TrackWrapper is used internally and never serialized)
+        }
+
+        public void setTime(float time, float weight, AnimControl control, AnimChannel channel, TempVars vars) {
+            if (boneTrack != null) {
+                boneTrack.setTime(time, weight, control, channel, vars);
+            } else {
+                spatialTrack.setTime(time, weight, control, channel, vars);
+            }
+        }
+
+        public float getLength() {
+            return spatialTrack == null ? boneTrack.getLength() : spatialTrack.getLength();
+        }
+
+        @Override
+        public TrackWrapper clone() {
+            if (boneTrack != null) {
+                return new TrackWrapper(boneTrack.clone());
+            }
+            return new TrackWrapper(spatialTrack.clone());
+        }
+    }
 }

+ 11 - 11
engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionAction.java

@@ -11,16 +11,16 @@ import com.jme3.scene.plugins.blender.file.Structure;
  * This class represents 'Action' constraint type in blender.
  * @author Marcin Roguski (Kaelthas)
  */
-/*package*/ class ConstraintDefinitionAction extends ConstraintDefinition {
-	private static final Logger LOGGER = Logger.getLogger(ConstraintDefinitionAction.class.getName());
-	
-	public ConstraintDefinitionAction(Structure constraintData, BlenderContext blenderContext) {
-		super(constraintData, blenderContext);
-	}
+/* package */class ConstraintDefinitionAction extends ConstraintDefinition {
+    private static final Logger LOGGER = Logger.getLogger(ConstraintDefinitionAction.class.getName());
 
-	@Override
-	public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
-		// TODO: implement 'Action' constraint
-		LOGGER.log(Level.WARNING, "'Action' constraint NOT implemented!");
-	}
+    public ConstraintDefinitionAction(Structure constraintData, BlenderContext blenderContext) {
+        super(constraintData, blenderContext);
+    }
+
+    @Override
+    public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
+        // TODO: implement 'Action' constraint
+        LOGGER.log(Level.WARNING, "'Action' constraint NOT implemented!");
+    }
 }

+ 11 - 11
engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionCameraSolver.java

@@ -11,16 +11,16 @@ import com.jme3.scene.plugins.blender.file.Structure;
  * This class represents 'Camera solver' constraint type in blender.
  * @author Marcin Roguski (Kaelthas)
  */
-/*package*/ class ConstraintDefinitionCameraSolver extends ConstraintDefinition {
-	private static final Logger LOGGER = Logger.getLogger(ConstraintDefinitionAction.class.getName());
-	
-	public ConstraintDefinitionCameraSolver(Structure constraintData, BlenderContext blenderContext) {
-		super(constraintData, blenderContext);
-	}
+/* package */class ConstraintDefinitionCameraSolver extends ConstraintDefinition {
+    private static final Logger LOGGER = Logger.getLogger(ConstraintDefinitionAction.class.getName());
 
-	@Override
-	public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
-		// TODO: implement 'Camera solver' constraint
-		LOGGER.log(Level.WARNING, "'Camera solver' constraint NOT implemented!");
-	}
+    public ConstraintDefinitionCameraSolver(Structure constraintData, BlenderContext blenderContext) {
+        super(constraintData, blenderContext);
+    }
+
+    @Override
+    public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
+        // TODO: implement 'Camera solver' constraint
+        LOGGER.log(Level.WARNING, "'Camera solver' constraint NOT implemented!");
+    }
 }

+ 12 - 12
engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionChildOf.java

@@ -11,16 +11,16 @@ import com.jme3.scene.plugins.blender.file.Structure;
  * This class represents 'ChildOf' constraint type in blender.
  * @author Marcin Roguski (Kaelthas)
  */
-/*package*/ class ConstraintDefinitionChildOf extends ConstraintDefinition {
-	private static final Logger LOGGER = Logger.getLogger(ConstraintDefinitionChildOf.class.getName());
-	
-	public ConstraintDefinitionChildOf(Structure constraintData, BlenderContext blenderContext) {
-		super(constraintData, blenderContext);
-	}
-	
-	@Override
-	public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
-		// TODO: implement ChildOf constraint
-		LOGGER.log(Level.WARNING, "ChildOf constraint NOT implemented!");
-	}
+/* package */class ConstraintDefinitionChildOf extends ConstraintDefinition {
+    private static final Logger LOGGER = Logger.getLogger(ConstraintDefinitionChildOf.class.getName());
+
+    public ConstraintDefinitionChildOf(Structure constraintData, BlenderContext blenderContext) {
+        super(constraintData, blenderContext);
+    }
+
+    @Override
+    public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
+        // TODO: implement ChildOf constraint
+        LOGGER.log(Level.WARNING, "ChildOf constraint NOT implemented!");
+    }
 }

+ 12 - 12
engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionClampTo.java

@@ -11,16 +11,16 @@ import com.jme3.scene.plugins.blender.file.Structure;
  * This class represents 'Clamp to' constraint type in blender.
  * @author Marcin Roguski (Kaelthas)
  */
-/*package*/ class ConstraintDefinitionClampTo extends ConstraintDefinition {
-	private static final Logger LOGGER = Logger.getLogger(ConstraintDefinitionClampTo.class.getName());
-	
-	public ConstraintDefinitionClampTo(Structure constraintData, BlenderContext blenderContext) {
-		super(constraintData, blenderContext);
-	}
-	
-	@Override
-	public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
-		//TODO: implement when curves are implemented
-		LOGGER.log(Level.WARNING, "'Clamp to' not yet implemented!");
-	}
+/* package */class ConstraintDefinitionClampTo extends ConstraintDefinition {
+    private static final Logger LOGGER = Logger.getLogger(ConstraintDefinitionClampTo.class.getName());
+
+    public ConstraintDefinitionClampTo(Structure constraintData, BlenderContext blenderContext) {
+        super(constraintData, blenderContext);
+    }
+
+    @Override
+    public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
+        // TODO: implement when curves are implemented
+        LOGGER.log(Level.WARNING, "'Clamp to' not yet implemented!");
+    }
 }

+ 12 - 12
engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionDampTrack.java

@@ -11,16 +11,16 @@ import com.jme3.scene.plugins.blender.file.Structure;
  * The damp track constraint. Available for blender 2.50+.
  * @author Marcin Roguski (Kaelthas)
  */
-/*package*/ class ConstraintDefinitionDampTrack extends ConstraintDefinition {
-	private static final Logger LOGGER = Logger.getLogger(ConstraintDefinitionDampTrack.class.getName());
-	
-	public ConstraintDefinitionDampTrack(Structure constraintData, BlenderContext blenderContext) {
-		super(constraintData, blenderContext);
-	}
-	
-	@Override
-	public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
-		// TODO Auto-generated method stub
-		LOGGER.log(Level.WARNING, "'Damp Track' constraint NOT implemented!");
-	}
+/* package */class ConstraintDefinitionDampTrack extends ConstraintDefinition {
+    private static final Logger LOGGER = Logger.getLogger(ConstraintDefinitionDampTrack.class.getName());
+
+    public ConstraintDefinitionDampTrack(Structure constraintData, BlenderContext blenderContext) {
+        super(constraintData, blenderContext);
+    }
+
+    @Override
+    public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
+        // TODO Auto-generated method stub
+        LOGGER.log(Level.WARNING, "'Damp Track' constraint NOT implemented!");
+    }
 }

+ 47 - 47
engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionDistLimit.java

@@ -9,51 +9,51 @@ import com.jme3.scene.plugins.blender.file.Structure;
  * This class represents 'Dist limit' constraint type in blender.
  * @author Marcin Roguski (Kaelthas)
  */
-/*package*/ class ConstraintDefinitionDistLimit extends ConstraintDefinition {
-	private static final int LIMITDIST_INSIDE = 0;
-	private static final int LIMITDIST_OUTSIDE = 1;
-	private static final int LIMITDIST_ONSURFACE = 2;
-    
-	protected int mode;
-	protected float dist;
-	
-	public ConstraintDefinitionDistLimit(Structure constraintData, BlenderContext blenderContext) {
-		super(constraintData, blenderContext);
-		mode = ((Number) constraintData.getFieldValue("mode")).intValue();
-		dist = ((Number) constraintData.getFieldValue("dist")).floatValue();
-	}
-	
-	@Override
-	public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
-		Vector3f v = ownerTransform.getTranslation().subtract(targetTransform.getTranslation());
-		float currentDistance = v.length();
-		
-		switch (mode) {
-			case LIMITDIST_INSIDE:
-				if (currentDistance >= dist) {
-					v.normalizeLocal();
-					v.multLocal(dist + (currentDistance - dist) * (1.0f - influence));
-					ownerTransform.getTranslation().set(v.addLocal(targetTransform.getTranslation()));
-				}
-				break;
-			case LIMITDIST_ONSURFACE:
-				if (currentDistance > dist) {
-					v.normalizeLocal();
-					v.multLocal(dist + (currentDistance - dist) * (1.0f - influence));
-					ownerTransform.getTranslation().set(v.addLocal(targetTransform.getTranslation()));
-				} else if(currentDistance < dist) {
-					v.normalizeLocal().multLocal(dist * influence);
-					ownerTransform.getTranslation().set(targetTransform.getTranslation().add(v));
-				}
-				break;
-			case LIMITDIST_OUTSIDE:
-				if (currentDistance <= dist) {
-					v = targetTransform.getTranslation().subtract(ownerTransform.getTranslation()).normalizeLocal().multLocal(dist * influence);
-					ownerTransform.getTranslation().set(targetTransform.getTranslation().add(v));
-				}
-				break;
-			default:
-				throw new IllegalStateException("Unknown distance limit constraint mode: " + mode);
-		}
-	}
+/* package */class ConstraintDefinitionDistLimit extends ConstraintDefinition {
+    private static final int LIMITDIST_INSIDE    = 0;
+    private static final int LIMITDIST_OUTSIDE   = 1;
+    private static final int LIMITDIST_ONSURFACE = 2;
+
+    protected int            mode;
+    protected float          dist;
+
+    public ConstraintDefinitionDistLimit(Structure constraintData, BlenderContext blenderContext) {
+        super(constraintData, blenderContext);
+        mode = ((Number) constraintData.getFieldValue("mode")).intValue();
+        dist = ((Number) constraintData.getFieldValue("dist")).floatValue();
+    }
+
+    @Override
+    public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
+        Vector3f v = ownerTransform.getTranslation().subtract(targetTransform.getTranslation());
+        float currentDistance = v.length();
+
+        switch (mode) {
+            case LIMITDIST_INSIDE:
+                if (currentDistance >= dist) {
+                    v.normalizeLocal();
+                    v.multLocal(dist + (currentDistance - dist) * (1.0f - influence));
+                    ownerTransform.getTranslation().set(v.addLocal(targetTransform.getTranslation()));
+                }
+                break;
+            case LIMITDIST_ONSURFACE:
+                if (currentDistance > dist) {
+                    v.normalizeLocal();
+                    v.multLocal(dist + (currentDistance - dist) * (1.0f - influence));
+                    ownerTransform.getTranslation().set(v.addLocal(targetTransform.getTranslation()));
+                } else if (currentDistance < dist) {
+                    v.normalizeLocal().multLocal(dist * influence);
+                    ownerTransform.getTranslation().set(targetTransform.getTranslation().add(v));
+                }
+                break;
+            case LIMITDIST_OUTSIDE:
+                if (currentDistance <= dist) {
+                    v = targetTransform.getTranslation().subtract(ownerTransform.getTranslation()).normalizeLocal().multLocal(dist * influence);
+                    ownerTransform.getTranslation().set(targetTransform.getTranslation().add(v));
+                }
+                break;
+            default:
+                throw new IllegalStateException("Unknown distance limit constraint mode: " + mode);
+        }
+    }
 }

+ 71 - 71
engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionFactory.java

@@ -40,75 +40,75 @@ import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
 import com.jme3.scene.plugins.blender.file.Structure;
 
 public class ConstraintDefinitionFactory {
-	private static final Map<String, Class<? extends ConstraintDefinition>> CONSTRAINT_CLASSES = new HashMap<String, Class<? extends ConstraintDefinition>>();
-	static {
-		CONSTRAINT_CLASSES.put("bActionConstraint", ConstraintDefinitionAction.class);
-		CONSTRAINT_CLASSES.put("bChildOfConstraint", ConstraintDefinitionChildOf.class);
-		CONSTRAINT_CLASSES.put("bClampToConstraint", ConstraintDefinitionClampTo.class);
-		CONSTRAINT_CLASSES.put("bDistLimitConstraint", ConstraintDefinitionDistLimit.class);
-		CONSTRAINT_CLASSES.put("bFollowPathConstraint", ConstraintDefinitionFollowPath.class);
-		CONSTRAINT_CLASSES.put("bKinematicConstraint", ConstraintDefinitionInverseKinematics.class);
-		CONSTRAINT_CLASSES.put("bLockTrackConstraint", ConstraintDefinitionLockTrack.class);
-		CONSTRAINT_CLASSES.put("bLocateLikeConstraint", ConstraintDefinitionLocLike.class);
-		CONSTRAINT_CLASSES.put("bLocLimitConstraint", ConstraintDefinitionLocLimit.class);
-		CONSTRAINT_CLASSES.put("bMinMaxConstraint", ConstraintDefinitionMinMax.class);
-		CONSTRAINT_CLASSES.put("bNullConstraint", ConstraintDefinitionNull.class);
-		CONSTRAINT_CLASSES.put("bPythonConstraint", ConstraintDefinitionPython.class);
-		CONSTRAINT_CLASSES.put("bRigidBodyJointConstraint", ConstraintDefinitionRigidBodyJoint.class);
-		CONSTRAINT_CLASSES.put("bRotateLikeConstraint", ConstraintDefinitionRotLike.class);
-		CONSTRAINT_CLASSES.put("bShrinkWrapConstraint", ConstraintDefinitionShrinkWrap.class);
-		CONSTRAINT_CLASSES.put("bSizeLikeConstraint", ConstraintDefinitionSizeLike.class);
-		CONSTRAINT_CLASSES.put("bSizeLimitConstraint", ConstraintDefinitionSizeLimit.class);
-		CONSTRAINT_CLASSES.put("bStretchToConstraint", ConstraintDefinitionStretchTo.class);
-		CONSTRAINT_CLASSES.put("bTransformConstraint", ConstraintDefinitionTransform.class);
-		CONSTRAINT_CLASSES.put("bRotLimitConstraint", ConstraintDefinitionRotLimit.class);
-		//Blender 2.50+
-		CONSTRAINT_CLASSES.put("bSplineIKConstraint", ConstraintDefinitionSplineInverseKinematic.class);
-		CONSTRAINT_CLASSES.put("bDampTrackConstraint", ConstraintDefinitionDampTrack.class);
-		CONSTRAINT_CLASSES.put("bPivotConstraint", ConstraintDefinitionDampTrack.class);
-		//Blender 2.56+
-		CONSTRAINT_CLASSES.put("bTrackToConstraint", ConstraintDefinitionTrackTo.class);
-		CONSTRAINT_CLASSES.put("bSameVolumeConstraint", ConstraintDefinitionSameVolume.class);
-		CONSTRAINT_CLASSES.put("bTransLikeConstraint", ConstraintDefinitionTransLike.class);
-		//Blender 2.62+
-		CONSTRAINT_CLASSES.put("bCameraSolverConstraint", ConstraintDefinitionCameraSolver.class);
-		CONSTRAINT_CLASSES.put("bObjectSolverConstraint", ConstraintDefinitionObjectSolver.class);
-		CONSTRAINT_CLASSES.put("bFollowTrackConstraint", ConstraintDefinitionFollowTrack.class);
-	}
-	
-	/**
-	 * This method creates the constraint instance.
-	 * 
-	 * @param constraintStructure
-	 *            the constraint's structure (bConstraint clss in blender 2.49). If the value is null the NullConstraint is created.
-	 * @param blenderContext
-	 *            the blender context
-	 * @throws BlenderFileException
-	 *             this exception is thrown when the blender file is somehow
-	 *             corrupted
-	 */
-	public static ConstraintDefinition createConstraintDefinition(Structure constraintStructure, BlenderContext blenderContext) throws BlenderFileException {
-		if(constraintStructure == null) {
-			return new ConstraintDefinitionNull(null, blenderContext);
-		}
-		String constraintClassName = constraintStructure.getType();
-		Class<? extends ConstraintDefinition> constraintDefinitionClass = CONSTRAINT_CLASSES.get(constraintClassName);
-		if(constraintDefinitionClass != null) {
-			try {
-				return (ConstraintDefinition) constraintDefinitionClass.getDeclaredConstructors()[0].newInstance(constraintStructure, blenderContext);
-			} catch (IllegalArgumentException e) {
-				throw new BlenderFileException(e.getLocalizedMessage(), e);
-			} catch (SecurityException e) {
-				throw new BlenderFileException(e.getLocalizedMessage(), e);
-			} catch (InstantiationException e) {
-				throw new BlenderFileException(e.getLocalizedMessage(), e);
-			} catch (IllegalAccessException e) {
-				throw new BlenderFileException(e.getLocalizedMessage(), e);
-			} catch (InvocationTargetException e) {
-				throw new BlenderFileException(e.getLocalizedMessage(), e);
-			}
-		} else {
-			throw new BlenderFileException("Unknown constraint type: " + constraintClassName);
-		}
-	}
+    private static final Map<String, Class<? extends ConstraintDefinition>> CONSTRAINT_CLASSES = new HashMap<String, Class<? extends ConstraintDefinition>>();
+    static {
+        CONSTRAINT_CLASSES.put("bActionConstraint", ConstraintDefinitionAction.class);
+        CONSTRAINT_CLASSES.put("bChildOfConstraint", ConstraintDefinitionChildOf.class);
+        CONSTRAINT_CLASSES.put("bClampToConstraint", ConstraintDefinitionClampTo.class);
+        CONSTRAINT_CLASSES.put("bDistLimitConstraint", ConstraintDefinitionDistLimit.class);
+        CONSTRAINT_CLASSES.put("bFollowPathConstraint", ConstraintDefinitionFollowPath.class);
+        CONSTRAINT_CLASSES.put("bKinematicConstraint", ConstraintDefinitionInverseKinematics.class);
+        CONSTRAINT_CLASSES.put("bLockTrackConstraint", ConstraintDefinitionLockTrack.class);
+        CONSTRAINT_CLASSES.put("bLocateLikeConstraint", ConstraintDefinitionLocLike.class);
+        CONSTRAINT_CLASSES.put("bLocLimitConstraint", ConstraintDefinitionLocLimit.class);
+        CONSTRAINT_CLASSES.put("bMinMaxConstraint", ConstraintDefinitionMinMax.class);
+        CONSTRAINT_CLASSES.put("bNullConstraint", ConstraintDefinitionNull.class);
+        CONSTRAINT_CLASSES.put("bPythonConstraint", ConstraintDefinitionPython.class);
+        CONSTRAINT_CLASSES.put("bRigidBodyJointConstraint", ConstraintDefinitionRigidBodyJoint.class);
+        CONSTRAINT_CLASSES.put("bRotateLikeConstraint", ConstraintDefinitionRotLike.class);
+        CONSTRAINT_CLASSES.put("bShrinkWrapConstraint", ConstraintDefinitionShrinkWrap.class);
+        CONSTRAINT_CLASSES.put("bSizeLikeConstraint", ConstraintDefinitionSizeLike.class);
+        CONSTRAINT_CLASSES.put("bSizeLimitConstraint", ConstraintDefinitionSizeLimit.class);
+        CONSTRAINT_CLASSES.put("bStretchToConstraint", ConstraintDefinitionStretchTo.class);
+        CONSTRAINT_CLASSES.put("bTransformConstraint", ConstraintDefinitionTransform.class);
+        CONSTRAINT_CLASSES.put("bRotLimitConstraint", ConstraintDefinitionRotLimit.class);
+        // Blender 2.50+
+        CONSTRAINT_CLASSES.put("bSplineIKConstraint", ConstraintDefinitionSplineInverseKinematic.class);
+        CONSTRAINT_CLASSES.put("bDampTrackConstraint", ConstraintDefinitionDampTrack.class);
+        CONSTRAINT_CLASSES.put("bPivotConstraint", ConstraintDefinitionDampTrack.class);
+        // Blender 2.56+
+        CONSTRAINT_CLASSES.put("bTrackToConstraint", ConstraintDefinitionTrackTo.class);
+        CONSTRAINT_CLASSES.put("bSameVolumeConstraint", ConstraintDefinitionSameVolume.class);
+        CONSTRAINT_CLASSES.put("bTransLikeConstraint", ConstraintDefinitionTransLike.class);
+        // Blender 2.62+
+        CONSTRAINT_CLASSES.put("bCameraSolverConstraint", ConstraintDefinitionCameraSolver.class);
+        CONSTRAINT_CLASSES.put("bObjectSolverConstraint", ConstraintDefinitionObjectSolver.class);
+        CONSTRAINT_CLASSES.put("bFollowTrackConstraint", ConstraintDefinitionFollowTrack.class);
+    }
+
+    /**
+     * This method creates the constraint instance.
+     * 
+     * @param constraintStructure
+     *            the constraint's structure (bConstraint clss in blender 2.49). If the value is null the NullConstraint is created.
+     * @param blenderContext
+     *            the blender context
+     * @throws BlenderFileException
+     *             this exception is thrown when the blender file is somehow
+     *             corrupted
+     */
+    public static ConstraintDefinition createConstraintDefinition(Structure constraintStructure, BlenderContext blenderContext) throws BlenderFileException {
+        if (constraintStructure == null) {
+            return new ConstraintDefinitionNull(null, blenderContext);
+        }
+        String constraintClassName = constraintStructure.getType();
+        Class<? extends ConstraintDefinition> constraintDefinitionClass = CONSTRAINT_CLASSES.get(constraintClassName);
+        if (constraintDefinitionClass != null) {
+            try {
+                return (ConstraintDefinition) constraintDefinitionClass.getDeclaredConstructors()[0].newInstance(constraintStructure, blenderContext);
+            } catch (IllegalArgumentException e) {
+                throw new BlenderFileException(e.getLocalizedMessage(), e);
+            } catch (SecurityException e) {
+                throw new BlenderFileException(e.getLocalizedMessage(), e);
+            } catch (InstantiationException e) {
+                throw new BlenderFileException(e.getLocalizedMessage(), e);
+            } catch (IllegalAccessException e) {
+                throw new BlenderFileException(e.getLocalizedMessage(), e);
+            } catch (InvocationTargetException e) {
+                throw new BlenderFileException(e.getLocalizedMessage(), e);
+            }
+        } else {
+            throw new BlenderFileException("Unknown constraint type: " + constraintClassName);
+        }
+    }
 }

+ 12 - 12
engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionFollowPath.java

@@ -11,16 +11,16 @@ import com.jme3.scene.plugins.blender.file.Structure;
  * This class represents 'Follow path' constraint type in blender.
  * @author Marcin Roguski (Kaelthas)
  */
-/*package*/ class ConstraintDefinitionFollowPath extends ConstraintDefinition {
-	private static final Logger LOGGER = Logger.getLogger(ConstraintDefinitionFollowPath.class.getName());
-	
-	public ConstraintDefinitionFollowPath(Structure constraintData, BlenderContext blenderContext) {
-		super(constraintData, blenderContext);
-	}
-	
-	@Override
-	public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
-		//TODO: implement when curves are implemented
-		LOGGER.log(Level.WARNING, "'Follow path' not implemented! Curves not yet implemented!");
-	}
+/* package */class ConstraintDefinitionFollowPath extends ConstraintDefinition {
+    private static final Logger LOGGER = Logger.getLogger(ConstraintDefinitionFollowPath.class.getName());
+
+    public ConstraintDefinitionFollowPath(Structure constraintData, BlenderContext blenderContext) {
+        super(constraintData, blenderContext);
+    }
+
+    @Override
+    public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
+        // TODO: implement when curves are implemented
+        LOGGER.log(Level.WARNING, "'Follow path' not implemented! Curves not yet implemented!");
+    }
 }

+ 11 - 11
engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionFollowTrack.java

@@ -11,16 +11,16 @@ import com.jme3.scene.plugins.blender.file.Structure;
  * This class represents 'Follow track' constraint type in blender.
  * @author Marcin Roguski (Kaelthas)
  */
-/*package*/ class ConstraintDefinitionFollowTrack extends ConstraintDefinition {
-	private static final Logger LOGGER = Logger.getLogger(ConstraintDefinitionAction.class.getName());
-	
-	public ConstraintDefinitionFollowTrack(Structure constraintData, BlenderContext blenderContext) {
-		super(constraintData, blenderContext);
-	}
+/* package */class ConstraintDefinitionFollowTrack extends ConstraintDefinition {
+    private static final Logger LOGGER = Logger.getLogger(ConstraintDefinitionAction.class.getName());
 
-	@Override
-	public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
-		// TODO: implement 'Follow track' constraint
-		LOGGER.log(Level.WARNING, "'Follow track' constraint NOT implemented!");
-	}
+    public ConstraintDefinitionFollowTrack(Structure constraintData, BlenderContext blenderContext) {
+        super(constraintData, blenderContext);
+    }
+
+    @Override
+    public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
+        // TODO: implement 'Follow track' constraint
+        LOGGER.log(Level.WARNING, "'Follow track' constraint NOT implemented!");
+    }
 }

+ 129 - 129
engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionInverseKinematics.java

@@ -11,134 +11,134 @@ import com.jme3.scene.plugins.blender.file.Structure;
  * This class represents 'Inverse kinematics' constraint type in blender.
  * @author Marcin Roguski (Kaelthas)
  */
-/*package*/ class ConstraintDefinitionInverseKinematics extends ConstraintDefinition {
-	//private static final Logger LOGGER = Logger.getLogger(ConstraintDefinitionInverseKinematics.class.getName());
-	//private static final float IK_SOLVER_ERROR = 0.5f;
-	
-	public ConstraintDefinitionInverseKinematics(Structure constraintData, BlenderContext blenderContext) {
-		super(constraintData, blenderContext);
-	}
+/* package */class ConstraintDefinitionInverseKinematics extends ConstraintDefinition {
+    // private static final Logger LOGGER = Logger.getLogger(ConstraintDefinitionInverseKinematics.class.getName());
+    // private static final float IK_SOLVER_ERROR = 0.5f;
 
-	@Override
-	public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
-//		try {
-			// IK solver is only attached to bones
-//			Bone ownerBone = (Bone) blenderContext.getLoadedFeature(ownerOMA, LoadedFeatureDataType.LOADED_FEATURE);
-//			AnimData animData = blenderContext.getAnimData(ownerOMA);
-//			if(animData == null) {
-				//TODO: to nie moxe byx null, utworzyx dane bez ruchu, w zalexnoxci czy target six rusza
-//			}
-			
-			//prepare a list of all parents of this bone
-//			CalculationBone[] bones = this.getBonesToCalculate(skeleton, boneAnimation);
-			
-			// get the target point
-//			Object targetObject = this.getTarget(LoadedFeatureDataType.LOADED_FEATURE);
-//			Vector3f pt = null;// Point Target
-//			if (targetObject instanceof Bone) {
-//				pt = ((Bone) targetObject).getModelSpacePosition();
-//			} else if (targetObject instanceof Spatial) {
-//				pt = ((Spatial) targetObject).getWorldTranslation();
-//			} else if (targetObject instanceof Skeleton) {
-//				Structure armatureNodeStructure = (Structure) this.getTarget(LoadedFeatureDataType.LOADED_STRUCTURE);
-//				ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class);
-//				Transform transform = objectHelper.getTransformation(armatureNodeStructure, blenderContext);
-//				pt = transform.getTranslation();
-//			} else {
-//				throw new IllegalStateException(
-//						"Unknown target object type! Should be Node, Bone or Skeleton and there is: "
-//						+ targetObject.getClass().getName());
-//			}
-			
-			//fetching the owner's bone track
-//			BoneTrack ownerBoneTrack = null;
-//			int boneIndex = skeleton.getBoneIndex(ownerBone);
-//			for (int i = 0; i < boneAnimation.getTracks().length; ++i) {
-//				if (boneAnimation.getTracks()[i].getTargetBoneIndex() == boneIndex) {
-//					ownerBoneTrack = boneAnimation.getTracks()[i];
-//					break;
-//				}
-//			}
-//			int ownerBoneFramesCount = ownerBoneTrack==null ? 0 : ownerBoneTrack.getTimes().length;
-//			
-//			// preparing data
-//			int maxIterations = ((Number) data.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();
-//			//all tracks should have the same amount of frames
-//			int framesCount = bones[0].getBoneFramesCount();
-//			assert framesCount >=1;
-//			for (int frame = 0; frame < framesCount; ++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;
-//				}
-//			}
-//
-//			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("===============================");
-//			}
-//		} catch(BlenderFileException e) {
-//			LOGGER.severe(e.getLocalizedMessage());
-//		}
-	}
-	
-	/**
-	 * 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(Skeleton skeleton, Animation boneAnimation) {
-//		Bone ownerBone = (Bone) blenderContext.getLoadedFeature(ownerOMA, LoadedFeatureDataType.LOADED_FEATURE);
-//		List<CalculationBone> bonesList = new ArrayList<CalculationBone>();
-//		do {
-//			bonesList.add(new CalculationBone(ownerBone, 1));
-//			int boneIndex = skeleton.getBoneIndex(ownerBone);
-//			for (int i = 0; i < boneAnimation.getTracks().length; ++i) {
-//				if (((BoneTrack[])boneAnimation.getTracks())[i].getTargetBoneIndex() == boneIndex) {
-//					bonesList.add(new CalculationBone(ownerBone, (BoneTrack)boneAnimation.getTracks()[i]));
-//					break;
-//				}
-//			}
-//			ownerBone = ownerBone.getParent();
-//		} while (ownerBone != 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;
-		return null;
-	}
+    public ConstraintDefinitionInverseKinematics(Structure constraintData, BlenderContext blenderContext) {
+        super(constraintData, blenderContext);
+    }
+
+    @Override
+    public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
+        // try {
+        // IK solver is only attached to bones
+        // Bone ownerBone = (Bone) blenderContext.getLoadedFeature(ownerOMA, LoadedFeatureDataType.LOADED_FEATURE);
+        // AnimData animData = blenderContext.getAnimData(ownerOMA);
+        // if(animData == null) {
+        // TODO: to nie moxe byx null, utworzyx dane bez ruchu, w zalexnoxci czy target six rusza
+        // }
+
+        // prepare a list of all parents of this bone
+        // CalculationBone[] bones = this.getBonesToCalculate(skeleton, boneAnimation);
+
+        // get the target point
+        // Object targetObject = this.getTarget(LoadedFeatureDataType.LOADED_FEATURE);
+        // Vector3f pt = null;// Point Target
+        // if (targetObject instanceof Bone) {
+        // pt = ((Bone) targetObject).getModelSpacePosition();
+        // } else if (targetObject instanceof Spatial) {
+        // pt = ((Spatial) targetObject).getWorldTranslation();
+        // } else if (targetObject instanceof Skeleton) {
+        // Structure armatureNodeStructure = (Structure) this.getTarget(LoadedFeatureDataType.LOADED_STRUCTURE);
+        // ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class);
+        // Transform transform = objectHelper.getTransformation(armatureNodeStructure, blenderContext);
+        // pt = transform.getTranslation();
+        // } else {
+        // throw new IllegalStateException(
+        // "Unknown target object type! Should be Node, Bone or Skeleton and there is: "
+        // + targetObject.getClass().getName());
+        // }
+
+        // fetching the owner's bone track
+        // BoneTrack ownerBoneTrack = null;
+        // int boneIndex = skeleton.getBoneIndex(ownerBone);
+        // for (int i = 0; i < boneAnimation.getTracks().length; ++i) {
+        // if (boneAnimation.getTracks()[i].getTargetBoneIndex() == boneIndex) {
+        // ownerBoneTrack = boneAnimation.getTracks()[i];
+        // break;
+        // }
+        // }
+        // int ownerBoneFramesCount = ownerBoneTrack==null ? 0 : ownerBoneTrack.getTimes().length;
+        //
+        // // preparing data
+        // int maxIterations = ((Number) data.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();
+        // //all tracks should have the same amount of frames
+        // int framesCount = bones[0].getBoneFramesCount();
+        // assert framesCount >=1;
+        // for (int frame = 0; frame < framesCount; ++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;
+        // }
+        // }
+        //
+        // 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("===============================");
+        // }
+        // } catch(BlenderFileException e) {
+        // LOGGER.severe(e.getLocalizedMessage());
+        // }
+    }
+
+    /**
+     * 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(Skeleton skeleton, Animation boneAnimation) {
+        // Bone ownerBone = (Bone) blenderContext.getLoadedFeature(ownerOMA, LoadedFeatureDataType.LOADED_FEATURE);
+        // List<CalculationBone> bonesList = new ArrayList<CalculationBone>();
+        // do {
+        // bonesList.add(new CalculationBone(ownerBone, 1));
+        // int boneIndex = skeleton.getBoneIndex(ownerBone);
+        // for (int i = 0; i < boneAnimation.getTracks().length; ++i) {
+        // if (((BoneTrack[])boneAnimation.getTracks())[i].getTargetBoneIndex() == boneIndex) {
+        // bonesList.add(new CalculationBone(ownerBone, (BoneTrack)boneAnimation.getTracks()[i]));
+        // break;
+        // }
+        // }
+        // ownerBone = ownerBone.getParent();
+        // } while (ownerBone != 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;
+        return null;
+    }
 }

+ 57 - 57
engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionLocLike.java

@@ -9,66 +9,66 @@ import com.jme3.scene.plugins.blender.file.Structure;
  * This class represents 'Loc like' constraint type in blender.
  * @author Marcin Roguski (Kaelthas)
  */
-/*package*/ class ConstraintDefinitionLocLike extends ConstraintDefinition {
-	private static final int LOCLIKE_X = 0x01;
-	private static final int LOCLIKE_Y = 0x02;
-	private static final int LOCLIKE_Z = 0x04;
-    //protected static final int LOCLIKE_TIP = 0x08;//this is deprecated in blender
+/* package */class ConstraintDefinitionLocLike extends ConstraintDefinition {
+    private static final int LOCLIKE_X        = 0x01;
+    private static final int LOCLIKE_Y        = 0x02;
+    private static final int LOCLIKE_Z        = 0x04;
+    // protected static final int LOCLIKE_TIP = 0x08;//this is deprecated in blender
     private static final int LOCLIKE_X_INVERT = 0x10;
     private static final int LOCLIKE_Y_INVERT = 0x20;
     private static final int LOCLIKE_Z_INVERT = 0x40;
-    private static final int LOCLIKE_OFFSET = 0x80;
-    
+    private static final int LOCLIKE_OFFSET   = 0x80;
+
     public ConstraintDefinitionLocLike(Structure constraintData, BlenderContext blenderContext) {
-		super(constraintData, blenderContext);
-		if(blenderContext.getBlenderKey().isFixUpAxis()) {
-			//swapping Y and X limits flag in the bitwise flag
-			int y = flag & LOCLIKE_Y;
-			int invY = flag & LOCLIKE_Y_INVERT;
-			int z = flag & LOCLIKE_Z;
-			int invZ = flag & LOCLIKE_Z_INVERT;
-			flag &= LOCLIKE_X | LOCLIKE_X_INVERT | LOCLIKE_OFFSET;//clear the other flags to swap them
-			flag |= y << 1;
-			flag |= invY << 1;
-			flag |= z >> 1;
-			flag |= invZ >> 1;
-		}
-	}
-    
-	@Override
-	public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
-		Vector3f ownerLocation = ownerTransform.getTranslation();
-		Vector3f targetLocation = targetTransform.getTranslation();
-		
-		Vector3f startLocation = ownerTransform.getTranslation().clone();
-		Vector3f offset = Vector3f.ZERO;
-		if ((flag & LOCLIKE_OFFSET) != 0) {//we add the original location to the copied location
-			offset = startLocation;
-		}
+        super(constraintData, blenderContext);
+        if (blenderContext.getBlenderKey().isFixUpAxis()) {
+            // swapping Y and X limits flag in the bitwise flag
+            int y = flag & LOCLIKE_Y;
+            int invY = flag & LOCLIKE_Y_INVERT;
+            int z = flag & LOCLIKE_Z;
+            int invZ = flag & LOCLIKE_Z_INVERT;
+            flag &= LOCLIKE_X | LOCLIKE_X_INVERT | LOCLIKE_OFFSET;// clear the other flags to swap them
+            flag |= y << 1;
+            flag |= invY << 1;
+            flag |= z >> 1;
+            flag |= invZ >> 1;
+        }
+    }
+
+    @Override
+    public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
+        Vector3f ownerLocation = ownerTransform.getTranslation();
+        Vector3f targetLocation = targetTransform.getTranslation();
+
+        Vector3f startLocation = ownerTransform.getTranslation().clone();
+        Vector3f offset = Vector3f.ZERO;
+        if ((flag & LOCLIKE_OFFSET) != 0) {// we add the original location to the copied location
+            offset = startLocation;
+        }
+
+        if ((flag & LOCLIKE_X) != 0) {
+            ownerLocation.x = targetLocation.x;
+            if ((flag & LOCLIKE_X_INVERT) != 0) {
+                ownerLocation.x = -ownerLocation.x;
+            }
+        }
+        if ((flag & LOCLIKE_Y) != 0) {
+            ownerLocation.y = targetLocation.y;
+            if ((flag & LOCLIKE_Y_INVERT) != 0) {
+                ownerLocation.y = -ownerLocation.y;
+            }
+        }
+        if ((flag & LOCLIKE_Z) != 0) {
+            ownerLocation.z = targetLocation.z;
+            if ((flag & LOCLIKE_Z_INVERT) != 0) {
+                ownerLocation.z = -ownerLocation.z;
+            }
+        }
+        ownerLocation.addLocal(offset);
 
-		if ((flag & LOCLIKE_X) != 0) {
-			ownerLocation.x = targetLocation.x;
-			if ((flag & LOCLIKE_X_INVERT) != 0) {
-				ownerLocation.x = -ownerLocation.x;
-			}
-		}
-		if ((flag & LOCLIKE_Y) != 0) {
-			ownerLocation.y = targetLocation.y;
-			if ((flag & LOCLIKE_Y_INVERT) != 0) {
-				ownerLocation.y = -ownerLocation.y;
-			}
-		}
-		if ((flag & LOCLIKE_Z) != 0) {
-			ownerLocation.z = targetLocation.z;
-			if ((flag & LOCLIKE_Z_INVERT) != 0) {
-				ownerLocation.z = -ownerLocation.z;
-			}
-		}
-		ownerLocation.addLocal(offset);
-		
-		if(influence < 1.0f) {
-			startLocation.subtractLocal(ownerLocation).normalizeLocal().mult(influence);
-			ownerLocation.addLocal(startLocation);
-		}
-	}
+        if (influence < 1.0f) {
+            startLocation.subtractLocal(ownerLocation).normalizeLocal().mult(influence);
+            ownerLocation.addLocal(startLocation);
+        }
+    }
 }

+ 57 - 57
engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionLocLimit.java

@@ -9,67 +9,67 @@ import com.jme3.scene.plugins.blender.file.Structure;
  * This class represents 'Loc limit' constraint type in blender.
  * @author Marcin Roguski (Kaelthas)
  */
-/*package*/ class ConstraintDefinitionLocLimit extends ConstraintDefinition {
-	private static final int LIMIT_XMIN = 0x01;
+/* package */class ConstraintDefinitionLocLimit extends ConstraintDefinition {
+    private static final int LIMIT_XMIN = 0x01;
     private static final int LIMIT_XMAX = 0x02;
     private static final int LIMIT_YMIN = 0x04;
     private static final int LIMIT_YMAX = 0x08;
     private static final int LIMIT_ZMIN = 0x10;
     private static final int LIMIT_ZMAX = 0x20;
-    
-    protected float[][] limits = new float[3][2];
-    
+
+    protected float[][]      limits     = new float[3][2];
+
     public ConstraintDefinitionLocLimit(Structure constraintData, BlenderContext blenderContext) {
-		super(constraintData, blenderContext);
-		if(blenderContext.getBlenderKey().isFixUpAxis()) {
-			limits[0][0] = ((Number) constraintData.getFieldValue("xmin")).floatValue();
-			limits[0][1] = ((Number) constraintData.getFieldValue("xmax")).floatValue();
-			limits[2][0] = -((Number) constraintData.getFieldValue("ymin")).floatValue();
-			limits[2][1] = -((Number) constraintData.getFieldValue("ymax")).floatValue();
-			limits[1][0] = ((Number) constraintData.getFieldValue("zmin")).floatValue();
-			limits[1][1] = ((Number) constraintData.getFieldValue("zmax")).floatValue();
-			
-			//swapping Y and X limits flag in the bitwise flag
-			int ymin = flag & LIMIT_YMIN;
-			int ymax = flag & LIMIT_YMAX;
-			int zmin = flag & LIMIT_ZMIN;
-			int zmax = flag & LIMIT_ZMAX;
-			flag &= LIMIT_XMIN | LIMIT_XMAX;//clear the other flags to swap them
-			flag |= ymin << 2;
-			flag |= ymax << 2;
-			flag |= zmin >> 2;
-			flag |= zmax >> 2;
-		} else {
-			limits[0][0] = ((Number) constraintData.getFieldValue("xmin")).floatValue();
-			limits[0][1] = ((Number) constraintData.getFieldValue("xmax")).floatValue();
-			limits[1][0] = ((Number) constraintData.getFieldValue("ymin")).floatValue();
-			limits[1][1] = ((Number) constraintData.getFieldValue("ymax")).floatValue();
-			limits[2][0] = ((Number) constraintData.getFieldValue("zmin")).floatValue();
-			limits[2][1] = ((Number) constraintData.getFieldValue("zmax")).floatValue();
-		}
-	}
-	
-	@Override
-	public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
-		Vector3f translation = ownerTransform.getTranslation();
-		
-		if ((flag & LIMIT_XMIN) != 0 && translation.x < limits[0][0]) {
-			translation.x -= (translation.x - limits[0][0]) * influence;
-		}
-		if ((flag & LIMIT_XMAX) != 0 && translation.x > limits[0][1]) {
-			translation.x -= (translation.x - limits[0][1]) * influence;
-		}
-		if ((flag & LIMIT_YMIN) != 0 && translation.y < limits[1][0]) {
-			translation.y -= (translation.y - limits[1][0]) * influence;
-		}
-		if ((flag & LIMIT_YMAX) != 0 && translation.y > limits[1][1]) {
-			translation.y -= (translation.y - limits[1][1]) * influence;
-		}
-		if ((flag & LIMIT_ZMIN) != 0 && translation.z < limits[2][0]) {
-			translation.z -= (translation.z - limits[2][0]) * influence;
-		}
-		if ((flag & LIMIT_ZMAX) != 0 && translation.z > limits[2][1]) {
-			translation.z -= (translation.z - limits[2][1]) * influence;
-		}
-	}
+        super(constraintData, blenderContext);
+        if (blenderContext.getBlenderKey().isFixUpAxis()) {
+            limits[0][0] = ((Number) constraintData.getFieldValue("xmin")).floatValue();
+            limits[0][1] = ((Number) constraintData.getFieldValue("xmax")).floatValue();
+            limits[2][0] = -((Number) constraintData.getFieldValue("ymin")).floatValue();
+            limits[2][1] = -((Number) constraintData.getFieldValue("ymax")).floatValue();
+            limits[1][0] = ((Number) constraintData.getFieldValue("zmin")).floatValue();
+            limits[1][1] = ((Number) constraintData.getFieldValue("zmax")).floatValue();
+
+            // swapping Y and X limits flag in the bitwise flag
+            int ymin = flag & LIMIT_YMIN;
+            int ymax = flag & LIMIT_YMAX;
+            int zmin = flag & LIMIT_ZMIN;
+            int zmax = flag & LIMIT_ZMAX;
+            flag &= LIMIT_XMIN | LIMIT_XMAX;// clear the other flags to swap them
+            flag |= ymin << 2;
+            flag |= ymax << 2;
+            flag |= zmin >> 2;
+            flag |= zmax >> 2;
+        } else {
+            limits[0][0] = ((Number) constraintData.getFieldValue("xmin")).floatValue();
+            limits[0][1] = ((Number) constraintData.getFieldValue("xmax")).floatValue();
+            limits[1][0] = ((Number) constraintData.getFieldValue("ymin")).floatValue();
+            limits[1][1] = ((Number) constraintData.getFieldValue("ymax")).floatValue();
+            limits[2][0] = ((Number) constraintData.getFieldValue("zmin")).floatValue();
+            limits[2][1] = ((Number) constraintData.getFieldValue("zmax")).floatValue();
+        }
+    }
+
+    @Override
+    public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
+        Vector3f translation = ownerTransform.getTranslation();
+
+        if ((flag & LIMIT_XMIN) != 0 && translation.x < limits[0][0]) {
+            translation.x -= (translation.x - limits[0][0]) * influence;
+        }
+        if ((flag & LIMIT_XMAX) != 0 && translation.x > limits[0][1]) {
+            translation.x -= (translation.x - limits[0][1]) * influence;
+        }
+        if ((flag & LIMIT_YMIN) != 0 && translation.y < limits[1][0]) {
+            translation.y -= (translation.y - limits[1][0]) * influence;
+        }
+        if ((flag & LIMIT_YMAX) != 0 && translation.y > limits[1][1]) {
+            translation.y -= (translation.y - limits[1][1]) * influence;
+        }
+        if ((flag & LIMIT_ZMIN) != 0 && translation.z < limits[2][0]) {
+            translation.z -= (translation.z - limits[2][0]) * influence;
+        }
+        if ((flag & LIMIT_ZMAX) != 0 && translation.z > limits[2][1]) {
+            translation.z -= (translation.z - limits[2][1]) * influence;
+        }
+    }
 }

+ 12 - 12
engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionLockTrack.java

@@ -11,16 +11,16 @@ import com.jme3.scene.plugins.blender.file.Structure;
  * This class represents 'Action' constraint type in blender.
  * @author Marcin Roguski (Kaelthas)
  */
-/*package*/ class ConstraintDefinitionLockTrack extends ConstraintDefinition {
-	private static final Logger LOGGER = Logger.getLogger(ConstraintDefinitionLockTrack.class.getName());
-	
-	public ConstraintDefinitionLockTrack(Structure constraintData, BlenderContext blenderContext) {
-		super(constraintData, blenderContext);
-	}
-	
-	@Override
-	public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
-		// TODO: implement 'Lock track' constraint
-		LOGGER.log(Level.WARNING, "'Lock track' constraint NOT implemented!");
-	}
+/* package */class ConstraintDefinitionLockTrack extends ConstraintDefinition {
+    private static final Logger LOGGER = Logger.getLogger(ConstraintDefinitionLockTrack.class.getName());
+
+    public ConstraintDefinitionLockTrack(Structure constraintData, BlenderContext blenderContext) {
+        super(constraintData, blenderContext);
+    }
+
+    @Override
+    public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
+        // TODO: implement 'Lock track' constraint
+        LOGGER.log(Level.WARNING, "'Lock track' constraint NOT implemented!");
+    }
 }

+ 12 - 12
engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionMinMax.java

@@ -11,16 +11,16 @@ import com.jme3.scene.plugins.blender.file.Structure;
  * This class represents 'Min max' constraint type in blender.
  * @author Marcin Roguski (Kaelthas)
  */
-/*package*/ class ConstraintDefinitionMinMax extends ConstraintDefinition {
-	private static final Logger LOGGER = Logger.getLogger(ConstraintDefinitionMinMax.class.getName());
-	
-	public ConstraintDefinitionMinMax(Structure constraintData, BlenderContext blenderContext) {
-		super(constraintData, blenderContext);
-	}
-	
-	@Override
-	public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
-		// TODO: implement 'Min max' constraint
-		LOGGER.log(Level.WARNING, "'Min max' constraint NOT implemented!");
-	}
+/* package */class ConstraintDefinitionMinMax extends ConstraintDefinition {
+    private static final Logger LOGGER = Logger.getLogger(ConstraintDefinitionMinMax.class.getName());
+
+    public ConstraintDefinitionMinMax(Structure constraintData, BlenderContext blenderContext) {
+        super(constraintData, blenderContext);
+    }
+
+    @Override
+    public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
+        // TODO: implement 'Min max' constraint
+        LOGGER.log(Level.WARNING, "'Min max' constraint NOT implemented!");
+    }
 }

+ 9 - 9
engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionNull.java

@@ -8,14 +8,14 @@ import com.jme3.scene.plugins.blender.file.Structure;
  * This class represents 'Null' constraint type in blender.
  * @author Marcin Roguski (Kaelthas)
  */
-/*package*/ class ConstraintDefinitionNull extends ConstraintDefinition {
+/* package */class ConstraintDefinitionNull extends ConstraintDefinition {
 
-	public ConstraintDefinitionNull(Structure constraintData, BlenderContext blenderContext) {
-		super(constraintData, blenderContext);
-	}
-	
-	@Override
-	public void bake(Transform ownerTransform, Transform targetTransform, float influence) { 
-		//null constraint does nothing so no need to implement this one
-	}
+    public ConstraintDefinitionNull(Structure constraintData, BlenderContext blenderContext) {
+        super(constraintData, blenderContext);
+    }
+
+    @Override
+    public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
+        // null constraint does nothing so no need to implement this one
+    }
 }

+ 11 - 11
engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionObjectSolver.java

@@ -11,16 +11,16 @@ import com.jme3.scene.plugins.blender.file.Structure;
  * This class represents 'Object solver' constraint type in blender.
  * @author Marcin Roguski (Kaelthas)
  */
-/*package*/ class ConstraintDefinitionObjectSolver extends ConstraintDefinition {
-	private static final Logger LOGGER = Logger.getLogger(ConstraintDefinitionAction.class.getName());
-	
-	public ConstraintDefinitionObjectSolver(Structure constraintData, BlenderContext blenderContext) {
-		super(constraintData, blenderContext);
-	}
+/* package */class ConstraintDefinitionObjectSolver extends ConstraintDefinition {
+    private static final Logger LOGGER = Logger.getLogger(ConstraintDefinitionAction.class.getName());
 
-	@Override
-	public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
-		// TODO: implement 'Object solver' constraint
-		LOGGER.log(Level.WARNING, "'Object solver' constraint NOT implemented!");
-	}
+    public ConstraintDefinitionObjectSolver(Structure constraintData, BlenderContext blenderContext) {
+        super(constraintData, blenderContext);
+    }
+
+    @Override
+    public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
+        // TODO: implement 'Object solver' constraint
+        LOGGER.log(Level.WARNING, "'Object solver' constraint NOT implemented!");
+    }
 }

+ 12 - 12
engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionPivot.java

@@ -11,16 +11,16 @@ import com.jme3.scene.plugins.blender.file.Structure;
  * The pivot constraint. Available for blender 2.50+.
  * @author Marcin Roguski (Kaelthas)
  */
-/*package*/ class ConstraintDefinitionPivot extends ConstraintDefinition {
-	private static final Logger LOGGER = Logger.getLogger(ConstraintDefinitionPivot.class.getName());
-	
-	public ConstraintDefinitionPivot(Structure constraintData, BlenderContext blenderContext) {
-		super(constraintData, blenderContext);
-	}
-	
-	@Override
-	public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
-		// TODO: implement 'Pivot' constraint
-		LOGGER.log(Level.WARNING, "'Pivot' constraint NOT implemented!");
-	}
+/* package */class ConstraintDefinitionPivot extends ConstraintDefinition {
+    private static final Logger LOGGER = Logger.getLogger(ConstraintDefinitionPivot.class.getName());
+
+    public ConstraintDefinitionPivot(Structure constraintData, BlenderContext blenderContext) {
+        super(constraintData, blenderContext);
+    }
+
+    @Override
+    public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
+        // TODO: implement 'Pivot' constraint
+        LOGGER.log(Level.WARNING, "'Pivot' constraint NOT implemented!");
+    }
 }

+ 12 - 12
engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionPython.java

@@ -11,16 +11,16 @@ import com.jme3.scene.plugins.blender.file.Structure;
  * This class represents 'Python' constraint type in blender.
  * @author Marcin Roguski (Kaelthas)
  */
-/*package*/ class ConstraintDefinitionPython extends ConstraintDefinition {
-	private static final Logger LOGGER = Logger.getLogger(ConstraintDefinitionPython.class.getName());
-	
-	public ConstraintDefinitionPython(Structure constraintData, BlenderContext blenderContext) {
-		super(constraintData, blenderContext);
-	}
-	
-	@Override
-	public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
-		// TODO: implement 'Python' constraint
-		LOGGER.log(Level.WARNING, "'Python' constraint NOT implemented!");
-	}
+/* package */class ConstraintDefinitionPython extends ConstraintDefinition {
+    private static final Logger LOGGER = Logger.getLogger(ConstraintDefinitionPython.class.getName());
+
+    public ConstraintDefinitionPython(Structure constraintData, BlenderContext blenderContext) {
+        super(constraintData, blenderContext);
+    }
+
+    @Override
+    public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
+        // TODO: implement 'Python' constraint
+        LOGGER.log(Level.WARNING, "'Python' constraint NOT implemented!");
+    }
 }

+ 12 - 12
engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionRigidBodyJoint.java

@@ -11,16 +11,16 @@ import com.jme3.scene.plugins.blender.file.Structure;
  * This class represents 'Rigid body joint' constraint type in blender.
  * @author Marcin Roguski (Kaelthas)
  */
-/*package*/ class ConstraintDefinitionRigidBodyJoint extends ConstraintDefinition {
-	private static final Logger LOGGER = Logger.getLogger(ConstraintDefinitionRigidBodyJoint.class.getName());
-	
-	public ConstraintDefinitionRigidBodyJoint(Structure constraintData, BlenderContext blenderContext) {
-		super(constraintData, blenderContext);
-	}
-	
-	@Override
-	public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
-		// TODO: implement 'Rigid body joint' constraint
-		LOGGER.log(Level.WARNING, "'Rigid body joint' constraint NOT implemented!");
-	}
+/* package */class ConstraintDefinitionRigidBodyJoint extends ConstraintDefinition {
+    private static final Logger LOGGER = Logger.getLogger(ConstraintDefinitionRigidBodyJoint.class.getName());
+
+    public ConstraintDefinitionRigidBodyJoint(Structure constraintData, BlenderContext blenderContext) {
+        super(constraintData, blenderContext);
+    }
+
+    @Override
+    public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
+        // TODO: implement 'Rigid body joint' constraint
+        LOGGER.log(Level.WARNING, "'Rigid body joint' constraint NOT implemented!");
+    }
 }

+ 48 - 48
engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionRotLike.java

@@ -9,59 +9,59 @@ import com.jme3.scene.plugins.blender.file.Structure;
  * This class represents 'Rot like' constraint type in blender.
  * @author Marcin Roguski (Kaelthas)
  */
-/*package*/ class ConstraintDefinitionRotLike extends ConstraintDefinition {
-	private static final int ROTLIKE_X = 0x01;
-	private static final int ROTLIKE_Y = 0x02;
-	private static final int ROTLIKE_Z = 0x04;
-	private static final int ROTLIKE_X_INVERT = 0x10;
-    private static final int ROTLIKE_Y_INVERT = 0x20;
-    private static final int ROTLIKE_Z_INVERT = 0x40;
-    private static final int ROTLIKE_OFFSET = 0x80;
-    
-    private transient float[] ownerAngles = new float[3];
-    private transient float[] targetAngles = new float[3];
-    
+/* package */class ConstraintDefinitionRotLike extends ConstraintDefinition {
+    private static final int  ROTLIKE_X        = 0x01;
+    private static final int  ROTLIKE_Y        = 0x02;
+    private static final int  ROTLIKE_Z        = 0x04;
+    private static final int  ROTLIKE_X_INVERT = 0x10;
+    private static final int  ROTLIKE_Y_INVERT = 0x20;
+    private static final int  ROTLIKE_Z_INVERT = 0x40;
+    private static final int  ROTLIKE_OFFSET   = 0x80;
+
+    private transient float[] ownerAngles      = new float[3];
+    private transient float[] targetAngles     = new float[3];
+
     public ConstraintDefinitionRotLike(Structure constraintData, BlenderContext blenderContext) {
-		super(constraintData, blenderContext);
-	}
+        super(constraintData, blenderContext);
+    }
 
     @Override
     public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
-    	Quaternion ownerRotation = ownerTransform.getRotation();    	
-    	ownerAngles = ownerRotation.toAngles(ownerAngles);
-    	targetAngles = targetTransform.getRotation().toAngles(targetAngles);
-    	
-    	Quaternion startRotation = ownerRotation.clone();
-		Quaternion offset = Quaternion.IDENTITY;
-		if ((flag & ROTLIKE_OFFSET) != 0) {//we add the original rotation to the copied rotation
-			offset = startRotation;
-		}
+        Quaternion ownerRotation = ownerTransform.getRotation();
+        ownerAngles = ownerRotation.toAngles(ownerAngles);
+        targetAngles = targetTransform.getRotation().toAngles(targetAngles);
+
+        Quaternion startRotation = ownerRotation.clone();
+        Quaternion offset = Quaternion.IDENTITY;
+        if ((flag & ROTLIKE_OFFSET) != 0) {// we add the original rotation to the copied rotation
+            offset = startRotation;
+        }
+
+        if ((flag & ROTLIKE_X) != 0) {
+            ownerAngles[0] = targetAngles[0];
+            if ((flag & ROTLIKE_X_INVERT) != 0) {
+                ownerAngles[0] = -ownerAngles[0];
+            }
+        }
+        if ((flag & ROTLIKE_Y) != 0) {
+            ownerAngles[1] = targetAngles[1];
+            if ((flag & ROTLIKE_Y_INVERT) != 0) {
+                ownerAngles[1] = -ownerAngles[1];
+            }
+        }
+        if ((flag & ROTLIKE_Z) != 0) {
+            ownerAngles[2] = targetAngles[2];
+            if ((flag & ROTLIKE_Z_INVERT) != 0) {
+                ownerAngles[2] = -ownerAngles[2];
+            }
+        }
+        ownerRotation.fromAngles(ownerAngles).multLocal(offset);
 
-		if ((flag & ROTLIKE_X) != 0) {
-			ownerAngles[0] = targetAngles[0];
-			if ((flag & ROTLIKE_X_INVERT) != 0) {
-				ownerAngles[0] = -ownerAngles[0];
-			}
-		}
-		if ((flag & ROTLIKE_Y) != 0) {
-			ownerAngles[1] = targetAngles[1];
-			if ((flag & ROTLIKE_Y_INVERT) != 0) {
-				ownerAngles[1] = -ownerAngles[1];
-			}
-		}
-		if ((flag & ROTLIKE_Z) != 0) {
-			ownerAngles[2] = targetAngles[2];
-			if ((flag & ROTLIKE_Z_INVERT) != 0) {
-				ownerAngles[2] = -ownerAngles[2];
-			}
-		}
-		ownerRotation.fromAngles(ownerAngles).multLocal(offset);
+        if (influence < 1.0f) {
 
-		if(influence < 1.0f) {
-			
-//			startLocation.subtractLocal(ownerLocation).normalizeLocal().mult(influence);
-//			ownerLocation.addLocal(startLocation);
-			//TODO
-		}
+            // startLocation.subtractLocal(ownerLocation).normalizeLocal().mult(influence);
+            // ownerLocation.addLocal(startLocation);
+            // TODO
+        }
     }
 }

+ 68 - 68
engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionRotLimit.java

@@ -11,75 +11,75 @@ import com.jme3.scene.plugins.blender.file.Structure;
  * @author Marcin Roguski (Kaelthas)
  */
 /* package */class ConstraintDefinitionRotLimit extends ConstraintDefinition {
-	private static final int	LIMIT_XROT	= 0x01;
-	private static final int	LIMIT_YROT	= 0x02;
-	private static final int	LIMIT_ZROT	= 0x04;
+    private static final int    LIMIT_XROT = 0x01;
+    private static final int    LIMIT_YROT = 0x02;
+    private static final int    LIMIT_ZROT = 0x04;
 
-	private transient float[][]	limits		= new float[3][2];
-	private transient float[]	angles 		= new float[3];
-	
-	public ConstraintDefinitionRotLimit(Structure constraintData, BlenderContext blenderContext) {
-		super(constraintData, blenderContext);
-		if (blenderContext.getBlenderKey().isFixUpAxis()/* && owner.spatial != null*/) {//FIXME: !!!!!!!!
-			limits[0][0] = ((Number) constraintData.getFieldValue("xmin")).floatValue();
-			limits[0][1] = ((Number) constraintData.getFieldValue("xmax")).floatValue();
-			limits[2][0] = -((Number) constraintData.getFieldValue("ymin")).floatValue();
-			limits[2][1] = -((Number) constraintData.getFieldValue("ymax")).floatValue();
-			limits[1][0] = ((Number) constraintData.getFieldValue("zmin")).floatValue();
-			limits[1][1] = ((Number) constraintData.getFieldValue("zmax")).floatValue();
+    private transient float[][] limits     = new float[3][2];
+    private transient float[]   angles     = new float[3];
 
-			// swapping Y and X limits flag in the bitwise flag
-			int limitY = flag & LIMIT_YROT;
-			int limitZ = flag & LIMIT_ZROT;
-			flag &= LIMIT_XROT;// clear the other flags to swap them
-			flag |= limitY << 1;
-			flag |= limitZ >> 1;
-		} else {
-			limits[0][0] = ((Number) constraintData.getFieldValue("xmin")).floatValue();
-			limits[0][1] = ((Number) constraintData.getFieldValue("xmax")).floatValue();
-			limits[1][0] = ((Number) constraintData.getFieldValue("ymin")).floatValue();
-			limits[1][1] = ((Number) constraintData.getFieldValue("ymax")).floatValue();
-			limits[2][0] = ((Number) constraintData.getFieldValue("zmin")).floatValue();
-			limits[2][1] = ((Number) constraintData.getFieldValue("zmax")).floatValue();
-		}
+    public ConstraintDefinitionRotLimit(Structure constraintData, BlenderContext blenderContext) {
+        super(constraintData, blenderContext);
+        if (blenderContext.getBlenderKey().isFixUpAxis()/* && owner.spatial != null */) {// FIXME: !!!!!!!!
+            limits[0][0] = ((Number) constraintData.getFieldValue("xmin")).floatValue();
+            limits[0][1] = ((Number) constraintData.getFieldValue("xmax")).floatValue();
+            limits[2][0] = -((Number) constraintData.getFieldValue("ymin")).floatValue();
+            limits[2][1] = -((Number) constraintData.getFieldValue("ymax")).floatValue();
+            limits[1][0] = ((Number) constraintData.getFieldValue("zmin")).floatValue();
+            limits[1][1] = ((Number) constraintData.getFieldValue("zmax")).floatValue();
 
-		// until blender 2.49 the rotations values were stored in degrees
-		if (blenderContext.getBlenderVersion() <= 249) {
-			for (int i = 0; i < limits.length; ++i) {
-				limits[i][0] *= FastMath.DEG_TO_RAD;
-				limits[i][1] *= FastMath.DEG_TO_RAD;
-			}
-		}
-	}
-	
-	@Override
-	public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
-		if ((flag & LIMIT_XROT) != 0) {
-			float difference = 0.0f;
-			if (angles[0] < limits[0][0]) {
-				difference = (angles[0] - limits[0][0]) * influence;
-			} else if (angles[0] > limits[0][1]) {
-				difference = (angles[0] - limits[0][1]) * influence;
-			}
-			angles[0] -= difference;
-		}
-		if ((flag & LIMIT_YROT) != 0) {
-			float difference = 0.0f;
-			if (angles[1] < limits[1][0]) {
-				difference = (angles[1] - limits[1][0]) * influence;
-			} else if (angles[1] > limits[1][1]) {
-				difference = (angles[1] - limits[1][1]) * influence;
-			}
-			angles[1] -= difference;
-		}
-		if ((flag & LIMIT_ZROT) != 0) {
-			float difference = 0.0f;
-			if (angles[2] < limits[2][0]) {
-				difference = (angles[2] - limits[2][0]) * influence;
-			} else if (angles[2] > limits[2][1]) {
-				difference = (angles[2] - limits[2][1]) * influence;
-			}
-			angles[2] -= difference;
-		}
-	}
+            // swapping Y and X limits flag in the bitwise flag
+            int limitY = flag & LIMIT_YROT;
+            int limitZ = flag & LIMIT_ZROT;
+            flag &= LIMIT_XROT;// clear the other flags to swap them
+            flag |= limitY << 1;
+            flag |= limitZ >> 1;
+        } else {
+            limits[0][0] = ((Number) constraintData.getFieldValue("xmin")).floatValue();
+            limits[0][1] = ((Number) constraintData.getFieldValue("xmax")).floatValue();
+            limits[1][0] = ((Number) constraintData.getFieldValue("ymin")).floatValue();
+            limits[1][1] = ((Number) constraintData.getFieldValue("ymax")).floatValue();
+            limits[2][0] = ((Number) constraintData.getFieldValue("zmin")).floatValue();
+            limits[2][1] = ((Number) constraintData.getFieldValue("zmax")).floatValue();
+        }
+
+        // until blender 2.49 the rotations values were stored in degrees
+        if (blenderContext.getBlenderVersion() <= 249) {
+            for (int i = 0; i < limits.length; ++i) {
+                limits[i][0] *= FastMath.DEG_TO_RAD;
+                limits[i][1] *= FastMath.DEG_TO_RAD;
+            }
+        }
+    }
+
+    @Override
+    public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
+        if ((flag & LIMIT_XROT) != 0) {
+            float difference = 0.0f;
+            if (angles[0] < limits[0][0]) {
+                difference = (angles[0] - limits[0][0]) * influence;
+            } else if (angles[0] > limits[0][1]) {
+                difference = (angles[0] - limits[0][1]) * influence;
+            }
+            angles[0] -= difference;
+        }
+        if ((flag & LIMIT_YROT) != 0) {
+            float difference = 0.0f;
+            if (angles[1] < limits[1][0]) {
+                difference = (angles[1] - limits[1][0]) * influence;
+            } else if (angles[1] > limits[1][1]) {
+                difference = (angles[1] - limits[1][1]) * influence;
+            }
+            angles[1] -= difference;
+        }
+        if ((flag & LIMIT_ZROT) != 0) {
+            float difference = 0.0f;
+            if (angles[2] < limits[2][0]) {
+                difference = (angles[2] - limits[2][0]) * influence;
+            } else if (angles[2] > limits[2][1]) {
+                difference = (angles[2] - limits[2][1]) * influence;
+            }
+            angles[2] -= difference;
+        }
+    }
 }

+ 12 - 12
engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionSameVolume.java

@@ -12,16 +12,16 @@ import com.jme3.scene.plugins.blender.file.Structure;
  * 
  * @author Marcin Roguski (Kaelthas)
  */
-/*package*/ class ConstraintDefinitionSameVolume extends ConstraintDefinition {
-	private static final Logger LOGGER = Logger.getLogger(ConstraintDefinitionSameVolume.class.getName());
-	
-	public ConstraintDefinitionSameVolume(Structure constraintData, BlenderContext blenderContext) {
-		super(constraintData, blenderContext);
-	}
-	
-	@Override
-	public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
-		// TODO: implement 'Same volume' constraint
-		LOGGER.log(Level.WARNING, "'Same volume' constraint NOT implemented!");
-	}
+/* package */class ConstraintDefinitionSameVolume extends ConstraintDefinition {
+    private static final Logger LOGGER = Logger.getLogger(ConstraintDefinitionSameVolume.class.getName());
+
+    public ConstraintDefinitionSameVolume(Structure constraintData, BlenderContext blenderContext) {
+        super(constraintData, blenderContext);
+    }
+
+    @Override
+    public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
+        // TODO: implement 'Same volume' constraint
+        LOGGER.log(Level.WARNING, "'Same volume' constraint NOT implemented!");
+    }
 }

+ 49 - 50
engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionShrinkWrap.java

@@ -8,55 +8,54 @@ import com.jme3.scene.plugins.blender.file.Structure;
  * This class represents 'Shrink wrap' constraint type in blender.
  * @author Marcin Roguski (Kaelthas)
  */
-/*package*/ class ConstraintDefinitionShrinkWrap extends ConstraintDefinition {
-	
-	public ConstraintDefinitionShrinkWrap(Structure constraintData, BlenderContext blenderContext) {
-		super(constraintData, blenderContext);
-	}
+/* package */class ConstraintDefinitionShrinkWrap extends ConstraintDefinition {
 
-	@Override
-	public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
-		//loading mesh points (blender ensures that the target is a mesh-object)
-		/*List<Vector3f> pts = new ArrayList<Vector3f>();
-		Node target = (Node) this.target.getObject();
-		for(Spatial spatial : target.getChildren()) {
-			if(spatial instanceof Geometry) {
-				Mesh mesh = ((Geometry) spatial).getMesh();
-				FloatBuffer floatBuffer = mesh.getFloatBuffer(Type.Position);
-				for(int i=0;i<floatBuffer.limit();i+=3) {
-					pts.add(new Vector3f(floatBuffer.get(i), floatBuffer.get(i + 1), floatBuffer.get(i + 2)));
-				}
-			}
-		}
-		
-		AnimData animData = blenderContext.getAnimData(this.owner.getOma());
-		if(animData != null) {
-			Object owner = this.owner.getObject();
-			for(Animation animation : animData.anims) {
-				BlenderTrack track = this.getTrack(owner, animData.skeleton, animation);
-				Vector3f[] translations = track.getTranslations();
-				Quaternion[] rotations = track.getRotations();
-				int maxFrames = translations.length;
-				for (int frame = 0; frame < maxFrames; ++frame) {
-					Vector3f currentTranslation = translations[frame];
-					
-					//looking for minimum distanced point
-					Vector3f minDistancePoint = null;
-					float distance = Float.MAX_VALUE;
-					for(Vector3f p : pts) {
-						float temp = currentTranslation.distance(p);
-						if(temp < distance) {
-							distance = temp;
-							minDistancePoint = p;
-						}
-					}
-					translations[frame] = minDistancePoint.clone();
-				}
-				
-				track.setKeyframes(track.getTimes(), translations, rotations, track.getScales());
-			}
-		}*/
-		
-		//TODO: static constraint for spatials
-	}
+    public ConstraintDefinitionShrinkWrap(Structure constraintData, BlenderContext blenderContext) {
+        super(constraintData, blenderContext);
+    }
+
+    @Override
+    public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
+        // loading mesh points (blender ensures that the target is a mesh-object)
+        /*
+         * List<Vector3f> pts = new ArrayList<Vector3f>();
+         * Node target = (Node) this.target.getObject();
+         * for(Spatial spatial : target.getChildren()) {
+         * if(spatial instanceof Geometry) {
+         * Mesh mesh = ((Geometry) spatial).getMesh();
+         * FloatBuffer floatBuffer = mesh.getFloatBuffer(Type.Position);
+         * for(int i=0;i<floatBuffer.limit();i+=3) {
+         * pts.add(new Vector3f(floatBuffer.get(i), floatBuffer.get(i + 1), floatBuffer.get(i + 2)));
+         * }
+         * }
+         * }
+         * AnimData animData = blenderContext.getAnimData(this.owner.getOma());
+         * if(animData != null) {
+         * Object owner = this.owner.getObject();
+         * for(Animation animation : animData.anims) {
+         * BlenderTrack track = this.getTrack(owner, animData.skeleton, animation);
+         * Vector3f[] translations = track.getTranslations();
+         * Quaternion[] rotations = track.getRotations();
+         * int maxFrames = translations.length;
+         * for (int frame = 0; frame < maxFrames; ++frame) {
+         * Vector3f currentTranslation = translations[frame];
+         * //looking for minimum distanced point
+         * Vector3f minDistancePoint = null;
+         * float distance = Float.MAX_VALUE;
+         * for(Vector3f p : pts) {
+         * float temp = currentTranslation.distance(p);
+         * if(temp < distance) {
+         * distance = temp;
+         * minDistancePoint = p;
+         * }
+         * }
+         * translations[frame] = minDistancePoint.clone();
+         * }
+         * track.setKeyframes(track.getTimes(), translations, rotations, track.getScales());
+         * }
+         * }
+         */
+
+        // TODO: static constraint for spatials
+    }
 }

+ 38 - 38
engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionSizeLike.java

@@ -9,43 +9,43 @@ import com.jme3.scene.plugins.blender.file.Structure;
  * This class represents 'Size like' constraint type in blender.
  * @author Marcin Roguski (Kaelthas)
  */
-/*package*/ class ConstraintDefinitionSizeLike extends ConstraintDefinition {
-	private static final int SIZELIKE_X = 0x01;
-	private static final int SIZELIKE_Y = 0x02;
-	private static final int SIZELIKE_Z = 0x04;
-	private static final int LOCLIKE_OFFSET = 0x80;
-    
-	public ConstraintDefinitionSizeLike(Structure constraintData, BlenderContext blenderContext) {
-		super(constraintData, blenderContext);
-		if(blenderContext.getBlenderKey().isFixUpAxis()) {
-			//swapping Y and X limits flag in the bitwise flag
-			int y = flag & SIZELIKE_Y;
-			int z = flag & SIZELIKE_Z;
-			flag &= SIZELIKE_X | LOCLIKE_OFFSET;//clear the other flags to swap them
-			flag |= y << 1;
-			flag |= z >> 1;
-		}
-	}
-	
-	@Override
-	public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
-		Vector3f ownerScale = ownerTransform.getScale();
-		Vector3f targetScale = targetTransform.getScale();
-		
-		Vector3f offset = Vector3f.ZERO;
-		if ((flag & LOCLIKE_OFFSET) != 0) {//we add the original scale to the copied scale
-			offset = ownerScale.clone();
-		}
+/* package */class ConstraintDefinitionSizeLike extends ConstraintDefinition {
+    private static final int SIZELIKE_X     = 0x01;
+    private static final int SIZELIKE_Y     = 0x02;
+    private static final int SIZELIKE_Z     = 0x04;
+    private static final int LOCLIKE_OFFSET = 0x80;
 
-		if ((flag & SIZELIKE_X) != 0) {
-			ownerScale.x = targetScale.x * influence + (1.0f - influence) * ownerScale.x;
-		}
-		if ((flag & SIZELIKE_Y) != 0) {
-			ownerScale.y = targetScale.y * influence + (1.0f - influence) * ownerScale.y;
-		}
-		if ((flag & SIZELIKE_Z) != 0) {
-			ownerScale.z = targetScale.z * influence + (1.0f - influence) * ownerScale.z;
-		}
-		ownerScale.addLocal(offset);
-	}
+    public ConstraintDefinitionSizeLike(Structure constraintData, BlenderContext blenderContext) {
+        super(constraintData, blenderContext);
+        if (blenderContext.getBlenderKey().isFixUpAxis()) {
+            // swapping Y and X limits flag in the bitwise flag
+            int y = flag & SIZELIKE_Y;
+            int z = flag & SIZELIKE_Z;
+            flag &= SIZELIKE_X | LOCLIKE_OFFSET;// clear the other flags to swap them
+            flag |= y << 1;
+            flag |= z >> 1;
+        }
+    }
+
+    @Override
+    public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
+        Vector3f ownerScale = ownerTransform.getScale();
+        Vector3f targetScale = targetTransform.getScale();
+
+        Vector3f offset = Vector3f.ZERO;
+        if ((flag & LOCLIKE_OFFSET) != 0) {// we add the original scale to the copied scale
+            offset = ownerScale.clone();
+        }
+
+        if ((flag & SIZELIKE_X) != 0) {
+            ownerScale.x = targetScale.x * influence + (1.0f - influence) * ownerScale.x;
+        }
+        if ((flag & SIZELIKE_Y) != 0) {
+            ownerScale.y = targetScale.y * influence + (1.0f - influence) * ownerScale.y;
+        }
+        if ((flag & SIZELIKE_Z) != 0) {
+            ownerScale.z = targetScale.z * influence + (1.0f - influence) * ownerScale.z;
+        }
+        ownerScale.addLocal(offset);
+    }
 }

+ 63 - 63
engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionSizeLimit.java

@@ -9,67 +9,67 @@ import com.jme3.scene.plugins.blender.file.Structure;
  * This class represents 'Size limit' constraint type in blender.
  * @author Marcin Roguski (Kaelthas)
  */
-/*package*/ class ConstraintDefinitionSizeLimit extends ConstraintDefinition {
-	private static final int LIMIT_XMIN = 0x01;
-	private static final int LIMIT_XMAX = 0x02;
-	private static final int LIMIT_YMIN = 0x04;
-	private static final int LIMIT_YMAX = 0x08;
-	private static final int LIMIT_ZMIN = 0x10;
-	private static final int LIMIT_ZMAX = 0x20;
-	
-	protected transient float[][] limits = new float[3][2];
-	
-	public ConstraintDefinitionSizeLimit(Structure constraintData, BlenderContext blenderContext) {
-		super(constraintData, blenderContext);
-		if(blenderContext.getBlenderKey().isFixUpAxis()) {
-			limits[0][0] = ((Number) constraintData.getFieldValue("xmin")).floatValue();
-			limits[0][1] = ((Number) constraintData.getFieldValue("xmax")).floatValue();
-			limits[2][0] = -((Number) constraintData.getFieldValue("ymin")).floatValue();
-			limits[2][1] = -((Number) constraintData.getFieldValue("ymax")).floatValue();
-			limits[1][0] = ((Number) constraintData.getFieldValue("zmin")).floatValue();
-			limits[1][1] = ((Number) constraintData.getFieldValue("zmax")).floatValue();
-			
-			//swapping Y and X limits flag in the bitwise flag
-			int ymin = flag & LIMIT_YMIN;
-			int ymax = flag & LIMIT_YMAX;
-			int zmin = flag & LIMIT_ZMIN;
-			int zmax = flag & LIMIT_ZMAX;
-			flag &= LIMIT_XMIN | LIMIT_XMAX;//clear the other flags to swap them
-			flag |= ymin << 2;
-			flag |= ymax << 2;
-			flag |= zmin >> 2;
-			flag |= zmax >> 2;
-		} else {
-			limits[0][0] = ((Number) constraintData.getFieldValue("xmin")).floatValue();
-			limits[0][1] = ((Number) constraintData.getFieldValue("xmax")).floatValue();
-			limits[1][0] = ((Number) constraintData.getFieldValue("ymin")).floatValue();
-			limits[1][1] = ((Number) constraintData.getFieldValue("ymax")).floatValue();
-			limits[2][0] = ((Number) constraintData.getFieldValue("zmin")).floatValue();
-			limits[2][1] = ((Number) constraintData.getFieldValue("zmax")).floatValue();
-		}
-	}
-	
-	@Override
-	public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
-		Vector3f scale = ownerTransform.getScale();
-		
-		if ((flag & LIMIT_XMIN) != 0 && scale.x < limits[0][0]) {
-			scale.x -= (scale.x - limits[0][0]) * influence;
-		}
-		if ((flag & LIMIT_XMAX) != 0 && scale.x > limits[0][1]) {
-			scale.x -= (scale.x - limits[0][1]) * influence;
-		}
-		if ((flag & LIMIT_YMIN) != 0 && scale.y < limits[1][0]) {
-			scale.y -= (scale.y - limits[1][0]) * influence;
-		}
-		if ((flag & LIMIT_YMAX) != 0 && scale.y > limits[1][1]) {
-			scale.y -= (scale.y - limits[1][1]) * influence;
-		}
-		if ((flag & LIMIT_ZMIN) != 0 && scale.z < limits[2][0]) {
-			scale.z -= (scale.z - limits[2][0]) * influence;
-		}
-		if ((flag & LIMIT_ZMAX) != 0 && scale.z > limits[2][1]) {
-			scale.z -= (scale.z - limits[2][1]) * influence;
-		}
-	}
+/* package */class ConstraintDefinitionSizeLimit extends ConstraintDefinition {
+    private static final int      LIMIT_XMIN = 0x01;
+    private static final int      LIMIT_XMAX = 0x02;
+    private static final int      LIMIT_YMIN = 0x04;
+    private static final int      LIMIT_YMAX = 0x08;
+    private static final int      LIMIT_ZMIN = 0x10;
+    private static final int      LIMIT_ZMAX = 0x20;
+
+    protected transient float[][] limits     = new float[3][2];
+
+    public ConstraintDefinitionSizeLimit(Structure constraintData, BlenderContext blenderContext) {
+        super(constraintData, blenderContext);
+        if (blenderContext.getBlenderKey().isFixUpAxis()) {
+            limits[0][0] = ((Number) constraintData.getFieldValue("xmin")).floatValue();
+            limits[0][1] = ((Number) constraintData.getFieldValue("xmax")).floatValue();
+            limits[2][0] = -((Number) constraintData.getFieldValue("ymin")).floatValue();
+            limits[2][1] = -((Number) constraintData.getFieldValue("ymax")).floatValue();
+            limits[1][0] = ((Number) constraintData.getFieldValue("zmin")).floatValue();
+            limits[1][1] = ((Number) constraintData.getFieldValue("zmax")).floatValue();
+
+            // swapping Y and X limits flag in the bitwise flag
+            int ymin = flag & LIMIT_YMIN;
+            int ymax = flag & LIMIT_YMAX;
+            int zmin = flag & LIMIT_ZMIN;
+            int zmax = flag & LIMIT_ZMAX;
+            flag &= LIMIT_XMIN | LIMIT_XMAX;// clear the other flags to swap them
+            flag |= ymin << 2;
+            flag |= ymax << 2;
+            flag |= zmin >> 2;
+            flag |= zmax >> 2;
+        } else {
+            limits[0][0] = ((Number) constraintData.getFieldValue("xmin")).floatValue();
+            limits[0][1] = ((Number) constraintData.getFieldValue("xmax")).floatValue();
+            limits[1][0] = ((Number) constraintData.getFieldValue("ymin")).floatValue();
+            limits[1][1] = ((Number) constraintData.getFieldValue("ymax")).floatValue();
+            limits[2][0] = ((Number) constraintData.getFieldValue("zmin")).floatValue();
+            limits[2][1] = ((Number) constraintData.getFieldValue("zmax")).floatValue();
+        }
+    }
+
+    @Override
+    public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
+        Vector3f scale = ownerTransform.getScale();
+
+        if ((flag & LIMIT_XMIN) != 0 && scale.x < limits[0][0]) {
+            scale.x -= (scale.x - limits[0][0]) * influence;
+        }
+        if ((flag & LIMIT_XMAX) != 0 && scale.x > limits[0][1]) {
+            scale.x -= (scale.x - limits[0][1]) * influence;
+        }
+        if ((flag & LIMIT_YMIN) != 0 && scale.y < limits[1][0]) {
+            scale.y -= (scale.y - limits[1][0]) * influence;
+        }
+        if ((flag & LIMIT_YMAX) != 0 && scale.y > limits[1][1]) {
+            scale.y -= (scale.y - limits[1][1]) * influence;
+        }
+        if ((flag & LIMIT_ZMIN) != 0 && scale.z < limits[2][0]) {
+            scale.z -= (scale.z - limits[2][0]) * influence;
+        }
+        if ((flag & LIMIT_ZMAX) != 0 && scale.z > limits[2][1]) {
+            scale.z -= (scale.z - limits[2][1]) * influence;
+        }
+    }
 }

+ 12 - 12
engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionSplineInverseKinematic.java

@@ -11,16 +11,16 @@ import com.jme3.scene.plugins.blender.file.Structure;
  * The spline inverse kinematic constraint. Available for blender 2.50+.
  * @author Marcin Roguski (Kaelthas)
  */
-/*package*/ class ConstraintDefinitionSplineInverseKinematic extends ConstraintDefinition {
-	private static final Logger LOGGER = Logger.getLogger(ConstraintDefinitionSplineInverseKinematic.class.getName());
-	
-	public ConstraintDefinitionSplineInverseKinematic(Structure constraintData, BlenderContext blenderContext) {
-		super(constraintData, blenderContext);
-	}
-	
-	@Override
-	public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
-		// TODO Auto-generated method stub
-		LOGGER.log(Level.WARNING, "'Splie IK' constraint NOT implemented!");		
-	}
+/* package */class ConstraintDefinitionSplineInverseKinematic extends ConstraintDefinition {
+    private static final Logger LOGGER = Logger.getLogger(ConstraintDefinitionSplineInverseKinematic.class.getName());
+
+    public ConstraintDefinitionSplineInverseKinematic(Structure constraintData, BlenderContext blenderContext) {
+        super(constraintData, blenderContext);
+    }
+
+    @Override
+    public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
+        // TODO Auto-generated method stub
+        LOGGER.log(Level.WARNING, "'Splie IK' constraint NOT implemented!");
+    }
 }

+ 11 - 11
engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionStretchTo.java

@@ -11,16 +11,16 @@ import com.jme3.scene.plugins.blender.file.Structure;
  * This class represents 'Stretch to' constraint type in blender.
  * @author Marcin Roguski (Kaelthas)
  */
-/*package*/ class ConstraintDefinitionStretchTo extends ConstraintDefinition {
-	private static final Logger LOGGER = Logger.getLogger(ConstraintDefinitionStretchTo.class.getName());
-	
-	public ConstraintDefinitionStretchTo(Structure constraintData, BlenderContext blenderContext) {
-		super(constraintData, blenderContext);
-	}
+/* package */class ConstraintDefinitionStretchTo extends ConstraintDefinition {
+    private static final Logger LOGGER = Logger.getLogger(ConstraintDefinitionStretchTo.class.getName());
 
-	@Override
-	public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
-		// TODO: implement 'Stretch to' constraint
-		LOGGER.log(Level.WARNING, "'Stretch to' constraint NOT implemented!");
-	}
+    public ConstraintDefinitionStretchTo(Structure constraintData, BlenderContext blenderContext) {
+        super(constraintData, blenderContext);
+    }
+
+    @Override
+    public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
+        // TODO: implement 'Stretch to' constraint
+        LOGGER.log(Level.WARNING, "'Stretch to' constraint NOT implemented!");
+    }
 }

+ 10 - 10
engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionTrackTo.java

@@ -13,15 +13,15 @@ import com.jme3.scene.plugins.blender.file.Structure;
  * @author Marcin Roguski (Kaelthas)
  */
 /* package */class ConstraintDefinitionTrackTo extends ConstraintDefinition {
-	private static final Logger LOGGER = Logger.getLogger(ConstraintDefinitionTrackTo.class.getName());
+    private static final Logger LOGGER = Logger.getLogger(ConstraintDefinitionTrackTo.class.getName());
 
-	public ConstraintDefinitionTrackTo(Structure constraintData, BlenderContext blenderContext) {
-		super(constraintData, blenderContext);
-	}
-	
-	@Override
-	public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
-		// TODO: implement 'Track to' constraint
-		LOGGER.log(Level.WARNING, "'Track to' constraint NOT implemented!");
-	}
+    public ConstraintDefinitionTrackTo(Structure constraintData, BlenderContext blenderContext) {
+        super(constraintData, blenderContext);
+    }
+
+    @Override
+    public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
+        // TODO: implement 'Track to' constraint
+        LOGGER.log(Level.WARNING, "'Track to' constraint NOT implemented!");
+    }
 }

+ 12 - 12
engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionTransLike.java

@@ -12,16 +12,16 @@ import com.jme3.scene.plugins.blender.file.Structure;
  * 
  * @author Marcin Roguski (Kaelthas)
  */
-/*package*/ class ConstraintDefinitionTransLike extends ConstraintDefinition {
-	private static final Logger LOGGER = Logger.getLogger(ConstraintDefinitionTransLike.class.getName());
-	
-	public ConstraintDefinitionTransLike(Structure constraintData, BlenderContext blenderContext) {
-		super(constraintData, blenderContext);
-	}
-	
-	@Override
-	public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
-		// TODO: implement 'Trans like' constraint
-		LOGGER.log(Level.WARNING, "'Trans like' constraint NOT implemented!");
-	}
+/* package */class ConstraintDefinitionTransLike extends ConstraintDefinition {
+    private static final Logger LOGGER = Logger.getLogger(ConstraintDefinitionTransLike.class.getName());
+
+    public ConstraintDefinitionTransLike(Structure constraintData, BlenderContext blenderContext) {
+        super(constraintData, blenderContext);
+    }
+
+    @Override
+    public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
+        // TODO: implement 'Trans like' constraint
+        LOGGER.log(Level.WARNING, "'Trans like' constraint NOT implemented!");
+    }
 }

+ 11 - 11
engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionTransform.java

@@ -11,16 +11,16 @@ import com.jme3.scene.plugins.blender.file.Structure;
  * This class represents 'Transform' constraint type in blender.
  * @author Marcin Roguski (Kaelthas)
  */
-/*package*/ class ConstraintDefinitionTransform extends ConstraintDefinition {
-	private static final Logger LOGGER = Logger.getLogger(ConstraintDefinitionAction.class.getName());
-	
-	public ConstraintDefinitionTransform(Structure constraintData, BlenderContext blenderContext) {
-		super(constraintData, blenderContext);
-	}
+/* package */class ConstraintDefinitionTransform extends ConstraintDefinition {
+    private static final Logger LOGGER = Logger.getLogger(ConstraintDefinitionAction.class.getName());
 
-	@Override
-	public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
-		// TODO: implement 'Transform' constraint
-		LOGGER.log(Level.WARNING, "'Transform' constraint NOT implemented!");
-	}
+    public ConstraintDefinitionTransform(Structure constraintData, BlenderContext blenderContext) {
+        super(constraintData, blenderContext);
+    }
+
+    @Override
+    public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
+        // TODO: implement 'Transform' constraint
+        LOGGER.log(Level.WARNING, "'Transform' constraint NOT implemented!");
+    }
 }

+ 29 - 33
engine/src/blender/com/jme3/scene/plugins/blender/curves/BezierCurve.java

@@ -16,17 +16,17 @@ 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. 
+    /**
+     * The type of the curve. Describes the data it modifies.
      * Used in ipos calculations.
      */
-    private int type;
+    private int             type;
     /** The dimension of the curve. */
-    private int dimension;
+    private int             dimension;
     /** A table of the bezier points. */
-    private float[][][] bezierPoints;
+    private float[][][]     bezierPoints;
     /** Array that stores a radius for each bezier triple. */
-    private float[] radiuses;
+    private float[]         radiuses;
 
     @SuppressWarnings("unchecked")
     public BezierCurve(final int type, final List<Structure> bezTriples, final int dimension) {
@@ -35,9 +35,9 @@ public class BezierCurve {
         }
         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
+        // 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];
         radiuses = new float[bezTriples.size()];
         int i = 0, j, k;
@@ -48,18 +48,18 @@ public class BezierCurve {
                     bezierPoints[i][j][k] = vec.get(j, k).floatValue();
                 }
             }
-            radiuses[i++] = ((Number)bezTriple.getFieldValue("radius")).floatValue();
+            radiuses[i++] = ((Number) bezTriple.getFieldValue("radius")).floatValue();
         }
     }
 
     /**
      * 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
+     *            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
+     *            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) {
@@ -74,7 +74,7 @@ public class BezierCurve {
         }
         if (frame < bezierPoints[0][1][0]) {
             return bezierPoints[0][1][1];
-        } else { //frame>bezierPoints[bezierPoints.length-1][1][0]
+        } else { // frame>bezierPoints[bezierPoints.length-1][1][0]
             return bezierPoints[bezierPoints.length - 1][1][1];
         }
     }
@@ -96,17 +96,17 @@ public class BezierCurve {
         return type;
     }
 
-	/**
-	 * The method returns the radius for the required bezier triple.
-	 * 
-	 * @param bezierTripleIndex
-	 *            index of the bezier triple
-	 * @return radius of the required bezier triple
-	 */
-	public float getRadius(int bezierTripleIndex) {
-		return radiuses[bezierTripleIndex];
-	}
-    
+    /**
+     * The method returns the radius for the required bezier triple.
+     * 
+     * @param bezierTripleIndex
+     *            index of the bezier triple
+     * @return radius of the required bezier triple
+     */
+    public float getRadius(int bezierTripleIndex) {
+        return radiuses[bezierTripleIndex];
+    }
+
     /**
      * This method returns a list of control points for this curve.
      * @return a list of control points for this curve.
@@ -133,18 +133,14 @@ public class BezierCurve {
     /**
      * This method converts the bezier triple of a specified index into text.
      * @param tripleIndex
-     *        index of the triple
+     *            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] + ")]";
+            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] + ")]";
+            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] + ")]";
         }
     }
 }

Dosya farkı çok büyük olduğundan ihmal edildi
+ 387 - 392
engine/src/blender/com/jme3/scene/plugins/blender/curves/CurvesHelper.java


+ 5 - 5
engine/src/blender/com/jme3/scene/plugins/blender/exceptions/BlenderFileException.java

@@ -43,13 +43,13 @@ public class BlenderFileException extends Exception {
      * Constructor. Creates an exception with no description.
      */
     public BlenderFileException() {
-    	//this constructor has no message
+        // this constructor has no message
     }
 
     /**
      * Constructor. Creates an exception containing the given message.
      * @param message
-     *        the message describing the problem that occured
+     *            the message describing the problem that occured
      */
     public BlenderFileException(String message) {
         super(message);
@@ -58,7 +58,7 @@ public class BlenderFileException extends Exception {
     /**
      * Constructor. Creates an exception that is based upon other thrown object. It contains the whole stacktrace then.
      * @param throwable
-     *        an exception/error that occured
+     *            an exception/error that occured
      */
     public BlenderFileException(Throwable throwable) {
         super(throwable);
@@ -67,9 +67,9 @@ public class BlenderFileException extends Exception {
     /**
      * Constructor. Creates an exception with both a message and stacktrace.
      * @param message
-     *        the message describing the problem that occured
+     *            the message describing the problem that occured
      * @param throwable
-     *        an exception/error that occured
+     *            an exception/error that occured
      */
     public BlenderFileException(String message, Throwable throwable) {
         super(message, throwable);

+ 38 - 39
engine/src/blender/com/jme3/scene/plugins/blender/file/BlenderInputStream.java

@@ -46,35 +46,35 @@ import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
  */
 public class BlenderInputStream extends InputStream {
 
-    private static final Logger LOGGER = Logger.getLogger(BlenderInputStream.class.getName());
+    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
+    private static final int    DEFAULT_BUFFER_SIZE = 1048576;                                             // 1MB
     /**
      * Size of a pointer; all pointers in the file are stored in this format. '_' means 4 bytes and '-' means 8 bytes.
      */
-    private int pointerSize;
+    private int                 pointerSize;
     /**
      * Type of byte ordering used; 'v' means little endian and 'V' means big endian.
      */
-    private char endianess;
+    private char                endianess;
     /** Version of Blender the file was created in; '248' means version 2.48. */
-    private String versionNumber;
+    private String              versionNumber;
     /** The buffer we store the read data to. */
-    protected byte[] cachedBuffer;
+    protected byte[]            cachedBuffer;
     /** The total size of the stored data. */
-    protected int size;
+    protected int               size;
     /** The current position of the read cursor. */
-    protected int position;
+    protected int               position;
 
     /**
      * Constructor. The input stream is stored and used to read data.
      * @param inputStream
-     *        the stream we read data from
+     *            the stream we read data from
      * @throws BlenderFileException
-     *         this exception is thrown if the file header has some invalid data
+     *             this exception is thrown if the file header has some invalid data
      */
     public BlenderInputStream(InputStream inputStream) throws BlenderFileException {
-        //the size value will canche while reading the file; the available() method cannot be counted on
+        // the size value will canche while reading the file; the available() method cannot be counted on
         try {
             size = inputStream.available();
         } catch (IOException e) {
@@ -84,7 +84,7 @@ public class BlenderInputStream extends InputStream {
             size = BlenderInputStream.DEFAULT_BUFFER_SIZE;
         }
 
-        //buffered input stream is used here for much faster file reading
+        // buffered input stream is used here for much faster file reading
         BufferedInputStream bufferedInputStream;
         if (inputStream instanceof BufferedInputStream) {
             bufferedInputStream = (BufferedInputStream) inputStream;
@@ -97,16 +97,16 @@ public class BlenderInputStream extends InputStream {
         } catch (IOException e) {
             throw new BlenderFileException("Problems occured while caching the file!", e);
         } finally {
-        	try {
-				inputStream.close();
-			} catch (IOException e) {
-				LOGGER.warning("Unable to close stream with blender file.");
-			}
+            try {
+                inputStream.close();
+            } catch (IOException e) {
+                LOGGER.warning("Unable to close stream with blender file.");
+            }
         }
 
         try {
             this.readFileHeader();
-        } catch (BlenderFileException e) {//the file might be packed, don't panic, try one more time ;)
+        } catch (BlenderFileException e) {// the file might be packed, don't panic, try one more time ;)
             this.decompressFile();
             this.position = 0;
             this.readFileHeader();
@@ -116,22 +116,22 @@ public class BlenderInputStream extends InputStream {
     /**
      * 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
+     *            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
+        size = 0;// this will count the actual size
         while (data != -1) {
-        	if (size >= cachedBuffer.length) {//widen the cached array
+            if (size >= cachedBuffer.length) {// widen the cached array
                 byte[] newBuffer = new byte[cachedBuffer.length + (cachedBuffer.length >> 1)];
                 System.arraycopy(cachedBuffer, 0, newBuffer, 0, cachedBuffer.length);
                 cachedBuffer = newBuffer;
             }
-        	cachedBuffer[size++] = (byte) data;
+            cachedBuffer[size++] = (byte) data;
             data = inputStream.read();
         }
     }
@@ -146,8 +146,7 @@ public class BlenderInputStream extends InputStream {
             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);
+            throw new IllegalStateException("IO errors occured where they should NOT! " + "The data is already buffered at this point!", e);
         } finally {
             try {
                 if (gis != null) {
@@ -162,9 +161,9 @@ public class BlenderInputStream extends InputStream {
     /**
      * This method loads the header from the given stream during instance creation.
      * @param inputStream
-     *        the stream we read the header from
+     *            the stream we read the header from
      * @throws BlenderFileException
-     *         this exception is thrown if the file header has some invalid data
+     *             this exception is thrown if the file header has some invalid data
      */
     private void readFileHeader() throws BlenderFileException {
         byte[] identifier = new byte[7];
@@ -213,7 +212,7 @@ public class BlenderInputStream extends InputStream {
     }
 
     /**
-     * This method reads a bytes number big enough to fill the table. 
+     * This method reads a bytes number big enough to fill the table.
      * It does not throw exceptions so it is for internal use only.
      * @param bytes
      *            an array to be filled with data
@@ -319,7 +318,7 @@ public class BlenderInputStream extends InputStream {
     /**
      * This method sets the current position of the read cursor.
      * @param position
-     *        the position of the read cursor
+     *            the position of the read cursor
      */
     public void setPosition(int position) {
         this.position = position;
@@ -352,7 +351,7 @@ public class BlenderInputStream extends InputStream {
     /**
      * This method aligns cursor position forward to a given amount of bytes.
      * @param bytesAmount
-     *        the byte amount to which we aligh the cursor
+     *            the byte amount to which we aligh the cursor
      */
     public void alignPosition(int bytesAmount) {
         if (bytesAmount <= 0) {
@@ -365,17 +364,17 @@ public class BlenderInputStream extends InputStream {
     }
 
     @Override
-    public void close() throws IOException { 
-    	//this method is unimplemented because some loaders (ie. TGALoader) have flaws that close the stream given from the outside
-    	//because the images can be stored directly in the blender file then this stream is properly positioned and given to the loader
-    	//to read the image file, that is why we do not want it to be closed before the reading is done
-    	//to properly close the stream use forceClose() method
+    public void close() throws IOException {
+        // this method is unimplemented because some loaders (ie. TGALoader) have flaws that close the stream given from the outside
+        // because the images can be stored directly in the blender file then this stream is properly positioned and given to the loader
+        // to read the image file, that is why we do not want it to be closed before the reading is done
+        // to properly close the stream use forceClose() method
     }
-    
+
     /**
      * This method should be used to close the stream because some loaders may close the stream while reading resources from it.
      */
     public void forceClose() {
-    	cachedBuffer = null;
+        cachedBuffer = null;
     }
 }

+ 24 - 29
engine/src/blender/com/jme3/scene/plugins/blender/file/DnaBlockData.java

@@ -42,39 +42,37 @@ import java.util.Map;
  */
 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
+    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;
+    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
+     *            the stream we read the block from
      * @param blenderContext
-     *        the blender context
+     *            the blender context
      * @throws BlenderFileException
-     *         this exception is throw if the blend file is invalid or somehow corrupted
+     *             this exception is throw if the blend file is invalid or somehow corrupted
      */
     public DnaBlockData(BlenderInputStream inputStream, BlenderContext blenderContext) throws BlenderFileException {
         int identifier;
 
-        //reading 'SDNA' identifier
-        identifier = inputStream.readByte() << 24 | inputStream.readByte() << 16
-                | inputStream.readByte() << 8 | inputStream.readByte();
+        // 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();
+        // 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));
         }
@@ -87,10 +85,9 @@ public class DnaBlockData {
             names[i] = inputStream.readString();
         }
 
-        //reding types
+        // reding types
         inputStream.alignPosition(4);
-        identifier = inputStream.readByte() << 24 | inputStream.readByte() << 16
-                | inputStream.readByte() << 8 | inputStream.readByte();
+        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));
         }
@@ -103,22 +100,20 @@ public class DnaBlockData {
             types[i] = inputStream.readString();
         }
 
-        //reading lengths
+        // reading lengths
         inputStream.alignPosition(4);
-        identifier = inputStream.readByte() << 24 | inputStream.readByte() << 16
-                | inputStream.readByte() << 8 | inputStream.readByte();
+        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
+        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
+        // reading structures
         inputStream.alignPosition(4);
-        identifier = inputStream.readByte() << 24 | inputStream.readByte() << 16
-                | inputStream.readByte() << 8 | inputStream.readByte();
+        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));
         }
@@ -148,7 +143,7 @@ public class DnaBlockData {
     /**
      * This method returns the structure of the given index.
      * @param index
-     *        the index of the structure
+     *            the index of the structure
      * @return the structure of the given index
      */
     public Structure getStructure(int index) {
@@ -162,7 +157,7 @@ public class DnaBlockData {
     /**
      * 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
+     *            the name of the structure
      * @return the required structure or null if the given name is inapropriate
      */
     public Structure getStructure(String name) {
@@ -176,7 +171,7 @@ public class DnaBlockData {
     /**
      * This method indicates if the structure of the given name exists.
      * @param name
-     *        the name of the structure
+     *            the name of the structure
      * @return true if the structure exists and false otherwise
      */
     public boolean hasStructure(String name) {
@@ -186,7 +181,7 @@ public class DnaBlockData {
     /**
      * This method converts the given identifier code to string.
      * @param code
-     *        the code taht is to be converted
+     *            the code taht is to be converted
      * @return the string value of the identifier
      */
     private String toString(int code) {

+ 10 - 10
engine/src/blender/com/jme3/scene/plugins/blender/file/DynamicArray.java

@@ -37,12 +37,12 @@ import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
  * An array that can be dynamically modified/
  * @author Marcin Roguski
  * @param <T>
- *        the type of stored data in the array
+ *            the type of stored data in the array
  */
 public class DynamicArray<T> implements Cloneable {
 
     /** An array object that holds the required data. */
-    private T[] array;
+    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:
@@ -53,9 +53,9 @@ public class DynamicArray<T> implements Cloneable {
     /**
      * Constructor. Builds an empty array of the specified sizes.
      * @param tableSizes
-     *        the sizes of the table
+     *            the sizes of the table
      * @throws BlenderFileException
-     *         an exception is thrown if one of the sizes is not a positive number
+     *             an exception is thrown if one of the sizes is not a positive number
      */
     @SuppressWarnings("unchecked")
     public DynamicArray(int[] tableSizes) throws BlenderFileException {
@@ -73,9 +73,9 @@ public class DynamicArray<T> implements Cloneable {
     /**
      * Constructor. Builds an empty array of the specified sizes.
      * @param tableSizes
-     *        the sizes of the table
+     *            the sizes of the table
      * @throws BlenderFileException
-     *         an exception is thrown if one of the sizes is not a positive number
+     *             an exception is thrown if one of the sizes is not a positive number
      */
     public DynamicArray(int[] tableSizes, T[] data) throws BlenderFileException {
         this.tableSizes = tableSizes;
@@ -101,7 +101,7 @@ public class DynamicArray<T> implements Cloneable {
      * 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
+     *            the position of the data
      * @return required data
      */
     public T get(int position) {
@@ -112,7 +112,7 @@ public class DynamicArray<T> implements Cloneable {
      * 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
+     *            the position of the data indices of data position
      * @return required data required data
      */
     public T get(int... position) {
@@ -138,8 +138,8 @@ public class DynamicArray<T> implements Cloneable {
     @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'
+        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 {

+ 32 - 32
engine/src/blender/com/jme3/scene/plugins/blender/file/Field.java

@@ -11,38 +11,38 @@ import java.util.List;
  * another structure.
  * @author Marcin Roguski
  */
-/*package*/
+/* package */
 class Field implements Cloneable {
 
     private static final int NAME_LENGTH = 24;
     private static final int TYPE_LENGTH = 16;
     /** The blender context. */
-    public BlenderContext blenderContext;
+    public BlenderContext    blenderContext;
     /** The type of the field. */
-    public String type;
+    public String            type;
     /** The name of the field. */
-    public String name;
+    public String            name;
     /** The value of the field. Filled during data reading. */
-    public Object value;
+    public Object            value;
     /** This variable indicates the level of the pointer. */
-    public int pointerLevel;
+    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;
+    public int[]             tableSizes;
     /** This variable indicates if the field is a function pointer. */
-    public boolean function;
+    public boolean           function;
 
     /**
      * Constructor. Saves the field data and parses its name.
      * @param name
-     *        the name of the field
+     *            the name of the field
      * @param type
-     *        the type of the field
+     *            the type of the field
      * @param blenderContext
-     *        the blender context
+     *            the blender context
      * @throws BlenderFileException
-     *         this exception is thrown if the names contain errors
+     *             this exception is thrown if the names contain errors
      */
     public Field(String name, String type, BlenderContext blenderContext) throws BlenderFileException {
         this.type = type;
@@ -54,7 +54,7 @@ class Field implements Cloneable {
      * 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
+     *            the object that we copy
      */
     private Field(Field field) {
         type = field.type;
@@ -75,9 +75,9 @@ class Field implements Cloneable {
     /**
      * This method fills the field wth data read from the input stream.
      * @param blenderInputStream
-     *        the stream we read data from
+     *            the stream we read data from
      * @throws BlenderFileException
-     *         an exception is thrown when the blend file is somehow invalid or corrupted
+     *             an exception is thrown when the blend file is somehow invalid or corrupted
      */
     public void fill(BlenderInputStream blenderInputStream) throws BlenderFileException {
         int dataToRead = 1;
@@ -107,9 +107,9 @@ class Field implements Cloneable {
                 }
                 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
+                // 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 {
@@ -200,28 +200,28 @@ class Field implements Cloneable {
     /**
      * This method parses the field name to determine how the field should be used.
      * @param nameBuilder
-     *        the name of the field (given as StringBuilder)
+     *            the name of the field (given as StringBuilder)
      * @throws BlenderFileException
-     *         this exception is thrown if the names contain errors
+     *             this exception is thrown if the names contain errors
      */
     private void parseField(StringBuilder nameBuilder) throws BlenderFileException {
         this.removeWhitespaces(nameBuilder);
-        //veryfying if the name is a pointer
+        // 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
+        // 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
+            // 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
+            List<Integer> lengths = new ArrayList<Integer>(3);// 3 dimensions will be enough in most cases
             do {
                 tableStartIndex = nameBuilder.indexOf("[");
                 if (tableStartIndex > 0) {
@@ -250,9 +250,9 @@ class Field implements Cloneable {
     /**
      * This method removes the required character from the text.
      * @param text
-     *        the text we remove characters from
+     *            the text we remove characters from
      * @param toRemove
-     *        the character to be removed
+     *            the character to be removed
      */
     private void removeCharacter(StringBuilder text, char toRemove) {
         for (int i = 0; i < text.length(); ++i) {
@@ -266,7 +266,7 @@ class Field implements Cloneable {
     /**
      * This method removes all whitespaces from the text.
      * @param text
-     *        the text we remove whitespaces from
+     *            the text we remove whitespaces from
      */
     private void removeWhitespaces(StringBuilder text) {
         for (int i = 0; i < text.length(); ++i) {
@@ -276,13 +276,13 @@ class Field implements Cloneable {
             }
         }
     }
-    
+
     /**
      * This method builds the full name of the field (with function, pointer and table indications).
      * @return the full name of the field
      */
     public String getFullName() {
-    	StringBuilder result = new StringBuilder();
+        StringBuilder result = new StringBuilder();
         if (function) {
             result.append('(');
         }
@@ -306,10 +306,10 @@ class Field implements Cloneable {
         StringBuilder result = new StringBuilder();
         result.append(this.getFullName());
 
-        //insert appropriate amount of spaces to format the output corrently
+        // 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(' ');// 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);

+ 29 - 30
engine/src/blender/com/jme3/scene/plugins/blender/file/FileBlockHeader.java

@@ -41,52 +41,51 @@ import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
  */
 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
+    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;
+    private int             code;
     /** Total length of the data after the file-block-header [4 bytes]. */
-    private int size;
+    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;
+    private long            oldMemoryAddress;
     /** Index of the SDNA structure [4 bytes]. */
-    private int sdnaIndex;
+    private int             sdnaIndex;
     /** Number of structure located in this file-block [4 bytes]. */
-    private int count;
+    private int             count;
     /** Start position of the block's data in the stream. */
-    private int blockPosition;
+    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
+     *            the stream we read the block header from
      * @param blenderContext
-     *        the blender context
+     *            the blender context
      * @throws BlenderFileException
-     *         this exception is thrown when the pointer size is neither 4 nor 8
+     *             this exception is thrown when the pointer size is neither 4 nor 8
      */
     public FileBlockHeader(BlenderInputStream inputStream, BlenderContext blenderContext) throws BlenderFileException {
         inputStream.alignPosition(4);
-        code = inputStream.readByte() << 24 | inputStream.readByte() << 16
-                | inputStream.readByte() << 8 | inputStream.readByte();
+        code = inputStream.readByte() << 24 | inputStream.readByte() << 16 | inputStream.readByte() << 8 | inputStream.readByte();
         size = inputStream.readInt();
         oldMemoryAddress = inputStream.readPointer();
         sdnaIndex = inputStream.readInt();
@@ -103,7 +102,7 @@ public class FileBlockHeader {
     /**
      * This method returns the structure described by the header filled with appropriate data.
      * @param blenderContext
-     *        the blender context
+     *            the blender context
      * @return structure filled with data
      * @throws BlenderFileException
      */
@@ -186,7 +185,7 @@ public class FileBlockHeader {
     /**
      * This method transforms the coded bloch id into a string value.
      * @param code
-     *        the id of the block
+     *            the id of the block
      * @return the string value of the block id
      */
     protected String codeToString(int code) {

+ 18 - 19
engine/src/blender/com/jme3/scene/plugins/blender/file/Pointer.java

@@ -45,20 +45,20 @@ public class Pointer {
     /** The blender context. */
     private BlenderContext blenderContext;
     /** The level of the pointer. */
-    private int pointerLevel;
+    private int            pointerLevel;
     /** The address in file it points to. */
-    private long oldMemoryAddress;
+    private long           oldMemoryAddress;
     /** This variable indicates if the field is a function pointer. */
-    public boolean function;
+    public boolean         function;
 
     /**
      * Constructr. Stores the basic data about the pointer.
      * @param pointerLevel
-     *        the level of the pointer
+     *            the level of the pointer
      * @param function
-     *        this variable indicates if the field is a function pointer
+     *            this variable indicates if the field is a function pointer
      * @param blenderContext
-     *        the repository f data; used in fetching the value that the pointer points
+     *            the repository f data; used in fetching the value that the pointer points
      */
     public Pointer(int pointerLevel, boolean function, BlenderContext blenderContext) {
         this.pointerLevel = pointerLevel;
@@ -70,7 +70,7 @@ public class Pointer {
      * 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
+     *            the stream we read the pointer value from
      */
     public void fill(BlenderInputStream inputStream) {
         oldMemoryAddress = inputStream.readPointer();
@@ -79,10 +79,10 @@ public class Pointer {
     /**
      * This method fetches the data stored under the given address.
      * @param inputStream
-     *        the stream we read data from
+     *            the stream we read data from
      * @return the data read from the file
      * @throws BlenderFileException
-     *         this exception is thrown when the blend file structure is somehow invalid or corrupted
+     *             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) {
@@ -90,9 +90,8 @@ public class Pointer {
         }
         List<Structure> structures = null;
         FileBlockHeader dataFileBlock = blenderContext.getFileBlock(oldMemoryAddress);
-        if(dataFileBlock == null) {
-        	throw new BlenderFileException("No data stored for address: " +oldMemoryAddress + 
-        			". Rarely blender makes mistakes when storing data. Try resaving the model after making minor changes. This usually helps.");
+        if (dataFileBlock == null) {
+            throw new BlenderFileException("No data stored for address: " + oldMemoryAddress + ". Rarely blender makes mistakes when storing data. Try resaving the model after making minor changes. This usually helps.");
         }
         if (pointerLevel > 1) {
             int pointersAmount = dataFileBlock.getSize() / inputStream.getPointerSize() * dataFileBlock.getCount();
@@ -108,12 +107,12 @@ public class Pointer {
                         structures.addAll(p.fetchData(inputStream));
                     }
                 } else {
-                	//it is necessary to put null's if the pointer is null, ie. in materials array that is attached to the mesh, the index
-                	//of the material is important, that is why we need null's to indicate that some materials' slots are empty
-                	if(structures == null) {
-                		structures = new ArrayList<Structure>();
-                	}
-                	structures.add(null);
+                    // it is necessary to put null's if the pointer is null, ie. in materials array that is attached to the mesh, the index
+                    // of the material is important, that is why we need null's to indicate that some materials' slots are empty
+                    if (structures == null) {
+                        structures = new ArrayList<Structure>();
+                    }
+                    structures.add(null);
                 }
             }
         } else {
@@ -144,7 +143,7 @@ public class Pointer {
     public boolean isNull() {
         return oldMemoryAddress == 0;
     }
-    
+
     /**
      * This method indicates if this is a null-pointer or not.
      * @return <b>true</b> if the pointer is not null and <b>false</b> otherwise

+ 41 - 41
engine/src/blender/com/jme3/scene/plugins/blender/file/Structure.java

@@ -49,20 +49,20 @@ public class Structure implements Cloneable {
     /** The address of the block that fills the structure. */
     private transient Long oldMemoryAddress;
     /** The type of the structure. */
-    private String type;
+    private String         type;
     /**
      * The fields of the structure. Each field consists of a pair: name-type.
      */
-    private Field[] fields;
+    private Field[]        fields;
 
     /**
      * Constructor that copies the data of the structure.
      * @param structure
-     *        the structure to copy.
+     *            the structure to copy.
      * @param blenderContext
-     *        the blender context of the structure
+     *            the blender context of the structure
      * @throws CloneNotSupportedException
-     *         this exception should never be thrown
+     *             this exception should never be thrown
      */
     private Structure(Structure structure, BlenderContext blenderContext) throws CloneNotSupportedException {
         type = structure.type;
@@ -77,15 +77,15 @@ public class Structure implements Cloneable {
     /**
      * Constructor. Loads the structure from the given stream during instance creation.
      * @param inputStream
-     *        the stream we read the structure from
+     *            the stream we read the structure from
      * @param names
-     *        the names from which the name of structure and its fields will be taken
+     *            the names from which the name of structure and its fields will be taken
      * @param types
-     *        the names of types for the structure
+     *            the names of types for the structure
      * @param blenderContext
-     *        the blender context
+     *            the blender context
      * @throws BlenderFileException
-     *         this exception occurs if the amount of fields, defined in the file, is negative
+     *             this exception occurs if the amount of fields, defined in the file, is negative
      */
     public Structure(BlenderInputStream inputStream, String[] names, String[] types, BlenderContext blenderContext) throws BlenderFileException {
         int nameIndex = inputStream.readShort();
@@ -109,10 +109,10 @@ public class Structure implements Cloneable {
     /**
      * 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
+     *            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
+     *             an exception is thrown when the blend file is somehow invalid or corrupted
      */
     public void fill(BlenderInputStream inputStream) throws BlenderFileException {
         int position = inputStream.getPosition();
@@ -127,7 +127,7 @@ public class Structure implements Cloneable {
     /**
      * This method returns the value of the filed with a given name.
      * @param fieldName
-     *        the name of the field
+     *            the name of the field
      * @return the value of the field or null if no field with a given name is found
      */
     public Object getFieldValue(String fieldName) {
@@ -143,7 +143,7 @@ public class Structure implements Cloneable {
      * 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
+     *            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) {
@@ -153,7 +153,7 @@ public class Structure implements Cloneable {
                 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
+                if (value != null) {// we can compare references here, since we use one static object as a NULL field value
                     return value;
                 }
             }
@@ -165,12 +165,12 @@ public class Structure implements Cloneable {
      * 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 blenderContext
-     *        the blender context
+     *            the blender context
      * @return a list of filled structures
      * @throws BlenderFileException
-     *         this exception is thrown when the blend file structure is somehow invalid or corrupted
+     *             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'
+     *             this exception is thrown if the type of the structure is not 'ListBase'
      */
     public List<Structure> evaluateListBase(BlenderContext blenderContext) throws BlenderFileException {
         if (!"ListBase".equals(this.type)) {
@@ -209,17 +209,17 @@ public class Structure implements Cloneable {
     /**
      * This method returns the field name of the given index.
      * @param fieldIndex
-     *        the index of the field
+     *            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 full field name of the given index.
      * @param fieldIndex
-     *        the index of the field
+     *            the index of the field
      * @return the full field name of the given index
      */
     public String getFieldFullName(int fieldIndex) {
@@ -229,7 +229,7 @@ public class Structure implements Cloneable {
     /**
      * This method returns the field type of the given index.
      * @param fieldIndex
-     *        the index of the field
+     *            the index of the field
      * @return the field type of the given index
      */
     public String getFieldType(int fieldIndex) {
@@ -248,20 +248,20 @@ public class Structure implements Cloneable {
         return oldMemoryAddress;
     }
 
-/**
-	 * 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() {
-		Object fieldValue = this.getFieldValue("ID");
-		if(fieldValue instanceof Structure) {
-			Structure id = (Structure)fieldValue;
-			return id == null ? null : id.getFieldValue("name").toString().substring(2);//blender adds 2-charactes as a name prefix
-		}
-		return null;
-	}
-	
+    /**
+     * 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() {
+        Object fieldValue = this.getFieldValue("ID");
+        if (fieldValue instanceof Structure) {
+            Structure id = (Structure) fieldValue;
+            return id == null ? null : id.getFieldValue("name").toString().substring(2);// blender adds 2-charactes as a name prefix
+        }
+        return null;
+    }
+
     @Override
     public String toString() {
         StringBuilder result = new StringBuilder("struct ").append(type).append(" {\n");
@@ -280,7 +280,7 @@ public class Structure implements Cloneable {
      * This enum enumerates all known data types that can be found in the blend file.
      * @author Marcin Roguski
      */
-    /*package*/
+    /* package */
     static enum DataType {
 
         CHARACTER, SHORT, INTEGER, LONG, FLOAT, DOUBLE, VOID, STRUCTURE, POINTER;
@@ -305,12 +305,12 @@ public class Structure implements Cloneable {
          * 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
+         *            the type name of the data
          * @param blenderContext
-         *        the blender context
+         *            the blender context
          * @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
+         *             this exception is thrown if the given type name does not exist in the blend file
          */
         public static DataType getDataType(String type, BlenderContext blenderContext) throws BlenderFileException {
             DataType result = PRIMARY_TYPES.get(type);

+ 22 - 22
engine/src/blender/com/jme3/scene/plugins/blender/lights/LightHelper.java

@@ -59,49 +59,49 @@ public class LightHelper 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
+     *            the version read from the blend file
      * @param fixUpAxis
-     *        a variable that indicates if the Y asxis is the UP axis or not
+     *            a variable that indicates if the Y asxis is the UP axis or not
      */
     public LightHelper(String blenderVersion, boolean fixUpAxis) {
         super(blenderVersion, fixUpAxis);
     }
 
     public LightNode toLight(Structure structure, BlenderContext blenderContext) throws BlenderFileException {
-    	LightNode result = (LightNode) blenderContext.getLoadedFeature(structure.getOldMemoryAddress(), LoadedFeatureDataType.LOADED_FEATURE);
+        LightNode result = (LightNode) blenderContext.getLoadedFeature(structure.getOldMemoryAddress(), LoadedFeatureDataType.LOADED_FEATURE);
         if (result != null) {
             return result;
         }
         Light light = null;
         int type = ((Number) structure.getFieldValue("type")).intValue();
         switch (type) {
-            case 0://Lamp
-            	light = new PointLight();
+            case 0:// Lamp
+                light = new PointLight();
                 float distance = ((Number) structure.getFieldValue("dist")).floatValue();
                 ((PointLight) light).setRadius(distance);
                 break;
-            case 1://Sun
+            case 1:// Sun
                 LOGGER.log(Level.WARNING, "'Sun' lamp is not supported in jMonkeyEngine.");
                 break;
-            case 2://Spot
-            	light = new SpotLight();
-            	//range
-            	((SpotLight)light).setSpotRange(((Number) structure.getFieldValue("dist")).floatValue());
-            	//outer angle
-            	float outerAngle = ((Number) structure.getFieldValue("spotsize")).floatValue()*FastMath.DEG_TO_RAD * 0.5f;
-            	((SpotLight)light).setSpotOuterAngle(outerAngle);
-            	
-            	//inner angle
-            	float spotblend = ((Number) structure.getFieldValue("spotblend")).floatValue();
+            case 2:// Spot
+                light = new SpotLight();
+                // range
+                ((SpotLight) light).setSpotRange(((Number) structure.getFieldValue("dist")).floatValue());
+                // outer angle
+                float outerAngle = ((Number) structure.getFieldValue("spotsize")).floatValue() * FastMath.DEG_TO_RAD * 0.5f;
+                ((SpotLight) light).setSpotOuterAngle(outerAngle);
+
+                // inner angle
+                float spotblend = ((Number) structure.getFieldValue("spotblend")).floatValue();
                 spotblend = FastMath.clamp(spotblend, 0, 1);
                 float innerAngle = outerAngle * (1 - spotblend);
-            	((SpotLight)light).setSpotInnerAngle(innerAngle);
+                ((SpotLight) light).setSpotInnerAngle(innerAngle);
                 break;
-            case 3://Hemi
+            case 3:// Hemi
                 LOGGER.log(Level.WARNING, "'Hemi' lamp is not supported in jMonkeyEngine.");
                 break;
-            case 4://Area
-            	light = new DirectionalLight();
+            case 4:// Area
+                light = new DirectionalLight();
                 break;
             default:
                 throw new BlenderFileException("Unknown light source type: " + type);
@@ -115,9 +115,9 @@ public class LightHelper extends AbstractBlenderHelper {
         }
         return result;
     }
-    
+
     @Override
     public boolean shouldBeLoaded(Structure structure, BlenderContext blenderContext) {
-    	return (blenderContext.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.LIGHTS) != 0;
+        return (blenderContext.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.LIGHTS) != 0;
     }
 }

+ 18 - 18
engine/src/blender/com/jme3/scene/plugins/blender/materials/IAlphaMask.java

@@ -4,23 +4,23 @@ package com.jme3.scene.plugins.blender.materials;
  * An interface used in calculating alpha mask during particles' texture calculations.
  * @author Marcin Roguski (Kaelthas)
  */
-/*package*/ 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);
+/* package */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);
+    /**
+     * 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);
 }

+ 360 - 361
engine/src/blender/com/jme3/scene/plugins/blender/materials/MaterialContext.java

@@ -36,378 +36,377 @@ import com.jme3.util.BufferUtils;
  * @author Marcin Roguski (Kaelthas)
  */
 public final class MaterialContext {
-	private static final Logger			LOGGER				= Logger.getLogger(MaterialContext.class.getName());
-
-	//texture mapping types
-	public static final int				MTEX_COL = 0x01;
-	public static final int				MTEX_NOR = 0x02;
-	public static final int				MTEX_SPEC = 0x04;
-	public static final int				MTEX_EMIT = 0x40;
-	public static final int				MTEX_ALPHA = 0x80;
-	
-	/* package */final String			name;
-	/* package */final Map<Number, CombinedTexture> loadedTextures;
-
-	/* package */final ColorRGBA		diffuseColor;
-	/* package */final DiffuseShader 	diffuseShader;
-	/* package */final SpecularShader 	specularShader;
-	/* package */final ColorRGBA		specularColor;
-	/* package */final ColorRGBA		ambientColor;
-	/* package */final float 			shininess;
-	/* package */final boolean			shadeless;
-	/* package */final boolean			vertexColor;
-	/* package */final boolean			transparent;
-	/* package */final boolean			vTangent;
-	/* package */FaceCullMode 			faceCullMode;
-	
-	@SuppressWarnings("unchecked")
-	/* package */MaterialContext(Structure structure, BlenderContext blenderContext) throws BlenderFileException {
-		name = structure.getName();
-
-		int mode = ((Number) structure.getFieldValue("mode")).intValue();
-		shadeless = (mode & 0x4) != 0;
-		vertexColor = (mode & 0x80) != 0;
-		vTangent = (mode & 0x4000000) != 0; // NOTE: Requires tangents
-
-		int diff_shader = ((Number) structure.getFieldValue("diff_shader")).intValue();
-		diffuseShader = DiffuseShader.values()[diff_shader];
-		
-		if(this.shadeless) {
+    private static final Logger                     LOGGER     = Logger.getLogger(MaterialContext.class.getName());
+
+    // texture mapping types
+    public static final int                         MTEX_COL   = 0x01;
+    public static final int                         MTEX_NOR   = 0x02;
+    public static final int                         MTEX_SPEC  = 0x04;
+    public static final int                         MTEX_EMIT  = 0x40;
+    public static final int                         MTEX_ALPHA = 0x80;
+
+    /* package */final String                       name;
+    /* package */final Map<Number, CombinedTexture> loadedTextures;
+
+    /* package */final ColorRGBA                    diffuseColor;
+    /* package */final DiffuseShader                diffuseShader;
+    /* package */final SpecularShader               specularShader;
+    /* package */final ColorRGBA                    specularColor;
+    /* package */final ColorRGBA                    ambientColor;
+    /* package */final float                        shininess;
+    /* package */final boolean                      shadeless;
+    /* package */final boolean                      vertexColor;
+    /* package */final boolean                      transparent;
+    /* package */final boolean                      vTangent;
+    /* package */FaceCullMode                       faceCullMode;
+
+    @SuppressWarnings("unchecked")
+    /* package */MaterialContext(Structure structure, BlenderContext blenderContext) throws BlenderFileException {
+        name = structure.getName();
+
+        int mode = ((Number) structure.getFieldValue("mode")).intValue();
+        shadeless = (mode & 0x4) != 0;
+        vertexColor = (mode & 0x80) != 0;
+        vTangent = (mode & 0x4000000) != 0; // NOTE: Requires tangents
+
+        int diff_shader = ((Number) structure.getFieldValue("diff_shader")).intValue();
+        diffuseShader = DiffuseShader.values()[diff_shader];
+
+        if (this.shadeless) {
             float r = ((Number) structure.getFieldValue("r")).floatValue();
             float g = ((Number) structure.getFieldValue("g")).floatValue();
             float b = ((Number) structure.getFieldValue("b")).floatValue();
             float alpha = ((Number) structure.getFieldValue("alpha")).floatValue();
 
-			diffuseColor = new ColorRGBA(r, g, b, alpha);
-			specularShader = null;
-			specularColor = ambientColor = null;
-			shininess = 0.0f;
-		} else {
-			diffuseColor = this.readDiffuseColor(structure, diffuseShader);
-			
-			int spec_shader = ((Number) structure.getFieldValue("spec_shader")).intValue();
-			specularShader = SpecularShader.values()[spec_shader];
-			specularColor = this.readSpecularColor(structure, specularShader);
-			
-			float r = ((Number) structure.getFieldValue("ambr")).floatValue();
-			float g = ((Number) structure.getFieldValue("ambg")).floatValue();
-			float b = ((Number) structure.getFieldValue("ambb")).floatValue();
-			float alpha = ((Number) structure.getFieldValue("alpha")).floatValue();
-			ambientColor = new ColorRGBA(r, g, b, alpha);
-			
-			float shininess = ((Number) structure.getFieldValue("emit")).floatValue();
-			this.shininess = shininess > 0.0f ? shininess : MaterialHelper.DEFAULT_SHININESS;
-		}
-		
-		DynamicArray<Pointer> mtexsArray = (DynamicArray<Pointer>) structure.getFieldValue("mtex");
-		int separatedTextures = ((Number) structure.getFieldValue("septex")).intValue();
-		List<TextureData> texturesList = new ArrayList<TextureData>();
-		for (int i = 0; i < mtexsArray.getTotalSize(); ++i) {
-			Pointer p = mtexsArray.get(i);
-			if (p.isNotNull() && (separatedTextures & 1 << i) == 0) {
-				TextureData textureData = new TextureData();
-				textureData.mtex = p.fetchData(blenderContext.getInputStream()).get(0);
-				textureData.uvCoordinatesType = ((Number) textureData.mtex.getFieldValue("texco")).intValue();
-				textureData.projectionType = ((Number) textureData.mtex.getFieldValue("mapping")).intValue();
-
-				Pointer pTex = (Pointer) textureData.mtex.getFieldValue("tex");
-				if (pTex.isNotNull()) {
-					Structure tex = pTex.fetchData(blenderContext.getInputStream()).get(0);
-					textureData.textureStructure = tex;
-					texturesList.add(textureData);
-				}
-			}
-		}
-		
-		//loading the textures and merging them
-		Map<Number, List<TextureData>> textureDataMap = this.sortAndFilterTextures(texturesList);
-		loadedTextures = new HashMap<Number, CombinedTexture>();
-		float[] diffuseColorArray = new float[] {diffuseColor.r, diffuseColor.g, diffuseColor.b, diffuseColor.a};
-		TextureHelper textureHelper = blenderContext.getHelper(TextureHelper.class);
-		for(Entry<Number, List<TextureData>> entry : textureDataMap.entrySet()) {
-			if(entry.getValue().size()>0) {
-				CombinedTexture combinedTexture = new CombinedTexture(entry.getKey().intValue());
-				for(TextureData textureData : entry.getValue()) {
-					int texflag = ((Number) textureData.mtex.getFieldValue("texflag")).intValue();
-					boolean negateTexture = (texflag & 0x04) != 0;
-					Texture texture = textureHelper.getTexture(textureData.textureStructure, textureData.mtex, blenderContext);
-					if(texture != null) {
-						int blendType = ((Number) textureData.mtex.getFieldValue("blendtype")).intValue();
-						float[] color = new float[] { ((Number) textureData.mtex.getFieldValue("r")).floatValue(), 
-													  ((Number) textureData.mtex.getFieldValue("g")).floatValue(), 
-													  ((Number) textureData.mtex.getFieldValue("b")).floatValue() };
-						float colfac = ((Number) textureData.mtex.getFieldValue("colfac")).floatValue();
-						TextureBlender textureBlender = TextureBlenderFactory.createTextureBlender(texture.getImage().getFormat(),
-								texflag, negateTexture, blendType, diffuseColorArray, color, colfac);
-						combinedTexture.add(texture, textureBlender, textureData.uvCoordinatesType, textureData.projectionType, textureData.textureStructure, blenderContext);
-					}
-				}
-				if(combinedTexture.getTexturesCount() > 0) {
-					loadedTextures.put(entry.getKey(), combinedTexture);
-				}
-			}
-		}
-
-		//veryfying if  the transparency is present
-		//(in blender transparent mask is 0x10000 but its better to verify it because blender can indicate transparency when
-		//it is not required
-		boolean transparent = false;
-		if(diffuseColor != null) {
-			transparent = diffuseColor.a < 1.0f;
-			if(textureDataMap.size() > 0) {//texutre covers the material color
-				diffuseColor.set(1, 1, 1, 1);
-			}
-		}
-		if(specularColor != null) {
-			transparent = transparent || specularColor.a < 1.0f;
-		}
-		if(ambientColor != null) {
-			transparent = transparent || ambientColor.a < 1.0f;
-		}
-		this.transparent = transparent;
-	}
-	
-	public void applyMaterial(Geometry geometry, Long geometriesOMA, List<Vector2f> userDefinedUVCoordinates, BlenderContext blenderContext) {
-		Material material = null;
-		if (shadeless) {
-			material = new Material(blenderContext.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");
-            
+            diffuseColor = new ColorRGBA(r, g, b, alpha);
+            specularShader = null;
+            specularColor = ambientColor = null;
+            shininess = 0.0f;
+        } else {
+            diffuseColor = this.readDiffuseColor(structure, diffuseShader);
+
+            int spec_shader = ((Number) structure.getFieldValue("spec_shader")).intValue();
+            specularShader = SpecularShader.values()[spec_shader];
+            specularColor = this.readSpecularColor(structure, specularShader);
+
+            float r = ((Number) structure.getFieldValue("ambr")).floatValue();
+            float g = ((Number) structure.getFieldValue("ambg")).floatValue();
+            float b = ((Number) structure.getFieldValue("ambb")).floatValue();
+            float alpha = ((Number) structure.getFieldValue("alpha")).floatValue();
+            ambientColor = new ColorRGBA(r, g, b, alpha);
+
+            float shininess = ((Number) structure.getFieldValue("emit")).floatValue();
+            this.shininess = shininess > 0.0f ? shininess : MaterialHelper.DEFAULT_SHININESS;
+        }
+
+        DynamicArray<Pointer> mtexsArray = (DynamicArray<Pointer>) structure.getFieldValue("mtex");
+        int separatedTextures = ((Number) structure.getFieldValue("septex")).intValue();
+        List<TextureData> texturesList = new ArrayList<TextureData>();
+        for (int i = 0; i < mtexsArray.getTotalSize(); ++i) {
+            Pointer p = mtexsArray.get(i);
+            if (p.isNotNull() && (separatedTextures & 1 << i) == 0) {
+                TextureData textureData = new TextureData();
+                textureData.mtex = p.fetchData(blenderContext.getInputStream()).get(0);
+                textureData.uvCoordinatesType = ((Number) textureData.mtex.getFieldValue("texco")).intValue();
+                textureData.projectionType = ((Number) textureData.mtex.getFieldValue("mapping")).intValue();
+
+                Pointer pTex = (Pointer) textureData.mtex.getFieldValue("tex");
+                if (pTex.isNotNull()) {
+                    Structure tex = pTex.fetchData(blenderContext.getInputStream()).get(0);
+                    textureData.textureStructure = tex;
+                    texturesList.add(textureData);
+                }
+            }
+        }
+
+        // loading the textures and merging them
+        Map<Number, List<TextureData>> textureDataMap = this.sortAndFilterTextures(texturesList);
+        loadedTextures = new HashMap<Number, CombinedTexture>();
+        float[] diffuseColorArray = new float[] { diffuseColor.r, diffuseColor.g, diffuseColor.b, diffuseColor.a };
+        TextureHelper textureHelper = blenderContext.getHelper(TextureHelper.class);
+        for (Entry<Number, List<TextureData>> entry : textureDataMap.entrySet()) {
+            if (entry.getValue().size() > 0) {
+                CombinedTexture combinedTexture = new CombinedTexture(entry.getKey().intValue());
+                for (TextureData textureData : entry.getValue()) {
+                    int texflag = ((Number) textureData.mtex.getFieldValue("texflag")).intValue();
+                    boolean negateTexture = (texflag & 0x04) != 0;
+                    Texture texture = textureHelper.getTexture(textureData.textureStructure, textureData.mtex, blenderContext);
+                    if (texture != null) {
+                        int blendType = ((Number) textureData.mtex.getFieldValue("blendtype")).intValue();
+                        float[] color = new float[] { ((Number) textureData.mtex.getFieldValue("r")).floatValue(), ((Number) textureData.mtex.getFieldValue("g")).floatValue(), ((Number) textureData.mtex.getFieldValue("b")).floatValue() };
+                        float colfac = ((Number) textureData.mtex.getFieldValue("colfac")).floatValue();
+                        TextureBlender textureBlender = TextureBlenderFactory.createTextureBlender(texture.getImage().getFormat(), texflag, negateTexture, blendType, diffuseColorArray, color, colfac);
+                        combinedTexture.add(texture, textureBlender, textureData.uvCoordinatesType, textureData.projectionType, textureData.textureStructure, blenderContext);
+                    }
+                }
+                if (combinedTexture.getTexturesCount() > 0) {
+                    loadedTextures.put(entry.getKey(), combinedTexture);
+                }
+            }
+        }
+
+        // veryfying if the transparency is present
+        // (in blender transparent mask is 0x10000 but its better to verify it because blender can indicate transparency when
+        // it is not required
+        boolean transparent = false;
+        if (diffuseColor != null) {
+            transparent = diffuseColor.a < 1.0f;
+            if (textureDataMap.size() > 0) {// texutre covers the material color
+                diffuseColor.set(1, 1, 1, 1);
+            }
+        }
+        if (specularColor != null) {
+            transparent = transparent || specularColor.a < 1.0f;
+        }
+        if (ambientColor != null) {
+            transparent = transparent || ambientColor.a < 1.0f;
+        }
+        this.transparent = transparent;
+    }
+
+    public void applyMaterial(Geometry geometry, Long geometriesOMA, List<Vector2f> userDefinedUVCoordinates, BlenderContext blenderContext) {
+        Material material = null;
+        if (shadeless) {
+            material = new Material(blenderContext.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");
+
             if (!transparent) {
                 diffuseColor.a = 1;
             }
-            
+
             material.setColor("Color", diffuseColor);
-		} else {
-			material = new Material(blenderContext.getAssetManager(), "Common/MatDefs/Light/Lighting.j3md");
-			material.setBoolean("UseMaterialColors", Boolean.TRUE);
-
-			// setting the colors
-			material.setBoolean("Minnaert", diffuseShader == DiffuseShader.MINNAERT);
-			if (!transparent) {
-				diffuseColor.a = 1;
-			}
-			material.setColor("Diffuse", diffuseColor);
-
-			material.setBoolean("WardIso", specularShader == SpecularShader.WARDISO);
-			material.setColor("Specular", specularColor);
-
-			material.setColor("Ambient", ambientColor);
-			material.setFloat("Shininess", shininess);
-		}
-		
-		//applying textures
-		if(loadedTextures != null && loadedTextures.size() > 0) {
-			Entry<Number, CombinedTexture> basicUVSOwner = null;
-			for(Entry<Number, CombinedTexture> entry : loadedTextures.entrySet()) {
-				CombinedTexture combinedTexture = entry.getValue();
-				combinedTexture.flatten(geometry, geometriesOMA, userDefinedUVCoordinates, blenderContext);
-				
-				if(basicUVSOwner == null) {
-					basicUVSOwner = entry;
-				} else {
-					combinedTexture.castToUVS(basicUVSOwner.getValue(), blenderContext);
-					this.setTexture(material, entry.getKey().intValue(), combinedTexture.getResultTexture());
-				}
-			}
-			
-			if(basicUVSOwner != null) {
-				this.setTexture(material, basicUVSOwner.getKey().intValue(), basicUVSOwner.getValue().getResultTexture());
-				List<Vector2f> basicUVS = basicUVSOwner.getValue().getResultUVS();
-				VertexBuffer uvCoordsBuffer = new VertexBuffer(VertexBuffer.Type.TexCoord);
-	            uvCoordsBuffer.setupData(Usage.Static, 2, Format.Float, BufferUtils.createFloatBuffer(basicUVS.toArray(new Vector2f[basicUVS.size()])));
-				geometry.getMesh().setBuffer(uvCoordsBuffer);
-			}
-		} else if(userDefinedUVCoordinates != null && userDefinedUVCoordinates.size() > 0) {
-			VertexBuffer uvCoordsBuffer = new VertexBuffer(VertexBuffer.Type.TexCoord);
-            uvCoordsBuffer.setupData(Usage.Static, 2, Format.Float,
-                    BufferUtils.createFloatBuffer(userDefinedUVCoordinates.toArray(new Vector2f[userDefinedUVCoordinates.size()])));
-			geometry.getMesh().setBuffer(uvCoordsBuffer);
-		}
-		
-		//applying additional data
-		material.setName(name);
-		if (vertexColor) {
-			material.setBoolean(shadeless ? "VertexColor" : "UseVertexColor", true);
-		}
-		if(this.faceCullMode != null) {
-			material.getAdditionalRenderState().setFaceCullMode(faceCullMode);
-		} else {
-			material.getAdditionalRenderState().setFaceCullMode(blenderContext.getBlenderKey().getFaceCullMode());
-		}
+        } else {
+            material = new Material(blenderContext.getAssetManager(), "Common/MatDefs/Light/Lighting.j3md");
+            material.setBoolean("UseMaterialColors", Boolean.TRUE);
+
+            // setting the colors
+            material.setBoolean("Minnaert", diffuseShader == DiffuseShader.MINNAERT);
+            if (!transparent) {
+                diffuseColor.a = 1;
+            }
+            material.setColor("Diffuse", diffuseColor);
+
+            material.setBoolean("WardIso", specularShader == SpecularShader.WARDISO);
+            material.setColor("Specular", specularColor);
+
+            material.setColor("Ambient", ambientColor);
+            material.setFloat("Shininess", shininess);
+        }
+
+        // applying textures
+        if (loadedTextures != null && loadedTextures.size() > 0) {
+            Entry<Number, CombinedTexture> basicUVSOwner = null;
+            for (Entry<Number, CombinedTexture> entry : loadedTextures.entrySet()) {
+                CombinedTexture combinedTexture = entry.getValue();
+                combinedTexture.flatten(geometry, geometriesOMA, userDefinedUVCoordinates, blenderContext);
+
+                if (basicUVSOwner == null) {
+                    basicUVSOwner = entry;
+                } else {
+                    combinedTexture.castToUVS(basicUVSOwner.getValue(), blenderContext);
+                    this.setTexture(material, entry.getKey().intValue(), combinedTexture.getResultTexture());
+                }
+            }
+
+            if (basicUVSOwner != null) {
+                this.setTexture(material, basicUVSOwner.getKey().intValue(), basicUVSOwner.getValue().getResultTexture());
+                List<Vector2f> basicUVS = basicUVSOwner.getValue().getResultUVS();
+                VertexBuffer uvCoordsBuffer = new VertexBuffer(VertexBuffer.Type.TexCoord);
+                uvCoordsBuffer.setupData(Usage.Static, 2, Format.Float, BufferUtils.createFloatBuffer(basicUVS.toArray(new Vector2f[basicUVS.size()])));
+                geometry.getMesh().setBuffer(uvCoordsBuffer);
+            }
+        } else if (userDefinedUVCoordinates != null && userDefinedUVCoordinates.size() > 0) {
+            VertexBuffer uvCoordsBuffer = new VertexBuffer(VertexBuffer.Type.TexCoord);
+            uvCoordsBuffer.setupData(Usage.Static, 2, Format.Float, BufferUtils.createFloatBuffer(userDefinedUVCoordinates.toArray(new Vector2f[userDefinedUVCoordinates.size()])));
+            geometry.getMesh().setBuffer(uvCoordsBuffer);
+        }
+
+        // applying additional data
+        material.setName(name);
+        if (vertexColor) {
+            material.setBoolean(shadeless ? "VertexColor" : "UseVertexColor", true);
+        }
+        if (this.faceCullMode != null) {
+            material.getAdditionalRenderState().setFaceCullMode(faceCullMode);
+        } else {
+            material.getAdditionalRenderState().setFaceCullMode(blenderContext.getBlenderKey().getFaceCullMode());
+        }
         if (transparent) {
-        	material.setTransparent(true);
-        	material.getAdditionalRenderState().setBlendMode(BlendMode.Alpha);
+            material.setTransparent(true);
+            material.getAdditionalRenderState().setBlendMode(BlendMode.Alpha);
             geometry.setQueueBucket(Bucket.Transparent);
         }
-        
+
         geometry.setMaterial(material);
-	}
-	
-	/**
-	 * Sets the texture to the given material.
-	 * 
-	 * @param material
-	 *            the material that we add texture to
-	 * @param mapTo
-	 *            the texture mapping type
-	 * @param texture
-	 *            the added texture
-	 */
-	private void setTexture(Material material, int mapTo, Texture texture) {
-		switch (mapTo) {
-			case MTEX_COL:
-				material.setTexture(shadeless ? MaterialHelper.TEXTURE_TYPE_COLOR : MaterialHelper.TEXTURE_TYPE_DIFFUSE, texture);
-				break;
-			case MTEX_NOR:
-				material.setTexture(MaterialHelper.TEXTURE_TYPE_NORMAL, texture);
-				break;
-			case MTEX_SPEC:
-				material.setTexture(MaterialHelper.TEXTURE_TYPE_SPECULAR, texture);
-				break;
-			case MTEX_EMIT:
-				material.setTexture(MaterialHelper.TEXTURE_TYPE_GLOW, texture);
-				break;
-			case MTEX_ALPHA:
-				if(!shadeless) {
-					material.setTexture(MaterialHelper.TEXTURE_TYPE_ALPHA, texture);
-				} else {
-					LOGGER.warning("JME does not support alpha map on unshaded material. Material name is " + name);
-				}
-				break;
-			default:
-				LOGGER.severe("Unknown mapping type: " + mapTo);
-		}
-	}
-	
-	/**
-	 * @return <b>true</b> if the material has at least one generated texture and <b>false</b> otherwise
-	 */
-	public boolean hasGeneratedTextures() {
-		if(loadedTextures != null) {
-			for(Entry<Number, CombinedTexture> entry : loadedTextures.entrySet()) {
-				if(entry.getValue().hasGeneratedTextures()) {
-					return true;
-				}
-			}
-		}
-		return false;
-	}
-	
-	/**
-	 * This method sorts the textures by their mapping type. In each group only
-	 * textures of one type are put (either two- or three-dimensional). If the
-	 * mapping type is MTEX_COL then if the texture has no alpha channel then
-	 * all textures before it are discarded and will not be loaded and merged
-	 * because texture with no alpha will cover them anyway.
-	 * 
-	 * @return a map with sorted and filtered textures
-	 */
-	private Map<Number, List<TextureData>> sortAndFilterTextures(List<TextureData> textures) {
-		int[] mappings = new int[] { MTEX_COL, MTEX_NOR, MTEX_EMIT, MTEX_SPEC, MTEX_ALPHA };
-		Map<Number, List<TextureData>> result = new HashMap<Number, List<TextureData>>();
-		for (TextureData data : textures) {
-			Number mapto = (Number) data.mtex.getFieldValue("mapto");
-			for (int i = 0; i < mappings.length; ++i) {
-				if((mappings[i] & mapto.intValue()) != 0) {
-					List<TextureData> datas = result.get(mappings[i]);
-					if (datas == null) {
-						datas = new ArrayList<TextureData>();
-						result.put(mappings[i], datas);
-					}
-					datas.add(data);
-				}
-			}
-		}
-		return result;
-	}
-	
-	/**
-	 * This method sets the face cull mode.
-	 * @param faceCullMode the face cull mode
-	 */
-	public void setFaceCullMode(FaceCullMode faceCullMode) {
-		this.faceCullMode = faceCullMode;
-	}
-	
-	/**
-	 * @return the face cull mode
-	 */
-	public FaceCullMode getFaceCullMode() {
-		return faceCullMode;
-	}
-	
-	/**
-	 * This method returns the diffuse color.
-	 * 
-	 * @param materialStructure the material structure
-	 * @param diffuseShader the diffuse shader
-	 * @return the diffuse color
-	 */
-	private ColorRGBA readDiffuseColor(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 a specular color used by the material.
-	 * 
-	 * @param materialStructure
-	 *        the material structure filled with data
-	 * @return a specular color used by the material
-	 */
-	private ColorRGBA readSpecularColor(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);
-	}
-	
-	private static class TextureData {
-		Structure mtex;
-		Structure textureStructure;
-		int uvCoordinatesType;
-		int projectionType;
-	}
+    }
+
+    /**
+     * Sets the texture to the given material.
+     * 
+     * @param material
+     *            the material that we add texture to
+     * @param mapTo
+     *            the texture mapping type
+     * @param texture
+     *            the added texture
+     */
+    private void setTexture(Material material, int mapTo, Texture texture) {
+        switch (mapTo) {
+            case MTEX_COL:
+                material.setTexture(shadeless ? MaterialHelper.TEXTURE_TYPE_COLOR : MaterialHelper.TEXTURE_TYPE_DIFFUSE, texture);
+                break;
+            case MTEX_NOR:
+                material.setTexture(MaterialHelper.TEXTURE_TYPE_NORMAL, texture);
+                break;
+            case MTEX_SPEC:
+                material.setTexture(MaterialHelper.TEXTURE_TYPE_SPECULAR, texture);
+                break;
+            case MTEX_EMIT:
+                material.setTexture(MaterialHelper.TEXTURE_TYPE_GLOW, texture);
+                break;
+            case MTEX_ALPHA:
+                if (!shadeless) {
+                    material.setTexture(MaterialHelper.TEXTURE_TYPE_ALPHA, texture);
+                } else {
+                    LOGGER.warning("JME does not support alpha map on unshaded material. Material name is " + name);
+                }
+                break;
+            default:
+                LOGGER.severe("Unknown mapping type: " + mapTo);
+        }
+    }
+
+    /**
+     * @return <b>true</b> if the material has at least one generated texture and <b>false</b> otherwise
+     */
+    public boolean hasGeneratedTextures() {
+        if (loadedTextures != null) {
+            for (Entry<Number, CombinedTexture> entry : loadedTextures.entrySet()) {
+                if (entry.getValue().hasGeneratedTextures()) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * This method sorts the textures by their mapping type. In each group only
+     * textures of one type are put (either two- or three-dimensional). If the
+     * mapping type is MTEX_COL then if the texture has no alpha channel then
+     * all textures before it are discarded and will not be loaded and merged
+     * because texture with no alpha will cover them anyway.
+     * 
+     * @return a map with sorted and filtered textures
+     */
+    private Map<Number, List<TextureData>> sortAndFilterTextures(List<TextureData> textures) {
+        int[] mappings = new int[] { MTEX_COL, MTEX_NOR, MTEX_EMIT, MTEX_SPEC, MTEX_ALPHA };
+        Map<Number, List<TextureData>> result = new HashMap<Number, List<TextureData>>();
+        for (TextureData data : textures) {
+            Number mapto = (Number) data.mtex.getFieldValue("mapto");
+            for (int i = 0; i < mappings.length; ++i) {
+                if ((mappings[i] & mapto.intValue()) != 0) {
+                    List<TextureData> datas = result.get(mappings[i]);
+                    if (datas == null) {
+                        datas = new ArrayList<TextureData>();
+                        result.put(mappings[i], datas);
+                    }
+                    datas.add(data);
+                }
+            }
+        }
+        return result;
+    }
+
+    /**
+     * This method sets the face cull mode.
+     * @param faceCullMode
+     *            the face cull mode
+     */
+    public void setFaceCullMode(FaceCullMode faceCullMode) {
+        this.faceCullMode = faceCullMode;
+    }
+
+    /**
+     * @return the face cull mode
+     */
+    public FaceCullMode getFaceCullMode() {
+        return faceCullMode;
+    }
+
+    /**
+     * This method returns the diffuse color.
+     * 
+     * @param materialStructure
+     *            the material structure
+     * @param diffuseShader
+     *            the diffuse shader
+     * @return the diffuse color
+     */
+    private ColorRGBA readDiffuseColor(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 a specular color used by the material.
+     * 
+     * @param materialStructure
+     *            the material structure filled with data
+     * @return a specular color used by the material
+     */
+    private ColorRGBA readSpecularColor(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);
+    }
+
+    private static class TextureData {
+        Structure mtex;
+        Structure textureStructure;
+        int       uvCoordinatesType;
+        int       projectionType;
+    }
 }

+ 407 - 406
engine/src/blender/com/jme3/scene/plugins/blender/materials/MaterialHelper.java

@@ -57,410 +57,411 @@ import java.util.logging.Level;
 import java.util.logging.Logger;
 
 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
-	}
-
-	/**
-	 * This constructor parses the given blender version and stores the result. Some functionalities may differ in different blender
-	 * versions.
-	 * 
-	 * @param blenderVersion
-	 *        the version read from the blend file
-	 * @param fixUpAxis
-     *        a variable that indicates if the Y asxis is the UP axis or not
-	 */
-	public MaterialHelper(String blenderVersion, boolean fixUpAxis) {
-		super(blenderVersion, false);
-		// setting alpha masks
-		alphaMasks.put(ALPHA_MASK_NONE, new IAlphaMask() {
-			public void setImageSize(int width, int height) {}
-
-			public byte getAlpha(float x, float y) {
-				return (byte) 255;
-			}
-		});
-		alphaMasks.put(ALPHA_MASK_CIRCLE, new IAlphaMask() {
-			private float	r;
-			private float[]	center;
-
-			public void setImageSize(int width, int height) {
-				r = Math.min(width, height) * 0.5f;
-				center = new float[] { width * 0.5f, height * 0.5f };
-			}
-
-			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;
-
-			public void setImageSize(int width, int height) {
-				r = Math.min(width, height) * 0.5f;
-				center = new float[] { width * 0.5f, height * 0.5f };
-			}
-
-			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;
-
-			public void setImageSize(int width, int height) {
-				r = Math.min(width, height) * 0.5f;
-				center = new float[] { width * 0.5f, height * 0.5f };
-			}
-
-			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 converts the material structure to jme Material.
-	 * @param structure
-	 *        structure with material data
-	 * @param blenderContext
-	 *        the blender context
-	 * @return jme material
-	 * @throws BlenderFileException
-	 *         an exception is throw when problems with blend file occur
-	 */
-	public MaterialContext toMaterialContext(Structure structure, BlenderContext blenderContext) throws BlenderFileException {
-		LOGGER.log(Level.FINE, "Loading material.");
-		MaterialContext result = (MaterialContext) blenderContext.getLoadedFeature(structure.getOldMemoryAddress(), LoadedFeatureDataType.LOADED_FEATURE);
-		if (result != null) {
-			return result;
-		}
-		
-		result = new MaterialContext(structure, blenderContext);
-		LOGGER.log(Level.FINE, "Material''s name: {0}", result.name);
-		blenderContext.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 blenderContext
-	 *        the blender context
-	 * @return material converted into particles-usable material
-	 */
-	public Material getParticlesMaterial(Material material, Integer alphaMaskIndex, BlenderContext blenderContext) {
-		Material result = new Material(blenderContext.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 any kind of texture.
-	 * 
-	 * @param material
-	 *        the material
-	 * @return <b>true</b> if the texture exists in the material and <B>false</b> otherwise
-	 */
-	public boolean hasTexture(Material material) {
-		if (material != null) {
-			if (material.getTextureParam(TEXTURE_TYPE_ALPHA) != null) {
-				return true;
-			}
-			if (material.getTextureParam(TEXTURE_TYPE_COLOR) != null) {
-				return true;
-			}
-			if (material.getTextureParam(TEXTURE_TYPE_DIFFUSE) != null) {
-				return true;
-			}
-			if (material.getTextureParam(TEXTURE_TYPE_GLOW) != null) {
-				return true;
-			}
-			if (material.getTextureParam(TEXTURE_TYPE_NORMAL) != null) {
-				return true;
-			}
-			if (material.getTextureParam(TEXTURE_TYPE_SPECULAR) != null) {
-				return true;
-			}
-		}
-		return false;
-	}
-
-	/**
-	 * 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 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 blenderContext
-	 *        the blender context
-	 * @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 MaterialContext[] getMaterials(Structure structureWithMaterials, BlenderContext blenderContext) throws BlenderFileException {
-		Pointer ppMaterials = (Pointer) structureWithMaterials.getFieldValue("mat");
-		MaterialContext[] materials = null;
-		if (ppMaterials.isNotNull()) {
-			List<Structure> materialStructures = ppMaterials.fetchData(blenderContext.getInputStream());
-			if (materialStructures != null && materialStructures.size() > 0) {
-				MaterialHelper materialHelper = blenderContext.getHelper(MaterialHelper.class);
-				materials = new MaterialContext[materialStructures.size()];
-				int i = 0;
-				for (Structure s : materialStructures) {
-					materials[i++] = s == null ? null : materialHelper.toMaterialContext(s, blenderContext);
-				}
-			}
-		}
-		return materials;
-	}
-
-	/**
-	 * This method converts rgb values to hsv values.
-	 * 
-	 * @param r
-	 *        red value of the color
-         * @param g
-         *        green value of the color
-         * @param b
-         *        blue value 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;
-			}
-		}
-	}
-
-	@Override
-	public boolean shouldBeLoaded(Structure structure, BlenderContext blenderContext) {
-		return (blenderContext.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.MATERIALS) != 0;
-	}
+    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
+    }
+
+    /**
+     * This constructor parses the given blender version and stores the result. Some functionalities may differ in different blender
+     * versions.
+     * 
+     * @param blenderVersion
+     *            the version read from the blend file
+     * @param fixUpAxis
+     *            a variable that indicates if the Y asxis is the UP axis or not
+     */
+    public MaterialHelper(String blenderVersion, boolean fixUpAxis) {
+        super(blenderVersion, false);
+        // setting alpha masks
+        alphaMasks.put(ALPHA_MASK_NONE, new IAlphaMask() {
+            public void setImageSize(int width, int height) {
+            }
+
+            public byte getAlpha(float x, float y) {
+                return (byte) 255;
+            }
+        });
+        alphaMasks.put(ALPHA_MASK_CIRCLE, new IAlphaMask() {
+            private float   r;
+            private float[] center;
+
+            public void setImageSize(int width, int height) {
+                r = Math.min(width, height) * 0.5f;
+                center = new float[] { width * 0.5f, height * 0.5f };
+            }
+
+            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;
+
+            public void setImageSize(int width, int height) {
+                r = Math.min(width, height) * 0.5f;
+                center = new float[] { width * 0.5f, height * 0.5f };
+            }
+
+            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;
+
+            public void setImageSize(int width, int height) {
+                r = Math.min(width, height) * 0.5f;
+                center = new float[] { width * 0.5f, height * 0.5f };
+            }
+
+            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 converts the material structure to jme Material.
+     * @param structure
+     *            structure with material data
+     * @param blenderContext
+     *            the blender context
+     * @return jme material
+     * @throws BlenderFileException
+     *             an exception is throw when problems with blend file occur
+     */
+    public MaterialContext toMaterialContext(Structure structure, BlenderContext blenderContext) throws BlenderFileException {
+        LOGGER.log(Level.FINE, "Loading material.");
+        MaterialContext result = (MaterialContext) blenderContext.getLoadedFeature(structure.getOldMemoryAddress(), LoadedFeatureDataType.LOADED_FEATURE);
+        if (result != null) {
+            return result;
+        }
+
+        result = new MaterialContext(structure, blenderContext);
+        LOGGER.log(Level.FINE, "Material''s name: {0}", result.name);
+        blenderContext.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 blenderContext
+     *            the blender context
+     * @return material converted into particles-usable material
+     */
+    public Material getParticlesMaterial(Material material, Integer alphaMaskIndex, BlenderContext blenderContext) {
+        Material result = new Material(blenderContext.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 any kind of texture.
+     * 
+     * @param material
+     *            the material
+     * @return <b>true</b> if the texture exists in the material and <B>false</b> otherwise
+     */
+    public boolean hasTexture(Material material) {
+        if (material != null) {
+            if (material.getTextureParam(TEXTURE_TYPE_ALPHA) != null) {
+                return true;
+            }
+            if (material.getTextureParam(TEXTURE_TYPE_COLOR) != null) {
+                return true;
+            }
+            if (material.getTextureParam(TEXTURE_TYPE_DIFFUSE) != null) {
+                return true;
+            }
+            if (material.getTextureParam(TEXTURE_TYPE_GLOW) != null) {
+                return true;
+            }
+            if (material.getTextureParam(TEXTURE_TYPE_NORMAL) != null) {
+                return true;
+            }
+            if (material.getTextureParam(TEXTURE_TYPE_SPECULAR) != null) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * 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 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 blenderContext
+     *            the blender context
+     * @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 MaterialContext[] getMaterials(Structure structureWithMaterials, BlenderContext blenderContext) throws BlenderFileException {
+        Pointer ppMaterials = (Pointer) structureWithMaterials.getFieldValue("mat");
+        MaterialContext[] materials = null;
+        if (ppMaterials.isNotNull()) {
+            List<Structure> materialStructures = ppMaterials.fetchData(blenderContext.getInputStream());
+            if (materialStructures != null && materialStructures.size() > 0) {
+                MaterialHelper materialHelper = blenderContext.getHelper(MaterialHelper.class);
+                materials = new MaterialContext[materialStructures.size()];
+                int i = 0;
+                for (Structure s : materialStructures) {
+                    materials[i++] = s == null ? null : materialHelper.toMaterialContext(s, blenderContext);
+                }
+            }
+        }
+        return materials;
+    }
+
+    /**
+     * This method converts rgb values to hsv values.
+     * 
+     * @param r
+     *            red value of the color
+     * @param g
+     *            green value of the color
+     * @param b
+     *            blue value 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;
+            }
+        }
+    }
+
+    @Override
+    public boolean shouldBeLoaded(Structure structure, BlenderContext blenderContext) {
+        return (blenderContext.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.MATERIALS) != 0;
+    }
 }

+ 254 - 236
engine/src/blender/com/jme3/scene/plugins/blender/meshes/MeshBuilder.java

@@ -12,85 +12,103 @@ import com.jme3.math.Vector2f;
 import com.jme3.math.Vector3f;
 import com.jme3.util.BufferUtils;
 
-/*package*/ class MeshBuilder {
-	private static final Logger LOGGER = Logger.getLogger(MeshBuilder.class.getName());
-	
-	/** An array of reference vertices. */
-	private Vector3f[][] verticesAndNormals;
-	/** An list of vertices colors. */
-	private List<byte[]> verticesColors;
-	/** A variable that indicates if the model uses generated textures. */
-	private boolean usesGeneratedTextures;
-	
-	/** 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). */
+/*package*/class MeshBuilder {
+    private static final Logger                       LOGGER          = Logger.getLogger(MeshBuilder.class.getName());
+
+    /** An array of reference vertices. */
+    private Vector3f[][]                              verticesAndNormals;
+    /** An list of vertices colors. */
+    private List<byte[]>                              verticesColors;
+    /** A variable that indicates if the model uses generated textures. */
+    private boolean                                   usesGeneratedTextures;
+
+    /**
+     * 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).
+     */
     private Map<Integer, Map<Integer, List<Integer>>> globalVertexReferenceMap;
-    
+
     /** A map between vertex index and its UV coordinates. */
-    private Map<Integer, Vector2f> uvsMap = new HashMap<Integer, Vector2f>();
+    private Map<Integer, Vector2f>                    uvsMap          = new HashMap<Integer, Vector2f>();
     /** The following map sorts vertices by material number (because in jme Mesh can have only one material). */
-    private Map<Integer, List<Vector3f>> normalMap = new HashMap<Integer, List<Vector3f>>();
+    private Map<Integer, List<Vector3f>>              normalMap       = new HashMap<Integer, List<Vector3f>>();
     /** The following map sorts vertices by material number (because in jme Mesh can have only one material). */
-    private Map<Integer, List<Vector3f>> vertexMap = new HashMap<Integer, List<Vector3f>>();
+    private Map<Integer, List<Vector3f>>              vertexMap       = new HashMap<Integer, List<Vector3f>>();
     /** The following map sorts vertices colors by material number (because in jme Mesh can have only one material). */
-    private Map<Integer, List<byte[]>> vertexColorsMap = new HashMap<Integer, List<byte[]>>();
+    private Map<Integer, List<byte[]>>                vertexColorsMap = new HashMap<Integer, List<byte[]>>();
     /** The following map sorts indexes by material number (because in jme Mesh can have only one material). */
-    private Map<Integer, List<Integer>> indexMap = new HashMap<Integer, List<Integer>>();
+    private Map<Integer, List<Integer>>               indexMap        = new HashMap<Integer, List<Integer>>();
     /** A map between material number and UV coordinates of mesh that has this material applied. */
-    private Map<Integer, List<Vector2f>> uvCoordinates = new HashMap<Integer, List<Vector2f>>();//<material_number; list of uv coordinates for mesh's vertices>
-    
+    private Map<Integer, List<Vector2f>>              uvCoordinates   = new HashMap<Integer, List<Vector2f>>();        // <material_number; list of uv coordinates for mesh's vertices>
+
     /**
      * Constructor. Stores the given array (not copying it).
      * The second argument describes if the model uses generated textures. If yes then no vertex amount optimisation is applied.
      * The amount of vertices is always faceCount * 3.
-     * @param verticesAndNormals the reference vertices and normals array
-     * @param usesGeneratedTextures a variable that indicates if the model uses generated textures or not
+     * @param verticesAndNormals
+     *            the reference vertices and normals array
+     * @param usesGeneratedTextures
+     *            a variable that indicates if the model uses generated textures or not
      */
-	public MeshBuilder(Vector3f[][] verticesAndNormals, List<byte[]> verticesColors, boolean usesGeneratedTextures) {
-		this.verticesAndNormals = verticesAndNormals;
-		this.verticesColors = verticesColors;
-		this.usesGeneratedTextures = usesGeneratedTextures;
-		globalVertexReferenceMap = new HashMap<Integer, Map<Integer, List<Integer>>>(verticesAndNormals.length);
-	}
-	
-	/**
-	 * This method adds a point to the mesh.
-	 * @param coordinates the coordinates of the point
-	 * @param normal the point's normal vector
-	 * @param materialNumber the material number for this point
-	 */
-	public void appendPoint(Vector3f coordinates, Vector3f normal, int materialNumber) {
-		LOGGER.warning("Appending single point not yet supported!");//TODO
-	}
-	
-	/**
-	 * This method adds a line to the mesh.
-	 * @param v1 index of the 1'st vertex from the reference vertex table
-	 * @param v2 index of the 2'nd vertex from the reference vertex table
-	 * @param smooth indicates if this face should have smooth shading or flat shading
-	 */
-	public void appendEdge(int v1, int v2, boolean smooth) {
-		LOGGER.warning("Appending single line not yet supported!");//TODO
-	}
-	
-	/**
-	 * This method adds a face to the mesh.
-	 * @param v1 index of the 1'st vertex from the reference vertex table
-	 * @param v2 index of the 2'nd vertex from the reference vertex table
-	 * @param v3 index of the 3'rd vertex from the reference vertex table
-	 * @param smooth indicates if this face should have smooth shading or flat shading
-	 * @param materialNumber the material number for this face
-	 * @param uvs a 3-element array of vertices UV coordinates
-	 * @param quad indicates if the appended face is a part of a quad face (used for creating vertex colors buffer)
-	 * @param faceIndex the face index (used for creating vertex colors buffer)
-	 */
-	public void appendFace(int v1, int v2, int v3, boolean smooth, int materialNumber, Vector2f[] uvs, boolean quad, int faceIndex) {
-		if(uvs != null && uvs.length != 3) {
-			throw new IllegalArgumentException("UV coordinates must be a 3-element array!");
-		}
-		
-		//getting the required lists
-		List<Integer> indexList = indexMap.get(materialNumber);
+    public MeshBuilder(Vector3f[][] verticesAndNormals, List<byte[]> verticesColors, boolean usesGeneratedTextures) {
+        this.verticesAndNormals = verticesAndNormals;
+        this.verticesColors = verticesColors;
+        this.usesGeneratedTextures = usesGeneratedTextures;
+        globalVertexReferenceMap = new HashMap<Integer, Map<Integer, List<Integer>>>(verticesAndNormals.length);
+    }
+
+    /**
+     * This method adds a point to the mesh.
+     * @param coordinates
+     *            the coordinates of the point
+     * @param normal
+     *            the point's normal vector
+     * @param materialNumber
+     *            the material number for this point
+     */
+    public void appendPoint(Vector3f coordinates, Vector3f normal, int materialNumber) {
+        LOGGER.warning("Appending single point not yet supported!");// TODO
+    }
+
+    /**
+     * This method adds a line to the mesh.
+     * @param v1
+     *            index of the 1'st vertex from the reference vertex table
+     * @param v2
+     *            index of the 2'nd vertex from the reference vertex table
+     * @param smooth
+     *            indicates if this face should have smooth shading or flat shading
+     */
+    public void appendEdge(int v1, int v2, boolean smooth) {
+        LOGGER.warning("Appending single line not yet supported!");// TODO
+    }
+
+    /**
+     * This method adds a face to the mesh.
+     * @param v1
+     *            index of the 1'st vertex from the reference vertex table
+     * @param v2
+     *            index of the 2'nd vertex from the reference vertex table
+     * @param v3
+     *            index of the 3'rd vertex from the reference vertex table
+     * @param smooth
+     *            indicates if this face should have smooth shading or flat shading
+     * @param materialNumber
+     *            the material number for this face
+     * @param uvs
+     *            a 3-element array of vertices UV coordinates
+     * @param quad
+     *            indicates if the appended face is a part of a quad face (used for creating vertex colors buffer)
+     * @param faceIndex
+     *            the face index (used for creating vertex colors buffer)
+     */
+    public void appendFace(int v1, int v2, int v3, boolean smooth, int materialNumber, Vector2f[] uvs, boolean quad, int faceIndex) {
+        if (uvs != null && uvs.length != 3) {
+            throw new IllegalArgumentException("UV coordinates must be a 3-element array!");
+        }
+
+        // getting the required lists
+        List<Integer> indexList = indexMap.get(materialNumber);
         if (indexList == null) {
             indexList = new ArrayList<Integer>();
             indexMap.put(materialNumber, indexList);
@@ -102,187 +120,187 @@ import com.jme3.util.BufferUtils;
         }
         List<byte[]> vertexColorsList = vertexColorsMap != null ? vertexColorsMap.get(materialNumber) : null;
         int[] vertexColorIndex = new int[] { 0, 1, 2 };
-        if(vertexColorsList == null && vertexColorsMap != null) {
-        	vertexColorsList = new ArrayList<byte[]>();
-        	vertexColorsMap.put(materialNumber, vertexColorsList);
+        if (vertexColorsList == null && vertexColorsMap != null) {
+            vertexColorsList = new ArrayList<byte[]>();
+            vertexColorsMap.put(materialNumber, vertexColorsList);
         }
         List<Vector3f> normalList = normalMap.get(materialNumber);
         if (normalList == null) {
-        	normalList = new ArrayList<Vector3f>();
-        	normalMap.put(materialNumber, normalList);
+            normalList = new ArrayList<Vector3f>();
+            normalMap.put(materialNumber, normalList);
         }
         Map<Integer, List<Integer>> vertexReferenceMap = globalVertexReferenceMap.get(materialNumber);
-        if(vertexReferenceMap == null) {
-        	vertexReferenceMap = new HashMap<Integer, List<Integer>>();
-        	globalVertexReferenceMap.put(materialNumber, vertexReferenceMap);
+        if (vertexReferenceMap == null) {
+            vertexReferenceMap = new HashMap<Integer, List<Integer>>();
+            globalVertexReferenceMap.put(materialNumber, vertexReferenceMap);
         }
         List<Vector2f> uvCoordinatesList = null;
-        if(uvs != null) {
-	        uvCoordinatesList = uvCoordinates.get(Integer.valueOf(materialNumber));
-	        if(uvCoordinatesList == null) {
-	        	uvCoordinatesList = new ArrayList<Vector2f>();
-	        	uvCoordinates.put(Integer.valueOf(materialNumber), uvCoordinatesList);
-	        }
+        if (uvs != null) {
+            uvCoordinatesList = uvCoordinates.get(Integer.valueOf(materialNumber));
+            if (uvCoordinatesList == null) {
+                uvCoordinatesList = new ArrayList<Vector2f>();
+                uvCoordinates.put(Integer.valueOf(materialNumber), uvCoordinatesList);
+            }
         }
-        
+
         faceIndex *= 4;
-        if(quad) {
-        	vertexColorIndex[1] = 2;
-        	vertexColorIndex[2] = 3;
+        if (quad) {
+            vertexColorIndex[1] = 2;
+            vertexColorIndex[2] = 3;
         }
-        
-        //creating faces
-        Integer[] index = new Integer[] {v1, v2, v3};
-        if(smooth && !usesGeneratedTextures) {
-			for (int i = 0; i < 3; ++i) {
-        		if(!vertexReferenceMap.containsKey(index[i])) {
-            		this.appendVertexReference(index[i], vertexList.size(), vertexReferenceMap);
-            		vertexList.add(verticesAndNormals[index[i]][0]);
-            		if(verticesColors != null) {
-            			vertexColorsList.add(verticesColors.get(faceIndex + vertexColorIndex[i]));
-            		}
-            		normalList.add(verticesAndNormals[index[i]][1]);
-            		if(uvCoordinatesList != null) {
-            			uvsMap.put(vertexList.size(), uvs[i]);
-            			uvCoordinatesList.add(uvs[i]);
-            		}
-            		index[i] = vertexList.size() - 1;
-            	} else if(uvCoordinatesList != null) {
-            		boolean vertexAlreadyUsed = false;
-            		for(Integer vertexIndex : vertexReferenceMap.get(index[i])) {
-            			if(uvs[i].equals(uvsMap.get(vertexIndex))) {
-            				vertexAlreadyUsed = true;
-            				index[i] = vertexIndex;
-            				break;
-            			}
-            		}
-            		if(!vertexAlreadyUsed) {
-            			this.appendVertexReference(index[i], vertexList.size(), vertexReferenceMap);
-            			uvsMap.put(vertexList.size(), uvs[i]);
-            			vertexList.add(verticesAndNormals[index[i]][0]);
-            			if(verticesColors != null) {
-            				vertexColorsList.add(verticesColors.get(faceIndex + vertexColorIndex[i]));
-            			}
-            			normalList.add(verticesAndNormals[index[i]][1]);
-            			uvCoordinatesList.add(uvs[i]);
-            			index[i] = vertexList.size() - 1;
-            		}
-            	} else {
-            		index[i] = vertexList.indexOf(verticesAndNormals[index[i]][0]);
-            	}
-        		indexList.add(index[i]);
-        	}
+
+        // creating faces
+        Integer[] index = new Integer[] { v1, v2, v3 };
+        if (smooth && !usesGeneratedTextures) {
+            for (int i = 0; i < 3; ++i) {
+                if (!vertexReferenceMap.containsKey(index[i])) {
+                    this.appendVertexReference(index[i], vertexList.size(), vertexReferenceMap);
+                    vertexList.add(verticesAndNormals[index[i]][0]);
+                    if (verticesColors != null) {
+                        vertexColorsList.add(verticesColors.get(faceIndex + vertexColorIndex[i]));
+                    }
+                    normalList.add(verticesAndNormals[index[i]][1]);
+                    if (uvCoordinatesList != null) {
+                        uvsMap.put(vertexList.size(), uvs[i]);
+                        uvCoordinatesList.add(uvs[i]);
+                    }
+                    index[i] = vertexList.size() - 1;
+                } else if (uvCoordinatesList != null) {
+                    boolean vertexAlreadyUsed = false;
+                    for (Integer vertexIndex : vertexReferenceMap.get(index[i])) {
+                        if (uvs[i].equals(uvsMap.get(vertexIndex))) {
+                            vertexAlreadyUsed = true;
+                            index[i] = vertexIndex;
+                            break;
+                        }
+                    }
+                    if (!vertexAlreadyUsed) {
+                        this.appendVertexReference(index[i], vertexList.size(), vertexReferenceMap);
+                        uvsMap.put(vertexList.size(), uvs[i]);
+                        vertexList.add(verticesAndNormals[index[i]][0]);
+                        if (verticesColors != null) {
+                            vertexColorsList.add(verticesColors.get(faceIndex + vertexColorIndex[i]));
+                        }
+                        normalList.add(verticesAndNormals[index[i]][1]);
+                        uvCoordinatesList.add(uvs[i]);
+                        index[i] = vertexList.size() - 1;
+                    }
+                } else {
+                    index[i] = vertexList.indexOf(verticesAndNormals[index[i]][0]);
+                }
+                indexList.add(index[i]);
+            }
         } else {
-        	Vector3f n = smooth ? null : FastMath.computeNormal(verticesAndNormals[v1][0], verticesAndNormals[v2][0], verticesAndNormals[v3][0]);
-        	for (int i = 0; i < 3; ++i) {
-        		indexList.add(vertexList.size());
-        		this.appendVertexReference(index[i], vertexList.size(), vertexReferenceMap);
-        		if(uvCoordinatesList != null) {
-        			uvCoordinatesList.add(uvs[i]);
-        			uvsMap.put(vertexList.size(), uvs[i]);
-        		}
-        		vertexList.add(verticesAndNormals[index[i]][0]);
-        		if(verticesColors != null) {
-        			vertexColorsList.add(verticesColors.get(faceIndex + vertexColorIndex[i]));
-        		}
-        		normalList.add(smooth ? verticesAndNormals[index[i]][1] : n);
-        	}
+            Vector3f n = smooth ? null : FastMath.computeNormal(verticesAndNormals[v1][0], verticesAndNormals[v2][0], verticesAndNormals[v3][0]);
+            for (int i = 0; i < 3; ++i) {
+                indexList.add(vertexList.size());
+                this.appendVertexReference(index[i], vertexList.size(), vertexReferenceMap);
+                if (uvCoordinatesList != null) {
+                    uvCoordinatesList.add(uvs[i]);
+                    uvsMap.put(vertexList.size(), uvs[i]);
+                }
+                vertexList.add(verticesAndNormals[index[i]][0]);
+                if (verticesColors != null) {
+                    vertexColorsList.add(verticesColors.get(faceIndex + vertexColorIndex[i]));
+                }
+                normalList.add(smooth ? verticesAndNormals[index[i]][1] : n);
+            }
         }
-	}
-	
-	/**
-	 * @return a map that maps vertex index from reference array to its indices in the result list
-	 */
-	public Map<Integer, Map<Integer, List<Integer>>> getVertexReferenceMap() {
-		return globalVertexReferenceMap;
-	}
-	
-	/**
-	 * @param materialNumber
-	 *            the material index
-	 * @return result vertices array
-	 */
-	public Vector3f[] getVertices(int materialNumber) {
-		return vertexMap.get(materialNumber).toArray(new Vector3f[vertexMap.get(materialNumber).size()]);
-	}
-	
-	/**
-	 * @param materialNumber
-	 *            the material index
-	 * @return the amount of result vertices
-	 */
-	public int getVerticesAmount(int materialNumber) {
-		return vertexMap.get(materialNumber).size();
-	}
-	
-	/**
-	 * @param materialNumber
-	 *            the material index
-	 * @return normals result array
-	 */
-	public Vector3f[] getNormals(int materialNumber) {
-		return normalMap.get(materialNumber).toArray(new Vector3f[normalMap.get(materialNumber).size()]);
-	}
-	
-	/**
-	 * @param materialNumber
-	 *            the material index
-	 * @return the vertices colors buffer or null if no vertex colors is set
-	 */
-	public ByteBuffer getVertexColorsBuffer(int materialNumber) {
-		ByteBuffer result = null;
-		if (verticesColors != null && vertexColorsMap.get(materialNumber) != null) {
-			List<byte[]> data = vertexColorsMap.get(materialNumber);
-			result = BufferUtils.createByteBuffer(4 * data.size());
-			for (byte[] v : data) {
-				if (v != null) {
-					result.put(v[0]).put(v[1]).put(v[2]).put(v[3]);
-				} else {
-					result.put((byte)0).put((byte)0).put((byte)0).put((byte)0);
-				}
-			}
-			result.flip();
-		}
-		return result;
-	}
-	
-	/**
-	 * @return a map between material number and the mesh part vertices indices
-	 */
-	public Map<Integer, List<Integer>> getMeshesMap() {
-		return indexMap;
-	}
-	
-	/**
-	 * @return the amount of meshes the source mesh was split into (depends on the applied materials count)
-	 */
-	public int getMeshesPartAmount() {
-		return indexMap.size();
-	}
-	
-	/**
-	 * @param materialNumber
-	 *            the material number that is appied to the mesh
-	 * @return UV coordinates of vertices that belong to the required mesh part
-	 */
-	public List<Vector2f> getUVCoordinates(int materialNumber) {
-		return uvCoordinates.get(materialNumber);
-	}
-	
-	/**
-	 * @return indicates if the mesh has UV coordinates
-	 */
-	public boolean hasUVCoordinates() {
-		return uvCoordinates.size() > 0;
-	}
-	
-	/**
-	 * @return <b>true</b> if the mesh has no vertices and <b>false</b> otherwise
-	 */
-	public boolean isEmpty() {
-		return vertexMap.size() == 0;
-	}
-	
+    }
+
+    /**
+     * @return a map that maps vertex index from reference array to its indices in the result list
+     */
+    public Map<Integer, Map<Integer, List<Integer>>> getVertexReferenceMap() {
+        return globalVertexReferenceMap;
+    }
+
+    /**
+     * @param materialNumber
+     *            the material index
+     * @return result vertices array
+     */
+    public Vector3f[] getVertices(int materialNumber) {
+        return vertexMap.get(materialNumber).toArray(new Vector3f[vertexMap.get(materialNumber).size()]);
+    }
+
+    /**
+     * @param materialNumber
+     *            the material index
+     * @return the amount of result vertices
+     */
+    public int getVerticesAmount(int materialNumber) {
+        return vertexMap.get(materialNumber).size();
+    }
+
+    /**
+     * @param materialNumber
+     *            the material index
+     * @return normals result array
+     */
+    public Vector3f[] getNormals(int materialNumber) {
+        return normalMap.get(materialNumber).toArray(new Vector3f[normalMap.get(materialNumber).size()]);
+    }
+
+    /**
+     * @param materialNumber
+     *            the material index
+     * @return the vertices colors buffer or null if no vertex colors is set
+     */
+    public ByteBuffer getVertexColorsBuffer(int materialNumber) {
+        ByteBuffer result = null;
+        if (verticesColors != null && vertexColorsMap.get(materialNumber) != null) {
+            List<byte[]> data = vertexColorsMap.get(materialNumber);
+            result = BufferUtils.createByteBuffer(4 * data.size());
+            for (byte[] v : data) {
+                if (v != null) {
+                    result.put(v[0]).put(v[1]).put(v[2]).put(v[3]);
+                } else {
+                    result.put((byte) 0).put((byte) 0).put((byte) 0).put((byte) 0);
+                }
+            }
+            result.flip();
+        }
+        return result;
+    }
+
+    /**
+     * @return a map between material number and the mesh part vertices indices
+     */
+    public Map<Integer, List<Integer>> getMeshesMap() {
+        return indexMap;
+    }
+
+    /**
+     * @return the amount of meshes the source mesh was split into (depends on the applied materials count)
+     */
+    public int getMeshesPartAmount() {
+        return indexMap.size();
+    }
+
+    /**
+     * @param materialNumber
+     *            the material number that is appied to the mesh
+     * @return UV coordinates of vertices that belong to the required mesh part
+     */
+    public List<Vector2f> getUVCoordinates(int materialNumber) {
+        return uvCoordinates.get(materialNumber);
+    }
+
+    /**
+     * @return indicates if the mesh has UV coordinates
+     */
+    public boolean hasUVCoordinates() {
+        return uvCoordinates.size() > 0;
+    }
+
+    /**
+     * @return <b>true</b> if the mesh has no vertices and <b>false</b> otherwise
+     */
+    public boolean isEmpty() {
+        return vertexMap.size() == 0;
+    }
+
     /**
      * 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

+ 123 - 119
engine/src/blender/com/jme3/scene/plugins/blender/meshes/MeshContext.java

@@ -14,131 +14,135 @@ import com.jme3.scene.VertexBuffer;
  * @author Marcin Roguski (Kaelthas)
  */
 public class MeshContext {
-	/** A map between material index and the geometry. */
-	private Map<Integer, Geometry> geometries = new HashMap<Integer, Geometry>();
-	/** The vertex reference map. */
-	private Map<Integer, Map<Integer, List<Integer>>>	vertexReferenceMap;
-	/** The UV-coordinates for each of the geometries. */
-	private Map<Geometry, VertexBuffer>	uvCoordinates	= new HashMap<Geometry, VertexBuffer>();
-	/** Bind buffer for vertices is stored here and applied when required. */
-	private Map<Integer, VertexBuffer>				bindPoseBuffer = new HashMap<Integer, VertexBuffer>();
-	/** Bind buffer for normals is stored here and applied when required. */
-	private Map<Integer, VertexBuffer>				bindNormalBuffer = new HashMap<Integer, VertexBuffer>();
+    /** A map between material index and the geometry. */
+    private Map<Integer, Geometry>                    geometries       = new HashMap<Integer, Geometry>();
+    /** The vertex reference map. */
+    private Map<Integer, Map<Integer, List<Integer>>> vertexReferenceMap;
+    /** The UV-coordinates for each of the geometries. */
+    private Map<Geometry, VertexBuffer>               uvCoordinates    = new HashMap<Geometry, VertexBuffer>();
+    /** Bind buffer for vertices is stored here and applied when required. */
+    private Map<Integer, VertexBuffer>                bindPoseBuffer   = new HashMap<Integer, VertexBuffer>();
+    /** Bind buffer for normals is stored here and applied when required. */
+    private Map<Integer, VertexBuffer>                bindNormalBuffer = new HashMap<Integer, VertexBuffer>();
 
-	/**
-	 * Adds a geometry for the specified material index.
-	 * @param materialIndex the material index
-	 * @param geometry the geometry
-	 */
-	public void putGeometry(Integer materialIndex, Geometry geometry) {
-		geometries.put(materialIndex, geometry);
-	}
-	
-	/**
-	 * @param materialIndex the material index
-	 * @return vertices amount that is used by mesh with the specified material
-	 */
-	public int getVertexCount(int materialIndex) {
-		return geometries.get(materialIndex).getVertexCount();
-	}
-	
-	/**
-	 * Returns material index for the geometry.
-	 * @param geometry the geometry
-	 * @return material index
-	 * @throws IllegalStateException this exception is thrown when no material is found for the specified geometry
-	 */
-	public int getMaterialIndex(Geometry geometry) {
-		for(Entry<Integer, Geometry> entry : geometries.entrySet()) {
-			if(entry.getValue().equals(geometry)) {
-				return entry.getKey();
-			}
-		}
-		throw new IllegalStateException("Cannot find material index for the given geometry: " + geometry);
-	}
-	
-	/**
-	 * This method returns the vertex reference map.
-	 * 
-	 * @return the vertex reference map
-	 */
-	public Map<Integer, List<Integer>> getVertexReferenceMap(int materialIndex) {
-		return vertexReferenceMap.get(materialIndex);
-	}
+    /**
+     * Adds a geometry for the specified material index.
+     * @param materialIndex
+     *            the material index
+     * @param geometry
+     *            the geometry
+     */
+    public void putGeometry(Integer materialIndex, Geometry geometry) {
+        geometries.put(materialIndex, geometry);
+    }
 
-	/**
-	 * This method sets the vertex reference map.
-	 * 
-	 * @param vertexReferenceMap
-	 *            the vertex reference map
-	 */
-	public void setVertexReferenceMap(Map<Integer, Map<Integer, List<Integer>>> vertexReferenceMap) {
-		this.vertexReferenceMap = vertexReferenceMap;
-	}
+    /**
+     * @param materialIndex
+     *            the material index
+     * @return vertices amount that is used by mesh with the specified material
+     */
+    public int getVertexCount(int materialIndex) {
+        return geometries.get(materialIndex).getVertexCount();
+    }
 
-	/**
-	 * This method adds the mesh's UV-coordinates.
-	 * 
-	 * @param geometry
-	 *            the mesh that has the UV-coordinates
-	 * @param vertexBuffer
-	 *            the mesh's UV-coordinates
-	 */
-	public void addUVCoordinates(Geometry geometry, VertexBuffer vertexBuffer) {
-		uvCoordinates.put(geometry, vertexBuffer);
-	}
+    /**
+     * Returns material index for the geometry.
+     * @param geometry
+     *            the geometry
+     * @return material index
+     * @throws IllegalStateException
+     *             this exception is thrown when no material is found for the specified geometry
+     */
+    public int getMaterialIndex(Geometry geometry) {
+        for (Entry<Integer, Geometry> entry : geometries.entrySet()) {
+            if (entry.getValue().equals(geometry)) {
+                return entry.getKey();
+            }
+        }
+        throw new IllegalStateException("Cannot find material index for the given geometry: " + geometry);
+    }
 
-	/**
-	 * This method returns the mesh's UV-coordinates.
-	 * 
-	 * @param geometry
-	 *            the mesh
-	 * @return the mesh's UV-coordinates
-	 */
-	public VertexBuffer getUVCoordinates(Geometry geometry) {
-		return uvCoordinates.get(geometry);
-	}
+    /**
+     * This method returns the vertex reference map.
+     * 
+     * @return the vertex reference map
+     */
+    public Map<Integer, List<Integer>> getVertexReferenceMap(int materialIndex) {
+        return vertexReferenceMap.get(materialIndex);
+    }
 
-	/**
-	 * This method sets the bind buffer for vertices.
-	 * 
-	 * @param materialIndex
-	 *            the index of the mesh's material
-	 * @param bindNormalBuffer
-	 *            the bind buffer for vertices
-	 */
-	public void setBindNormalBuffer(int materialIndex,
-			VertexBuffer bindNormalBuffer) {
-		this.bindNormalBuffer.put(materialIndex, bindNormalBuffer);
-	}
+    /**
+     * This method sets the vertex reference map.
+     * 
+     * @param vertexReferenceMap
+     *            the vertex reference map
+     */
+    public void setVertexReferenceMap(Map<Integer, Map<Integer, List<Integer>>> vertexReferenceMap) {
+        this.vertexReferenceMap = vertexReferenceMap;
+    }
 
-	/**
-	 * @param materialIndex
-	 *            the index of the mesh's material
-	 * @return the bind buffer for vertices
-	 */
-	public VertexBuffer getBindNormalBuffer(int materialIndex) {
-		return bindNormalBuffer.get(materialIndex);
-	}
+    /**
+     * This method adds the mesh's UV-coordinates.
+     * 
+     * @param geometry
+     *            the mesh that has the UV-coordinates
+     * @param vertexBuffer
+     *            the mesh's UV-coordinates
+     */
+    public void addUVCoordinates(Geometry geometry, VertexBuffer vertexBuffer) {
+        uvCoordinates.put(geometry, vertexBuffer);
+    }
 
-	/**
-	 * This method sets the bind buffer for normals.
-	 * 
-	 * @param materialIndex
-	 *            the index of the mesh's material
-	 * @param bindNormalBuffer
-	 *            the bind buffer for normals
-	 */
-	public void setBindPoseBuffer(int materialIndex, VertexBuffer bindPoseBuffer) {
-		this.bindPoseBuffer.put(materialIndex, bindPoseBuffer);
-	}
+    /**
+     * This method returns the mesh's UV-coordinates.
+     * 
+     * @param geometry
+     *            the mesh
+     * @return the mesh's UV-coordinates
+     */
+    public VertexBuffer getUVCoordinates(Geometry geometry) {
+        return uvCoordinates.get(geometry);
+    }
 
-	/**
-	 * @param materialIndex
-	 *            the index of the mesh's material
-	 * @return the bind buffer for normals
-	 */
-	public VertexBuffer getBindPoseBuffer(int materialIndex) {
-		return bindPoseBuffer.get(materialIndex);
-	}
+    /**
+     * This method sets the bind buffer for vertices.
+     * 
+     * @param materialIndex
+     *            the index of the mesh's material
+     * @param bindNormalBuffer
+     *            the bind buffer for vertices
+     */
+    public void setBindNormalBuffer(int materialIndex, VertexBuffer bindNormalBuffer) {
+        this.bindNormalBuffer.put(materialIndex, bindNormalBuffer);
+    }
+
+    /**
+     * @param materialIndex
+     *            the index of the mesh's material
+     * @return the bind buffer for vertices
+     */
+    public VertexBuffer getBindNormalBuffer(int materialIndex) {
+        return bindNormalBuffer.get(materialIndex);
+    }
+
+    /**
+     * This method sets the bind buffer for normals.
+     * 
+     * @param materialIndex
+     *            the index of the mesh's material
+     * @param bindNormalBuffer
+     *            the bind buffer for normals
+     */
+    public void setBindPoseBuffer(int materialIndex, VertexBuffer bindPoseBuffer) {
+        this.bindPoseBuffer.put(materialIndex, bindPoseBuffer);
+    }
+
+    /**
+     * @param materialIndex
+     *            the index of the mesh's material
+     * @return the bind buffer for normals
+     */
+    public VertexBuffer getBindPoseBuffer(int materialIndex) {
+        return bindPoseBuffer.get(materialIndex);
+    }
 }

+ 233 - 235
engine/src/blender/com/jme3/scene/plugins/blender/meshes/MeshHelper.java

@@ -63,8 +63,8 @@ import com.jme3.util.BufferUtils;
  * @author Marcin Roguski (Kaelthas)
  */
 public class MeshHelper extends AbstractBlenderHelper {
-	private static final Logger LOGGER = Logger.getLogger(MeshHelper.class.getName());
-	
+    private static final Logger LOGGER = Logger.getLogger(MeshHelper.class.getName());
+
     /**
      * This constructor parses the given blender version and stores the result. Some functionalities may differ in different blender
      * versions.
@@ -72,10 +72,10 @@ public class MeshHelper extends AbstractBlenderHelper {
      * @param blenderVersion
      *            the version read from the blend file
      * @param fixUpAxis
-     *        a variable that indicates if the Y asxis is the UP axis or not
+     *            a variable that indicates if the Y asxis is the UP axis or not
      */
     public MeshHelper(String blenderVersion, boolean fixUpAxis) {
-        super(blenderVersion,fixUpAxis);
+        super(blenderVersion, fixUpAxis);
     }
 
     /**
@@ -107,26 +107,26 @@ public class MeshHelper extends AbstractBlenderHelper {
         if ((blenderContext.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.MATERIALS) != 0) {
             materials = materialHelper.getMaterials(structure, blenderContext);
         }
-        
+
         // reading vertices and their colors
         Vector3f[][] verticesAndNormals = this.getVerticesAndNormals(structure, blenderContext);
         List<byte[]> verticesColors = this.getVerticesColors(structure, blenderContext);
-        
+
         MeshBuilder meshBuilder = new MeshBuilder(verticesAndNormals, verticesColors, this.areGeneratedTexturesPresent(materials));
 
-        if(this.isBMeshCompatible(structure)) {
-        	this.readBMesh(meshBuilder, structure, blenderContext);
+        if (this.isBMeshCompatible(structure)) {
+            this.readBMesh(meshBuilder, structure, blenderContext);
         } else {
-        	this.readTraditionalFaces(meshBuilder, structure, blenderContext);
+            this.readTraditionalFaces(meshBuilder, structure, blenderContext);
         }
 
-        if(meshBuilder.isEmpty()) {
-        	geometries = new ArrayList<Geometry>(0);
-        	blenderContext.addLoadedFeatures(structure.getOldMemoryAddress(), structure.getName(), structure, geometries);
+        if (meshBuilder.isEmpty()) {
+            geometries = new ArrayList<Geometry>(0);
+            blenderContext.addLoadedFeatures(structure.getOldMemoryAddress(), structure.getName(), structure, geometries);
             blenderContext.setMeshContext(structure.getOldMemoryAddress(), meshContext);
             return geometries;
         }
-        
+
         meshContext.setVertexReferenceMap(meshBuilder.getVertexReferenceMap());
 
         // reading vertices groups (from the parent)
@@ -142,32 +142,32 @@ public class MeshHelper extends AbstractBlenderHelper {
         // creating the result meshes
         geometries = new ArrayList<Geometry>(meshBuilder.getMeshesPartAmount());
 
-        //reading custom properties
+        // reading custom properties
         Properties properties = this.loadProperties(structure, blenderContext);
 
         // generating meshes
         for (Entry<Integer, List<Integer>> meshEntry : meshBuilder.getMeshesMap().entrySet()) {
-        	int materialIndex = meshEntry.getKey();
-        	//key is the material index (or -1 if the material has no texture)
-        	//value is a list of vertex indices
+            int materialIndex = meshEntry.getKey();
+            // key is the material index (or -1 if the material has no texture)
+            // value is a list of vertex indices
             Mesh mesh = new Mesh();
-            
+
             // creating vertices indices for this mesh
             List<Integer> indexList = meshEntry.getValue();
-            if(meshBuilder.getVerticesAmount(materialIndex) <= Short.MAX_VALUE) {
-            	short[] indices = new short[indexList.size()];
+            if (meshBuilder.getVerticesAmount(materialIndex) <= Short.MAX_VALUE) {
+                short[] indices = new short[indexList.size()];
                 for (int i = 0; i < indexList.size(); ++i) {
                     indices[i] = indexList.get(i).shortValue();
                 }
                 mesh.setBuffer(Type.Index, 1, indices);
             } else {
-            	int[] indices = new int[indexList.size()];
+                int[] indices = new int[indexList.size()];
                 for (int i = 0; i < indexList.size(); ++i) {
                     indices[i] = indexList.get(i).intValue();
                 }
                 mesh.setBuffer(Type.Index, 1, indices);
             }
-            
+
             VertexBuffer verticesBuffer = new VertexBuffer(Type.Position);
             verticesBuffer.setupData(Usage.Static, 3, Format.Float, BufferUtils.createFloatBuffer(meshBuilder.getVertices(materialIndex)));
 
@@ -181,9 +181,9 @@ public class MeshHelper extends AbstractBlenderHelper {
             // initial normals position (used with animation)
             VertexBuffer normalsBind = new VertexBuffer(Type.BindPoseNormal);
             normalsBind.setupData(Usage.CpuOnly, 3, Format.Float, BufferUtils.createFloatBuffer(meshBuilder.getNormals(materialIndex)));
-            
+
             mesh.setBuffer(verticesBuffer);
-            meshContext.setBindPoseBuffer(materialIndex, verticesBind);//this is stored in the context and applied when needed (when animation is applied to the mesh)
+            meshContext.setBindPoseBuffer(materialIndex, verticesBind);// this is stored in the context and applied when needed (when animation is applied to the mesh)
 
             // setting vertices colors
             if (verticesColors != null) {
@@ -193,227 +193,225 @@ public class MeshHelper extends AbstractBlenderHelper {
 
             // setting faces' normals
             mesh.setBuffer(normalsBuffer);
-            meshContext.setBindNormalBuffer(materialIndex, normalsBind);//this is stored in the context and applied when needed (when animation is applied to the mesh)
+            meshContext.setBindNormalBuffer(materialIndex, normalsBind);// this is stored in the context and applied when needed (when animation is applied to the mesh)
 
             // creating the result
             Geometry geometry = new Geometry(name + (geometries.size() + 1), mesh);
             if (properties != null && properties.getValue() != null) {
-            	this.applyProperties(geometry, properties);
+                this.applyProperties(geometry, properties);
             }
             geometries.add(geometry);
             meshContext.putGeometry(materialIndex, geometry);
         }
-        
-        //store the data in blender context before applying the material
+
+        // store the data in blender context before applying the material
         blenderContext.addLoadedFeatures(structure.getOldMemoryAddress(), structure.getName(), structure, geometries);
         blenderContext.setMeshContext(structure.getOldMemoryAddress(), meshContext);
-        
-        //apply materials only when all geometries are in place
-        if(materials != null) {
-        	for(Geometry geometry : geometries) {
-        		int materialNumber = meshContext.getMaterialIndex(geometry);
-                if(materials[materialNumber] != null) {
-                	List<Vector2f> uvCoordinates = meshBuilder.getUVCoordinates(materialNumber);
-	                MaterialContext materialContext = materials[materialNumber];
-	                materialContext.applyMaterial(geometry, structure.getOldMemoryAddress(), uvCoordinates, blenderContext);
+
+        // apply materials only when all geometries are in place
+        if (materials != null) {
+            for (Geometry geometry : geometries) {
+                int materialNumber = meshContext.getMaterialIndex(geometry);
+                if (materials[materialNumber] != null) {
+                    List<Vector2f> uvCoordinates = meshBuilder.getUVCoordinates(materialNumber);
+                    MaterialContext materialContext = materials[materialNumber];
+                    materialContext.applyMaterial(geometry, structure.getOldMemoryAddress(), uvCoordinates, blenderContext);
                 } else {
-                	geometry.setMaterial(blenderContext.getDefaultMaterial());
-                	LOGGER.warning("The importer came accross mesh that points to a null material. Default material is used to prevent loader from crashing, " +
-                				   "but the model might look not the way it should. Sometimes blender does not assign materials properly. " + 
-                				   "Enter the edit mode and assign materials once more to your faces.");
+                    geometry.setMaterial(blenderContext.getDefaultMaterial());
+                    LOGGER.warning("The importer came accross mesh that points to a null material. Default material is used to prevent loader from crashing, " + "but the model might look not the way it should. Sometimes blender does not assign materials properly. " + "Enter the edit mode and assign materials once more to your faces.");
                 }
-        	}
+            }
         } else {
-        	//add UV coordinates if they are defined even if the material is not applied to the model
-        	VertexBuffer uvCoordsBuffer = null;
-        	if(meshBuilder.hasUVCoordinates()) {
-	        	List<Vector2f> uvs = meshBuilder.getUVCoordinates(0);
-	        	uvCoordsBuffer = new VertexBuffer(Type.TexCoord);
-	            uvCoordsBuffer.setupData(Usage.Static, 2, Format.Float, BufferUtils.createFloatBuffer(uvs.toArray(new Vector2f[uvs.size()])));
-        	}
-			
-        	for(Geometry geometry : geometries) {
-        		geometry.setMaterial(blenderContext.getDefaultMaterial());
-        		if(uvCoordsBuffer != null) {
-        			geometry.getMesh().setBuffer(uvCoordsBuffer);
-        		}
-        	}
+            // add UV coordinates if they are defined even if the material is not applied to the model
+            VertexBuffer uvCoordsBuffer = null;
+            if (meshBuilder.hasUVCoordinates()) {
+                List<Vector2f> uvs = meshBuilder.getUVCoordinates(0);
+                uvCoordsBuffer = new VertexBuffer(Type.TexCoord);
+                uvCoordsBuffer.setupData(Usage.Static, 2, Format.Float, BufferUtils.createFloatBuffer(uvs.toArray(new Vector2f[uvs.size()])));
+            }
+
+            for (Geometry geometry : geometries) {
+                geometry.setMaterial(blenderContext.getDefaultMaterial());
+                if (uvCoordsBuffer != null) {
+                    geometry.getMesh().setBuffer(uvCoordsBuffer);
+                }
+            }
         }
-        
+
         return geometries;
     }
-    
-	/**
-	 * Tells if the given mesh structure supports BMesh.
-	 * 
-	 * @param meshStructure
-	 *            the mesh structure
-	 * @return <b>true</b> if BMesh is supported and <b>false</b> otherwise
-	 */
+
+    /**
+     * Tells if the given mesh structure supports BMesh.
+     * 
+     * @param meshStructure
+     *            the mesh structure
+     * @return <b>true</b> if BMesh is supported and <b>false</b> otherwise
+     */
     private boolean isBMeshCompatible(Structure meshStructure) {
-    	Pointer pMLoop = (Pointer) meshStructure.getFieldValue("mloop");
-		Pointer pMPoly = (Pointer) meshStructure.getFieldValue("mpoly");
-		return pMLoop != null && pMPoly != null && pMLoop.isNotNull() && pMPoly.isNotNull();
+        Pointer pMLoop = (Pointer) meshStructure.getFieldValue("mloop");
+        Pointer pMPoly = (Pointer) meshStructure.getFieldValue("mpoly");
+        return pMLoop != null && pMPoly != null && pMLoop.isNotNull() && pMPoly.isNotNull();
+    }
+
+    /**
+     * This method reads the mesh from the new BMesh system.
+     * 
+     * @param meshBuilder
+     *            the mesh builder
+     * @param meshStructure
+     *            the mesh structure
+     * @param blenderContext
+     *            the blender context
+     * @throws BlenderFileException
+     *             an exception is thrown when there are problems with the
+     *             blender file
+     */
+    @SuppressWarnings("unchecked")
+    private void readBMesh(MeshBuilder meshBuilder, Structure meshStructure, BlenderContext blenderContext) throws BlenderFileException {
+        Pointer pMLoop = (Pointer) meshStructure.getFieldValue("mloop");
+        Pointer pMPoly = (Pointer) meshStructure.getFieldValue("mpoly");
+        Pointer pMEdge = (Pointer) meshStructure.getFieldValue("medge");
+        Pointer pMLoopUV = (Pointer) meshStructure.getFieldValue("mloopuv");
+        Vector2f[] uvCoordinatesForFace = new Vector2f[3];
+
+        if (pMPoly.isNotNull() && pMLoop.isNotNull() && pMEdge.isNotNull()) {
+            int faceIndex = 0;
+            List<Structure> polys = pMPoly.fetchData(blenderContext.getInputStream());
+            List<Structure> loops = pMLoop.fetchData(blenderContext.getInputStream());
+            List<Structure> loopuvs = pMLoopUV.isNotNull() ? pMLoopUV.fetchData(blenderContext.getInputStream()) : null;
+            for (Structure poly : polys) {
+                int materialNumber = ((Number) poly.getFieldValue("mat_nr")).intValue();
+                int loopStart = ((Number) poly.getFieldValue("loopstart")).intValue();
+                int totLoop = ((Number) poly.getFieldValue("totloop")).intValue();
+                boolean smooth = (((Number) poly.getFieldValue("flag")).byteValue() & 0x01) != 0x00;
+                int[] vertexIndexes = new int[totLoop];
+                Vector2f[] uvs = loopuvs != null ? new Vector2f[totLoop] : null;
+
+                for (int i = loopStart; i < loopStart + totLoop; ++i) {
+                    vertexIndexes[i - loopStart] = ((Number) loops.get(i).getFieldValue("v")).intValue();
+                    if (uvs != null) {
+                        DynamicArray<Number> loopUVS = (DynamicArray<Number>) loopuvs.get(i).getFieldValue("uv");
+                        uvs[i - loopStart] = new Vector2f(loopUVS.get(0).floatValue(), loopUVS.get(1).floatValue());
+                    }
+                }
+
+                int i = 0;
+                while (i < totLoop - 2) {
+                    int v1 = vertexIndexes[0];
+                    int v2 = vertexIndexes[i + 1];
+                    int v3 = vertexIndexes[i + 2];
+
+                    if (uvs != null) {// uvs always must be added wheater we
+                                      // have texture or not
+                        uvCoordinatesForFace[0] = uvs[0];
+                        uvCoordinatesForFace[1] = uvs[i + 1];
+                        uvCoordinatesForFace[2] = uvs[i + 2];
+                    }
+
+                    meshBuilder.appendFace(v1, v2, v3, smooth, materialNumber, uvs == null ? null : uvCoordinatesForFace, false, faceIndex);
+
+                    ++i;
+                }
+                ++faceIndex;
+            }
+        }
     }
-    
-	/**
-	 * This method reads the mesh from the new BMesh system.
-	 * 
-	 * @param meshBuilder
-	 *            the mesh builder
-	 * @param meshStructure
-	 *            the mesh structure
-	 * @param blenderContext
-	 *            the blender context
-	 * @throws BlenderFileException
-	 *             an exception is thrown when there are problems with the
-	 *             blender file
-	 */
-	@SuppressWarnings("unchecked")
-	private void readBMesh(MeshBuilder meshBuilder, Structure meshStructure, BlenderContext blenderContext) throws BlenderFileException {
-		Pointer pMLoop = (Pointer) meshStructure.getFieldValue("mloop");
-		Pointer pMPoly = (Pointer) meshStructure.getFieldValue("mpoly");
-		Pointer pMEdge = (Pointer) meshStructure.getFieldValue("medge");
-		Pointer pMLoopUV = (Pointer) meshStructure.getFieldValue("mloopuv");
-		Vector2f[] uvCoordinatesForFace = new Vector2f[3];
-
-		if (pMPoly.isNotNull() && pMLoop.isNotNull() && pMEdge.isNotNull()) {
-			int faceIndex = 0;
-			List<Structure> polys = pMPoly.fetchData(blenderContext.getInputStream());
-			List<Structure> loops = pMLoop.fetchData(blenderContext.getInputStream());
-			List<Structure> loopuvs = pMLoopUV.isNotNull() ? pMLoopUV.fetchData(blenderContext.getInputStream()) : null;
-			for (Structure poly : polys) {
-				int materialNumber = ((Number) poly.getFieldValue("mat_nr")).intValue();
-				int loopStart = ((Number) poly.getFieldValue("loopstart")).intValue();
-				int totLoop = ((Number) poly.getFieldValue("totloop")).intValue();
-				boolean smooth = (((Number) poly.getFieldValue("flag")).byteValue() & 0x01) != 0x00;
-				int[] vertexIndexes = new int[totLoop];
-				Vector2f[] uvs = loopuvs != null ? new Vector2f[totLoop] : null;
-
-				for (int i = loopStart; i < loopStart + totLoop; ++i) {
-					vertexIndexes[i - loopStart] = ((Number) loops.get(i).getFieldValue("v")).intValue();
-					if (uvs != null) {
-						DynamicArray<Number> loopUVS = (DynamicArray<Number>) loopuvs.get(i).getFieldValue("uv");
-						uvs[i - loopStart] = new Vector2f(loopUVS.get(0).floatValue(), loopUVS.get(1).floatValue());
-					}
-				}
-
-				int i = 0;
-				while (i < totLoop - 2) {
-					int v1 = vertexIndexes[0];
-					int v2 = vertexIndexes[i + 1];
-					int v3 = vertexIndexes[i + 2];
-
-					if (uvs != null) {// uvs always must be added wheater we
-										// have texture or not
-						uvCoordinatesForFace[0] = uvs[0];
-						uvCoordinatesForFace[1] = uvs[i + 1];
-						uvCoordinatesForFace[2] = uvs[i + 2];
-					}
-
-					meshBuilder.appendFace(v1, v2, v3, smooth, materialNumber, uvs == null ? null : uvCoordinatesForFace, false, faceIndex);
-
-					++i;
-				}
-				++faceIndex;
-			}
-		}
-	}
-
-	/**
-	 * This method reads the mesh from traditional triangle/quad storing
-	 * structures.
-	 * 
-	 * @param meshBuilder
-	 *            the mesh builder
-	 * @param meshStructure
-	 *            the mesh structure
-	 * @param blenderContext
-	 *            the blender context
-	 * @throws BlenderFileException
-	 *             an exception is thrown when there are problems with the
-	 *             blender file
-	 */
-	@SuppressWarnings("unchecked")
-	private void readTraditionalFaces(MeshBuilder meshBuilder, Structure meshStructure, BlenderContext blenderContext) throws BlenderFileException {
-		Pointer pMFace = (Pointer) meshStructure.getFieldValue("mface");
-		List<Structure> mFaces = pMFace.isNotNull() ? pMFace.fetchData(blenderContext.getInputStream()) : null;
-		if (mFaces != null && mFaces.size() > 0) {
-			Pointer pMTFace = (Pointer) meshStructure.getFieldValue("mtface");
-			List<Structure> mtFaces = null;
-
-			if (pMTFace.isNotNull()) {
-				mtFaces = pMTFace.fetchData(blenderContext.getInputStream());
-				int facesAmount = ((Number) meshStructure.getFieldValue("totface")).intValue();
-				if (mtFaces.size() != facesAmount) {
-					throw new BlenderFileException("The amount of faces uv coordinates is not equal to faces amount!");
-				}
-			}
-
-			// indicates if the material with the specified number should have a
-			// texture attached
-			Vector2f[] uvCoordinatesForFace = new Vector2f[3];
-			for (int i = 0; i < mFaces.size(); ++i) {
-				Structure mFace = mFaces.get(i);
-				int materialNumber = ((Number) mFace.getFieldValue("mat_nr")).intValue();
-				boolean smooth = (((Number) mFace.getFieldValue("flag")).byteValue() & 0x01) != 0x00;
-				DynamicArray<Number> uvs = null;
-
-				if (mtFaces != null) {
-					Structure mtFace = mtFaces.get(i);
-					// uvs always must be added wheater we have texture or not
-					uvs = (DynamicArray<Number>) mtFace.getFieldValue("uv");
-					uvCoordinatesForFace[0] = new Vector2f(uvs.get(0, 0).floatValue(), uvs.get(0, 1).floatValue());
-					uvCoordinatesForFace[1] = new Vector2f(uvs.get(1, 0).floatValue(), uvs.get(1, 1).floatValue());
-					uvCoordinatesForFace[2] = new Vector2f(uvs.get(2, 0).floatValue(), uvs.get(2, 1).floatValue());
-				}
-
-				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();
-
-				meshBuilder.appendFace(v1, v2, v3, smooth, materialNumber, uvs == null ? null : uvCoordinatesForFace, false, i);
-				if (v4 > 0) {
-					if (uvs != null) {
-						uvCoordinatesForFace[0] = new Vector2f(uvs.get(0, 0).floatValue(), uvs.get(0, 1).floatValue());
-						uvCoordinatesForFace[1] = new Vector2f(uvs.get(2, 0).floatValue(), uvs.get(2, 1).floatValue());
-						uvCoordinatesForFace[2] = new Vector2f(uvs.get(3, 0).floatValue(), uvs.get(3, 1).floatValue());
-					}
-					meshBuilder.appendFace(v1, v3, v4, smooth, materialNumber, uvs == null ? null : uvCoordinatesForFace, true, i);
-				}
-			}
-		} else {
-			Pointer pMEdge = (Pointer) meshStructure.getFieldValue("medge");
-			List<Structure> mEdges = pMEdge.isNotNull() ? pMEdge.fetchData(blenderContext.getInputStream()) : null;
-			if (mEdges != null && mEdges.size() > 0) {
-				for (int i = 0; i < mEdges.size(); ++i) {
-					Structure mEdge = mEdges.get(i);
-					boolean smooth = (((Number) mEdge.getFieldValue("flag")).byteValue() & 0x01) != 0x00;
-
-					int v1 = ((Number) mEdge.getFieldValue("v1")).intValue();
-					int v2 = ((Number) mEdge.getFieldValue("v2")).intValue();
-
-					meshBuilder.appendEdge(v1, v2, smooth);
-				}
-			}
-		}
-	}
 
     /**
-	 * @return <b>true</b> if the material has at least one generated component and <b>false</b> otherwise
-	 */
+     * This method reads the mesh from traditional triangle/quad storing
+     * structures.
+     * 
+     * @param meshBuilder
+     *            the mesh builder
+     * @param meshStructure
+     *            the mesh structure
+     * @param blenderContext
+     *            the blender context
+     * @throws BlenderFileException
+     *             an exception is thrown when there are problems with the
+     *             blender file
+     */
+    @SuppressWarnings("unchecked")
+    private void readTraditionalFaces(MeshBuilder meshBuilder, Structure meshStructure, BlenderContext blenderContext) throws BlenderFileException {
+        Pointer pMFace = (Pointer) meshStructure.getFieldValue("mface");
+        List<Structure> mFaces = pMFace.isNotNull() ? pMFace.fetchData(blenderContext.getInputStream()) : null;
+        if (mFaces != null && mFaces.size() > 0) {
+            Pointer pMTFace = (Pointer) meshStructure.getFieldValue("mtface");
+            List<Structure> mtFaces = null;
+
+            if (pMTFace.isNotNull()) {
+                mtFaces = pMTFace.fetchData(blenderContext.getInputStream());
+                int facesAmount = ((Number) meshStructure.getFieldValue("totface")).intValue();
+                if (mtFaces.size() != facesAmount) {
+                    throw new BlenderFileException("The amount of faces uv coordinates is not equal to faces amount!");
+                }
+            }
+
+            // indicates if the material with the specified number should have a
+            // texture attached
+            Vector2f[] uvCoordinatesForFace = new Vector2f[3];
+            for (int i = 0; i < mFaces.size(); ++i) {
+                Structure mFace = mFaces.get(i);
+                int materialNumber = ((Number) mFace.getFieldValue("mat_nr")).intValue();
+                boolean smooth = (((Number) mFace.getFieldValue("flag")).byteValue() & 0x01) != 0x00;
+                DynamicArray<Number> uvs = null;
+
+                if (mtFaces != null) {
+                    Structure mtFace = mtFaces.get(i);
+                    // uvs always must be added wheater we have texture or not
+                    uvs = (DynamicArray<Number>) mtFace.getFieldValue("uv");
+                    uvCoordinatesForFace[0] = new Vector2f(uvs.get(0, 0).floatValue(), uvs.get(0, 1).floatValue());
+                    uvCoordinatesForFace[1] = new Vector2f(uvs.get(1, 0).floatValue(), uvs.get(1, 1).floatValue());
+                    uvCoordinatesForFace[2] = new Vector2f(uvs.get(2, 0).floatValue(), uvs.get(2, 1).floatValue());
+                }
+
+                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();
+
+                meshBuilder.appendFace(v1, v2, v3, smooth, materialNumber, uvs == null ? null : uvCoordinatesForFace, false, i);
+                if (v4 > 0) {
+                    if (uvs != null) {
+                        uvCoordinatesForFace[0] = new Vector2f(uvs.get(0, 0).floatValue(), uvs.get(0, 1).floatValue());
+                        uvCoordinatesForFace[1] = new Vector2f(uvs.get(2, 0).floatValue(), uvs.get(2, 1).floatValue());
+                        uvCoordinatesForFace[2] = new Vector2f(uvs.get(3, 0).floatValue(), uvs.get(3, 1).floatValue());
+                    }
+                    meshBuilder.appendFace(v1, v3, v4, smooth, materialNumber, uvs == null ? null : uvCoordinatesForFace, true, i);
+                }
+            }
+        } else {
+            Pointer pMEdge = (Pointer) meshStructure.getFieldValue("medge");
+            List<Structure> mEdges = pMEdge.isNotNull() ? pMEdge.fetchData(blenderContext.getInputStream()) : null;
+            if (mEdges != null && mEdges.size() > 0) {
+                for (int i = 0; i < mEdges.size(); ++i) {
+                    Structure mEdge = mEdges.get(i);
+                    boolean smooth = (((Number) mEdge.getFieldValue("flag")).byteValue() & 0x01) != 0x00;
+
+                    int v1 = ((Number) mEdge.getFieldValue("v1")).intValue();
+                    int v2 = ((Number) mEdge.getFieldValue("v2")).intValue();
+
+                    meshBuilder.appendEdge(v1, v2, smooth);
+                }
+            }
+        }
+    }
+
+    /**
+     * @return <b>true</b> if the material has at least one generated component and <b>false</b> otherwise
+     */
     private boolean areGeneratedTexturesPresent(MaterialContext[] materials) {
-    	if(materials != null) {
-    		for(MaterialContext material : materials) {
-    			if(material != null && material.hasGeneratedTextures()) {
-    				return true;
-    			}
-    		}
-    	}
-    	return false;
+        if (materials != null) {
+            for (MaterialContext material : materials) {
+                if (material != null && material.hasGeneratedTextures()) {
+                    return true;
+                }
+            }
+        }
+        return false;
     }
-    
+
     /**
      * This method returns the vertices colors. Each vertex is stored in byte[4] array.
      * 
@@ -433,11 +431,11 @@ public class MeshHelper extends AbstractBlenderHelper {
             verticesColors = new ArrayList<byte[]>();
             mCol = pMCol.fetchData(blenderContext.getInputStream());
             for (Structure color : mCol) {
-                byte r = ((Number)color.getFieldValue("r")).byteValue();
-                byte g = ((Number)color.getFieldValue("g")).byteValue();
-                byte b = ((Number)color.getFieldValue("b")).byteValue();
-                byte a = ((Number)color.getFieldValue("a")).byteValue();
-                verticesColors.add(new byte[]{b, g, r, a});
+                byte r = ((Number) color.getFieldValue("r")).byteValue();
+                byte g = ((Number) color.getFieldValue("g")).byteValue();
+                byte b = ((Number) color.getFieldValue("b")).byteValue();
+                byte a = ((Number) color.getFieldValue("a")).byteValue();
+                verticesColors.add(new byte[] { b, g, r, a });
             }
         }
         return verticesColors;
@@ -464,21 +462,21 @@ public class MeshHelper extends AbstractBlenderHelper {
 
         Pointer pMVert = (Pointer) meshStructure.getFieldValue("mvert");
         List<Structure> mVerts = pMVert.fetchData(blenderContext.getInputStream());
-        if(this.fixUpAxis) {
-        	for (int i = 0; i < count; ++i) {
+        if (this.fixUpAxis) {
+            for (int i = 0; i < count; ++i) {
                 DynamicArray<Number> coordinates = (DynamicArray<Number>) mVerts.get(i).getFieldValue("co");
                 result[i][0] = new Vector3f(coordinates.get(0).floatValue(), coordinates.get(2).floatValue(), -coordinates.get(1).floatValue());
-                
+
                 DynamicArray<Number> normals = (DynamicArray<Number>) mVerts.get(i).getFieldValue("no");
-                result[i][1] = new Vector3f(normals.get(0).shortValue()/32767.0f, normals.get(2).shortValue()/32767.0f, -normals.get(1).shortValue()/32767.0f);
+                result[i][1] = new Vector3f(normals.get(0).shortValue() / 32767.0f, normals.get(2).shortValue() / 32767.0f, -normals.get(1).shortValue() / 32767.0f);
             }
         } else {
-        	for (int i = 0; i < count; ++i) {
+            for (int i = 0; i < count; ++i) {
                 DynamicArray<Number> coordinates = (DynamicArray<Number>) mVerts.get(i).getFieldValue("co");
                 result[i][0] = new Vector3f(coordinates.get(0).floatValue(), coordinates.get(1).floatValue(), coordinates.get(2).floatValue());
-                
+
                 DynamicArray<Number> normals = (DynamicArray<Number>) mVerts.get(i).getFieldValue("no");
-                result[i][1] = new Vector3f(normals.get(0).shortValue()/32767.0f, normals.get(1).shortValue()/32767.0f, normals.get(2).shortValue()/32767.0f);
+                result[i][1] = new Vector3f(normals.get(0).shortValue() / 32767.0f, normals.get(1).shortValue() / 32767.0f, normals.get(2).shortValue() / 32767.0f);
             }
         }
         return result;

+ 371 - 374
engine/src/blender/com/jme3/scene/plugins/blender/modifiers/ArmatureModifier.java

@@ -43,378 +43,375 @@ import com.jme3.util.BufferUtils;
  * @author Marcin Roguski (Kaelthas)
  */
 /* package */class ArmatureModifier extends Modifier {
-	private static final Logger	LOGGER						= Logger.getLogger(ArmatureModifier.class.getName());
-	private static final int		MAXIMUM_WEIGHTS_PER_VERTEX	= 4;//JME limitation
-
-	private Skeleton skeleton;
-	private Structure objectStructure;
-	private Structure meshStructure;
-	
-	/** Loaded animation data. */
-	private AnimData			animData;
-	/** Old memory address of the mesh that will have the skeleton applied. */
-	private Long				meshOMA;
-
-	/**
-	 * This constructor reads animation data from the object structore. The
-	 * stored data is the AnimData and additional data is armature's OMA.
-	 * 
-	 * @param objectStructure
-	 *            the structure of the object
-	 * @param modifierStructure
-	 *            the structure of the modifier
-	 * @param blenderContext
-	 *            the blender context
-	 * @throws BlenderFileException
-	 *             this exception is thrown when the blender file is somehow
-	 *             corrupted
-	 */
-	public ArmatureModifier(Structure objectStructure, Structure modifierStructure, BlenderContext blenderContext) throws BlenderFileException {
-		Structure meshStructure = ((Pointer) objectStructure.getFieldValue("data")).fetchData(blenderContext.getInputStream()).get(0);
-		Pointer pDvert = (Pointer) meshStructure.getFieldValue("dvert");// dvert = DeformVERTices
-
-		// if pDvert==null then there are not vertex groups and no need to load
-		// skeleton (untill bone envelopes are supported)
-		if (this.validate(modifierStructure, blenderContext) && pDvert.isNotNull()) {
-			Pointer pArmatureObject = (Pointer) modifierStructure.getFieldValue("object");
-			if (pArmatureObject.isNotNull()) {
-				ArmatureHelper armatureHelper = blenderContext.getHelper(ArmatureHelper.class);
-
-				Structure armatureObject = pArmatureObject.fetchData(blenderContext.getInputStream()).get(0);
-
-				// load skeleton
-				Structure armatureStructure = ((Pointer) armatureObject.getFieldValue("data")).fetchData(blenderContext.getInputStream()).get(0);
-
-				Structure pose = ((Pointer) armatureObject.getFieldValue("pose")).fetchData(blenderContext.getInputStream()).get(0);
-				List<Structure> chanbase = ((Structure) pose.getFieldValue("chanbase")).evaluateListBase(blenderContext);
-
-				Map<Long, Structure> bonesPoseChannels = new HashMap<Long, Structure>(chanbase.size());
-				for (Structure poseChannel : chanbase) {
-					Pointer pBone = (Pointer) poseChannel.getFieldValue("bone");
-					bonesPoseChannels.put(pBone.getOldMemoryAddress(), poseChannel);
-				}
-
-				ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class);
-				Matrix4f armatureObjectMatrix = objectHelper.getMatrix(armatureObject, "obmat", true);
-				Matrix4f inverseMeshObjectMatrix = objectHelper.getMatrix(objectStructure, "obmat", true).invertLocal();
-				Matrix4f objectToArmatureTransformation = armatureObjectMatrix.multLocal(inverseMeshObjectMatrix);
-
-				List<Structure> bonebase = ((Structure) armatureStructure.getFieldValue("bonebase")).evaluateListBase(blenderContext);
-				List<Bone> bonesList = new ArrayList<Bone>();
-				for (int i = 0; i < bonebase.size(); ++i) {
-					armatureHelper.buildBones(armatureObject.getOldMemoryAddress(), bonebase.get(i), null, bonesList, objectToArmatureTransformation, bonesPoseChannels, blenderContext);
-				}
-				bonesList.add(0, new Bone(""));
-				Bone[] bones = bonesList.toArray(new Bone[bonesList.size()]);
-				skeleton = new Skeleton(bones);
-				blenderContext.setSkeleton(armatureObject.getOldMemoryAddress(), skeleton);
-				this.objectStructure = objectStructure;
-				this.meshStructure = meshStructure;
-				
-				// read mesh indexes
-				this.meshOMA = meshStructure.getOldMemoryAddress();
-
-				// read animations
-				ArrayList<Animation> animations = new ArrayList<Animation>();
-				List<FileBlockHeader> actionHeaders = blenderContext.getFileBlocks(Integer.valueOf(FileBlockHeader.BLOCK_AC00));
-				if (actionHeaders != null) {// it may happen that the model has armature with no actions
-					for (FileBlockHeader header : actionHeaders) {
-						Structure actionStructure = header.getStructure(blenderContext);
-						String actionName = actionStructure.getName();
-
-						BoneTrack[] tracks = armatureHelper.getTracks(actionStructure, skeleton, blenderContext);
-						if(tracks != null && tracks.length > 0) {
-							// determining the animation time
-							float maximumTrackLength = 0;
-							for (BoneTrack track : tracks) {
-								float length = track.getLength();
-								if (length > maximumTrackLength) {
-									maximumTrackLength = length;
-								}
-							}
-
-							Animation boneAnimation = new Animation(actionName, maximumTrackLength);
-							boneAnimation.setTracks(tracks);
-							animations.add(boneAnimation);
-						}
-					}
-				}
-				//fetching action defined in object
-				Pointer pAction = (Pointer) objectStructure.getFieldValue("action");
-				if (pAction.isNotNull()) {
-					Structure actionStructure = pAction.fetchData(blenderContext.getInputStream()).get(0);
-					String actionName = actionStructure.getName();
-
-					BoneTrack[] tracks = armatureHelper.getTracks(actionStructure, skeleton, blenderContext);
-					if(tracks != null && tracks.length > 0) {
-						// determining the animation time
-						float maximumTrackLength = 0;
-						for (BoneTrack track : tracks) {
-							float length = track.getLength();
-							if (length > maximumTrackLength) {
-								maximumTrackLength = length;
-							}
-						}
-
-						Animation boneAnimation = new Animation(actionName, maximumTrackLength);
-						boneAnimation.setTracks(tracks);
-						animations.add(boneAnimation);
-					}
-				}
-				
-				animData = new AnimData(skeleton, animations);
-
-				// store the animation data for each bone
-				for (Bone bone : bones) {
-					Long boneOma = armatureHelper.getBoneOMA(bone);
-					if (boneOma != null) {
-						blenderContext.setAnimData(boneOma, animData);
-					}
-				}
-			} else {
-				modifying = false;
-			}
-		}
-	}
-
-	@Override
-	@SuppressWarnings("unchecked")
-	public Node apply(Node node, BlenderContext blenderContext) {
-		if (invalid) {
-			LOGGER.log(Level.WARNING, "Armature modifier is invalid! Cannot be applied to: {0}", node.getName());
-		}// if invalid, animData will be null
-		if (animData == null || skeleton == null) {
-			return node;
-		}
-
-		// setting weights for bones
-		List<Geometry> geomList = (List<Geometry>) blenderContext.getLoadedFeature(meshOMA, LoadedFeatureDataType.LOADED_FEATURE);
-		MeshContext meshContext = blenderContext.getMeshContext(meshOMA);
-		int[] bonesGroups = new int[] { 0 };
-		for (Geometry geom : geomList) {
-			int materialIndex = meshContext.getMaterialIndex(geom);
-			Mesh mesh = geom.getMesh();
-			
-			try {
-				VertexBuffer[] buffers = this.readVerticesWeightsData(objectStructure, meshStructure, skeleton, materialIndex, bonesGroups, blenderContext);
-				if (buffers != null) {
-					mesh.setMaxNumWeights(bonesGroups[0]);
-					mesh.setBuffer(buffers[0]);
-					mesh.setBuffer(buffers[1]);
-					
-					VertexBuffer bindNormalBuffer = (meshContext.getBindNormalBuffer(materialIndex));
-					if(bindNormalBuffer != null) {
-						mesh.setBuffer(bindNormalBuffer);
-					}
-					VertexBuffer bindPoseBuffer = (meshContext.getBindPoseBuffer(materialIndex));
-					if(bindPoseBuffer != null) {
-						mesh.setBuffer(bindPoseBuffer);
-					}
-					//change the usage type of vertex and normal buffers from Static to Stream
-					mesh.getBuffer(Type.Position).setUsage(Usage.Stream);
-					mesh.getBuffer(Type.Normal).setUsage(Usage.Stream);
-				}
-			} catch (BlenderFileException e) {
-				LOGGER.log(Level.SEVERE, e.getLocalizedMessage(), e);
-				this.invalid = true;
-				return node;
-			}
-		}
-
-		// applying animations
-		AnimControl control = new AnimControl(animData.skeleton);
-		ArrayList<Animation> animList = animData.anims;
-		if (animList != null && animList.size() > 0) {
-			HashMap<String, Animation> anims = new HashMap<String, Animation>(animList.size());
-			for (int i = 0; i < animList.size(); ++i) {
-				Animation animation = animList.get(i);
-				anims.put(animation.getName(), animation);
-			}
-			control.setAnimations(anims);
-		}
-		node.addControl(control);
-		node.addControl(new SkeletonControl(animData.skeleton));
-
-		return node;
-	}
-
-	/**
-	 * This method reads mesh indexes
-	 * 
-	 * @param objectStructure
-	 *            structure of the object that has the armature modifier applied
-	 * @param meshStructure
-	 *            the structure of the object's mesh
-	 * @param blenderContext
-	 *            the blender context
-	 * @throws BlenderFileException
-	 *             this exception is thrown when the blend file structure is
-	 *             somehow invalid or corrupted
-	 */
-	private VertexBuffer[] readVerticesWeightsData(Structure objectStructure, Structure meshStructure, Skeleton skeleton, int materialIndex,
-										 int[] bonesGroups, BlenderContext blenderContext) throws BlenderFileException {
-		ArmatureHelper armatureHelper = blenderContext.getHelper(ArmatureHelper.class);
-		Structure defBase = (Structure) objectStructure.getFieldValue("defbase");
-		Map<Integer, Integer> groupToBoneIndexMap = armatureHelper.getGroupToBoneIndexMap(defBase, skeleton, blenderContext);
-
-		MeshContext meshContext = blenderContext.getMeshContext(meshStructure.getOldMemoryAddress());
-		
-		return this.getBoneWeightAndIndexBuffer(meshStructure, meshContext.getVertexCount(materialIndex), 
-									bonesGroups, meshContext.getVertexReferenceMap(materialIndex), groupToBoneIndexMap, blenderContext);
-	}
-
-	/**
-	 * 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 blenderContext
-	 *            the blender context
-	 * @return arrays of vertices weights and their bone indices and (as an
-	 *         output 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
-	 */
-	private VertexBuffer[] getBoneWeightAndIndexBuffer(Structure meshStructure, int vertexListSize, int[] bonesGroups, Map<Integer, List<Integer>> vertexReferenceMap, Map<Integer, Integer> groupToBoneIndexMap, BlenderContext blenderContext)
-			throws BlenderFileException {
-		bonesGroups[0] = 0;
-		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.isNotNull()) {// assigning weights and bone indices
-			boolean warnAboutTooManyVertexWeights = false;
-			List<Structure> dverts = pDvert.fetchData(blenderContext.getInputStream());// dverts.size() == verticesAmount (one dvert per vertex in blender)
-			int vertexIndex = 0;
-			//use tree map to sort weights from the lowest to the highest ones
-			TreeMap<Float, Integer> weightToIndexMap = new TreeMap<Float, Integer>();
-			
-			for (Structure dvert : dverts) {
-				List<Integer> vertexIndices = vertexReferenceMap.get(Integer.valueOf(vertexIndex));// we fetch the referenced vertices here
-				if(vertexIndices != null) {
-					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");
-					if (totweight > 0 && groupToBoneIndexMap != null) {
-						weightToIndexMap.clear();
-						int weightIndex = 0;
-						List<Structure> dw = pDW.fetchData(blenderContext.getInputStream());
-						for (Structure deformWeight : dw) {
-							Integer boneIndex = groupToBoneIndexMap.get(((Number) deformWeight.getFieldValue("def_nr")).intValue());
-							// null here means that we came accross group that has no bone attached to
-							if (boneIndex != null) {
-								float weight = ((Number) deformWeight.getFieldValue("weight")).floatValue();
-								if(weightIndex < MAXIMUM_WEIGHTS_PER_VERTEX) {
-									if (weight == 0.0f) {
-										boneIndex = Integer.valueOf(0);
-									}
-									// we apply the weight to all referenced vertices
-									for (Integer index : vertexIndices) {
-										weightsFloatData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX + weightIndex, weight);
-										indicesData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX + weightIndex, boneIndex.byteValue());
-									}
-									weightToIndexMap.put(weight, weightIndex);
-									bonesGroups[0] = Math.max(bonesGroups[0], weightIndex + 1);
-								} else if(weight > 0) {//if weight is zero the simply ignore it
-									warnAboutTooManyVertexWeights = true;
-									Entry<Float, Integer> lowestWeightAndIndex = weightToIndexMap.firstEntry();
-									if(lowestWeightAndIndex != null && lowestWeightAndIndex.getKey() < weight) {
-										weightsFloatData.put(lowestWeightAndIndex.getValue(), weight);
-										indicesData.put(lowestWeightAndIndex.getValue(), boneIndex.byteValue());
-										weightToIndexMap.remove(lowestWeightAndIndex.getKey());
-										weightToIndexMap.put(weight, lowestWeightAndIndex.getValue());
-									}
-								}
-							}
-							++weightIndex;
-						}
-					} else {
-						// 0.0 weight indicates, do not transform this vertex, but keep it in bind pose.
-						for (Integer index : vertexIndices) {
-							weightsFloatData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX, 0.0f);
-							indicesData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX, (byte) 0);
-						}
-					}
-				}
-				++vertexIndex;
-			}
-			
-			if(warnAboutTooManyVertexWeights) {
-				LOGGER.log(Level.WARNING, "{0} has vertices with more than 4 weights assigned. The model may not behave as it should.", meshStructure.getName());
-			}
-		} 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] = Math.max(bonesGroups[0], 1);
-
-		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.
-	 * 
-	 * @param vertCount
-	 *            amount of vertices
-	 * @param weightsFloatData
-	 *            weights for vertices
-	 */
-	private void endBoneAssigns(int vertCount, FloatBuffer weightsFloatData) {
-		weightsFloatData.rewind();
-		float[] weights = new float[MAXIMUM_WEIGHTS_PER_VERTEX];
-		for (int v = 0; v < vertCount; ++v) {
-			float sum = 0;
-			for (int i = 0; i < MAXIMUM_WEIGHTS_PER_VERTEX; ++i) {
-				weights[i] = weightsFloatData.get();
-				sum += weights[i];
-			}
-			if (sum != 1f && sum != 0.0f) {
-				weightsFloatData.position(weightsFloatData.position() - MAXIMUM_WEIGHTS_PER_VERTEX);
-				// compute new vals based on sum
-				float sumToB = 1f / sum;
-				for (int i = 0; i < MAXIMUM_WEIGHTS_PER_VERTEX; ++i) {
-					weightsFloatData.put(weights[i] * sumToB);
-				}
-			}
-		}
-		weightsFloatData.rewind();
-	}
-
-	@Override
-	public String getType() {
-		return Modifier.ARMATURE_MODIFIER_DATA;
-	}
+    private static final Logger LOGGER                     = Logger.getLogger(ArmatureModifier.class.getName());
+    private static final int    MAXIMUM_WEIGHTS_PER_VERTEX = 4;                                                  // JME limitation
+
+    private Skeleton            skeleton;
+    private Structure           objectStructure;
+    private Structure           meshStructure;
+
+    /** Loaded animation data. */
+    private AnimData            animData;
+    /** Old memory address of the mesh that will have the skeleton applied. */
+    private Long                meshOMA;
+
+    /**
+     * This constructor reads animation data from the object structore. The
+     * stored data is the AnimData and additional data is armature's OMA.
+     * 
+     * @param objectStructure
+     *            the structure of the object
+     * @param modifierStructure
+     *            the structure of the modifier
+     * @param blenderContext
+     *            the blender context
+     * @throws BlenderFileException
+     *             this exception is thrown when the blender file is somehow
+     *             corrupted
+     */
+    public ArmatureModifier(Structure objectStructure, Structure modifierStructure, BlenderContext blenderContext) throws BlenderFileException {
+        Structure meshStructure = ((Pointer) objectStructure.getFieldValue("data")).fetchData(blenderContext.getInputStream()).get(0);
+        Pointer pDvert = (Pointer) meshStructure.getFieldValue("dvert");// dvert = DeformVERTices
+
+        // if pDvert==null then there are not vertex groups and no need to load
+        // skeleton (untill bone envelopes are supported)
+        if (this.validate(modifierStructure, blenderContext) && pDvert.isNotNull()) {
+            Pointer pArmatureObject = (Pointer) modifierStructure.getFieldValue("object");
+            if (pArmatureObject.isNotNull()) {
+                ArmatureHelper armatureHelper = blenderContext.getHelper(ArmatureHelper.class);
+
+                Structure armatureObject = pArmatureObject.fetchData(blenderContext.getInputStream()).get(0);
+
+                // load skeleton
+                Structure armatureStructure = ((Pointer) armatureObject.getFieldValue("data")).fetchData(blenderContext.getInputStream()).get(0);
+
+                Structure pose = ((Pointer) armatureObject.getFieldValue("pose")).fetchData(blenderContext.getInputStream()).get(0);
+                List<Structure> chanbase = ((Structure) pose.getFieldValue("chanbase")).evaluateListBase(blenderContext);
+
+                Map<Long, Structure> bonesPoseChannels = new HashMap<Long, Structure>(chanbase.size());
+                for (Structure poseChannel : chanbase) {
+                    Pointer pBone = (Pointer) poseChannel.getFieldValue("bone");
+                    bonesPoseChannels.put(pBone.getOldMemoryAddress(), poseChannel);
+                }
+
+                ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class);
+                Matrix4f armatureObjectMatrix = objectHelper.getMatrix(armatureObject, "obmat", true);
+                Matrix4f inverseMeshObjectMatrix = objectHelper.getMatrix(objectStructure, "obmat", true).invertLocal();
+                Matrix4f objectToArmatureTransformation = armatureObjectMatrix.multLocal(inverseMeshObjectMatrix);
+
+                List<Structure> bonebase = ((Structure) armatureStructure.getFieldValue("bonebase")).evaluateListBase(blenderContext);
+                List<Bone> bonesList = new ArrayList<Bone>();
+                for (int i = 0; i < bonebase.size(); ++i) {
+                    armatureHelper.buildBones(armatureObject.getOldMemoryAddress(), bonebase.get(i), null, bonesList, objectToArmatureTransformation, bonesPoseChannels, blenderContext);
+                }
+                bonesList.add(0, new Bone(""));
+                Bone[] bones = bonesList.toArray(new Bone[bonesList.size()]);
+                skeleton = new Skeleton(bones);
+                blenderContext.setSkeleton(armatureObject.getOldMemoryAddress(), skeleton);
+                this.objectStructure = objectStructure;
+                this.meshStructure = meshStructure;
+
+                // read mesh indexes
+                this.meshOMA = meshStructure.getOldMemoryAddress();
+
+                // read animations
+                ArrayList<Animation> animations = new ArrayList<Animation>();
+                List<FileBlockHeader> actionHeaders = blenderContext.getFileBlocks(Integer.valueOf(FileBlockHeader.BLOCK_AC00));
+                if (actionHeaders != null) {// it may happen that the model has armature with no actions
+                    for (FileBlockHeader header : actionHeaders) {
+                        Structure actionStructure = header.getStructure(blenderContext);
+                        String actionName = actionStructure.getName();
+
+                        BoneTrack[] tracks = armatureHelper.getTracks(actionStructure, skeleton, blenderContext);
+                        if (tracks != null && tracks.length > 0) {
+                            // determining the animation time
+                            float maximumTrackLength = 0;
+                            for (BoneTrack track : tracks) {
+                                float length = track.getLength();
+                                if (length > maximumTrackLength) {
+                                    maximumTrackLength = length;
+                                }
+                            }
+
+                            Animation boneAnimation = new Animation(actionName, maximumTrackLength);
+                            boneAnimation.setTracks(tracks);
+                            animations.add(boneAnimation);
+                        }
+                    }
+                }
+                // fetching action defined in object
+                Pointer pAction = (Pointer) objectStructure.getFieldValue("action");
+                if (pAction.isNotNull()) {
+                    Structure actionStructure = pAction.fetchData(blenderContext.getInputStream()).get(0);
+                    String actionName = actionStructure.getName();
+
+                    BoneTrack[] tracks = armatureHelper.getTracks(actionStructure, skeleton, blenderContext);
+                    if (tracks != null && tracks.length > 0) {
+                        // determining the animation time
+                        float maximumTrackLength = 0;
+                        for (BoneTrack track : tracks) {
+                            float length = track.getLength();
+                            if (length > maximumTrackLength) {
+                                maximumTrackLength = length;
+                            }
+                        }
+
+                        Animation boneAnimation = new Animation(actionName, maximumTrackLength);
+                        boneAnimation.setTracks(tracks);
+                        animations.add(boneAnimation);
+                    }
+                }
+
+                animData = new AnimData(skeleton, animations);
+
+                // store the animation data for each bone
+                for (Bone bone : bones) {
+                    Long boneOma = armatureHelper.getBoneOMA(bone);
+                    if (boneOma != null) {
+                        blenderContext.setAnimData(boneOma, animData);
+                    }
+                }
+            } else {
+                modifying = false;
+            }
+        }
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public Node apply(Node node, BlenderContext blenderContext) {
+        if (invalid) {
+            LOGGER.log(Level.WARNING, "Armature modifier is invalid! Cannot be applied to: {0}", node.getName());
+        }// if invalid, animData will be null
+        if (animData == null || skeleton == null) {
+            return node;
+        }
+
+        // setting weights for bones
+        List<Geometry> geomList = (List<Geometry>) blenderContext.getLoadedFeature(meshOMA, LoadedFeatureDataType.LOADED_FEATURE);
+        MeshContext meshContext = blenderContext.getMeshContext(meshOMA);
+        int[] bonesGroups = new int[] { 0 };
+        for (Geometry geom : geomList) {
+            int materialIndex = meshContext.getMaterialIndex(geom);
+            Mesh mesh = geom.getMesh();
+
+            try {
+                VertexBuffer[] buffers = this.readVerticesWeightsData(objectStructure, meshStructure, skeleton, materialIndex, bonesGroups, blenderContext);
+                if (buffers != null) {
+                    mesh.setMaxNumWeights(bonesGroups[0]);
+                    mesh.setBuffer(buffers[0]);
+                    mesh.setBuffer(buffers[1]);
+
+                    VertexBuffer bindNormalBuffer = (meshContext.getBindNormalBuffer(materialIndex));
+                    if (bindNormalBuffer != null) {
+                        mesh.setBuffer(bindNormalBuffer);
+                    }
+                    VertexBuffer bindPoseBuffer = (meshContext.getBindPoseBuffer(materialIndex));
+                    if (bindPoseBuffer != null) {
+                        mesh.setBuffer(bindPoseBuffer);
+                    }
+                    // change the usage type of vertex and normal buffers from Static to Stream
+                    mesh.getBuffer(Type.Position).setUsage(Usage.Stream);
+                    mesh.getBuffer(Type.Normal).setUsage(Usage.Stream);
+                }
+            } catch (BlenderFileException e) {
+                LOGGER.log(Level.SEVERE, e.getLocalizedMessage(), e);
+                this.invalid = true;
+                return node;
+            }
+        }
+
+        // applying animations
+        AnimControl control = new AnimControl(animData.skeleton);
+        ArrayList<Animation> animList = animData.anims;
+        if (animList != null && animList.size() > 0) {
+            HashMap<String, Animation> anims = new HashMap<String, Animation>(animList.size());
+            for (int i = 0; i < animList.size(); ++i) {
+                Animation animation = animList.get(i);
+                anims.put(animation.getName(), animation);
+            }
+            control.setAnimations(anims);
+        }
+        node.addControl(control);
+        node.addControl(new SkeletonControl(animData.skeleton));
+
+        return node;
+    }
+
+    /**
+     * This method reads mesh indexes
+     * 
+     * @param objectStructure
+     *            structure of the object that has the armature modifier applied
+     * @param meshStructure
+     *            the structure of the object's mesh
+     * @param blenderContext
+     *            the blender context
+     * @throws BlenderFileException
+     *             this exception is thrown when the blend file structure is
+     *             somehow invalid or corrupted
+     */
+    private VertexBuffer[] readVerticesWeightsData(Structure objectStructure, Structure meshStructure, Skeleton skeleton, int materialIndex, int[] bonesGroups, BlenderContext blenderContext) throws BlenderFileException {
+        ArmatureHelper armatureHelper = blenderContext.getHelper(ArmatureHelper.class);
+        Structure defBase = (Structure) objectStructure.getFieldValue("defbase");
+        Map<Integer, Integer> groupToBoneIndexMap = armatureHelper.getGroupToBoneIndexMap(defBase, skeleton, blenderContext);
+
+        MeshContext meshContext = blenderContext.getMeshContext(meshStructure.getOldMemoryAddress());
+
+        return this.getBoneWeightAndIndexBuffer(meshStructure, meshContext.getVertexCount(materialIndex), bonesGroups, meshContext.getVertexReferenceMap(materialIndex), groupToBoneIndexMap, blenderContext);
+    }
+
+    /**
+     * 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 blenderContext
+     *            the blender context
+     * @return arrays of vertices weights and their bone indices and (as an
+     *         output 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
+     */
+    private VertexBuffer[] getBoneWeightAndIndexBuffer(Structure meshStructure, int vertexListSize, int[] bonesGroups, Map<Integer, List<Integer>> vertexReferenceMap, Map<Integer, Integer> groupToBoneIndexMap, BlenderContext blenderContext) throws BlenderFileException {
+        bonesGroups[0] = 0;
+        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.isNotNull()) {// assigning weights and bone indices
+            boolean warnAboutTooManyVertexWeights = false;
+            List<Structure> dverts = pDvert.fetchData(blenderContext.getInputStream());// dverts.size() == verticesAmount (one dvert per vertex in blender)
+            int vertexIndex = 0;
+            // use tree map to sort weights from the lowest to the highest ones
+            TreeMap<Float, Integer> weightToIndexMap = new TreeMap<Float, Integer>();
+
+            for (Structure dvert : dverts) {
+                List<Integer> vertexIndices = vertexReferenceMap.get(Integer.valueOf(vertexIndex));// we fetch the referenced vertices here
+                if (vertexIndices != null) {
+                    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");
+                    if (totweight > 0 && groupToBoneIndexMap != null) {
+                        weightToIndexMap.clear();
+                        int weightIndex = 0;
+                        List<Structure> dw = pDW.fetchData(blenderContext.getInputStream());
+                        for (Structure deformWeight : dw) {
+                            Integer boneIndex = groupToBoneIndexMap.get(((Number) deformWeight.getFieldValue("def_nr")).intValue());
+                            // null here means that we came accross group that has no bone attached to
+                            if (boneIndex != null) {
+                                float weight = ((Number) deformWeight.getFieldValue("weight")).floatValue();
+                                if (weightIndex < MAXIMUM_WEIGHTS_PER_VERTEX) {
+                                    if (weight == 0.0f) {
+                                        boneIndex = Integer.valueOf(0);
+                                    }
+                                    // we apply the weight to all referenced vertices
+                                    for (Integer index : vertexIndices) {
+                                        weightsFloatData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX + weightIndex, weight);
+                                        indicesData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX + weightIndex, boneIndex.byteValue());
+                                    }
+                                    weightToIndexMap.put(weight, weightIndex);
+                                    bonesGroups[0] = Math.max(bonesGroups[0], weightIndex + 1);
+                                } else if (weight > 0) {// if weight is zero the simply ignore it
+                                    warnAboutTooManyVertexWeights = true;
+                                    Entry<Float, Integer> lowestWeightAndIndex = weightToIndexMap.firstEntry();
+                                    if (lowestWeightAndIndex != null && lowestWeightAndIndex.getKey() < weight) {
+                                        weightsFloatData.put(lowestWeightAndIndex.getValue(), weight);
+                                        indicesData.put(lowestWeightAndIndex.getValue(), boneIndex.byteValue());
+                                        weightToIndexMap.remove(lowestWeightAndIndex.getKey());
+                                        weightToIndexMap.put(weight, lowestWeightAndIndex.getValue());
+                                    }
+                                }
+                            }
+                            ++weightIndex;
+                        }
+                    } else {
+                        // 0.0 weight indicates, do not transform this vertex, but keep it in bind pose.
+                        for (Integer index : vertexIndices) {
+                            weightsFloatData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX, 0.0f);
+                            indicesData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX, (byte) 0);
+                        }
+                    }
+                }
+                ++vertexIndex;
+            }
+
+            if (warnAboutTooManyVertexWeights) {
+                LOGGER.log(Level.WARNING, "{0} has vertices with more than 4 weights assigned. The model may not behave as it should.", meshStructure.getName());
+            }
+        } 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] = Math.max(bonesGroups[0], 1);
+
+        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.
+     * 
+     * @param vertCount
+     *            amount of vertices
+     * @param weightsFloatData
+     *            weights for vertices
+     */
+    private void endBoneAssigns(int vertCount, FloatBuffer weightsFloatData) {
+        weightsFloatData.rewind();
+        float[] weights = new float[MAXIMUM_WEIGHTS_PER_VERTEX];
+        for (int v = 0; v < vertCount; ++v) {
+            float sum = 0;
+            for (int i = 0; i < MAXIMUM_WEIGHTS_PER_VERTEX; ++i) {
+                weights[i] = weightsFloatData.get();
+                sum += weights[i];
+            }
+            if (sum != 1f && sum != 0.0f) {
+                weightsFloatData.position(weightsFloatData.position() - MAXIMUM_WEIGHTS_PER_VERTEX);
+                // compute new vals based on sum
+                float sumToB = 1f / sum;
+                for (int i = 0; i < MAXIMUM_WEIGHTS_PER_VERTEX; ++i) {
+                    weightsFloatData.put(weights[i] * sumToB);
+                }
+            }
+        }
+        weightsFloatData.rewind();
+    }
+
+    @Override
+    public String getType() {
+        return Modifier.ARMATURE_MODIFIER_DATA;
+    }
 }

+ 113 - 113
engine/src/blender/com/jme3/scene/plugins/blender/modifiers/ArrayModifier.java

@@ -29,116 +29,116 @@ import java.util.logging.Logger;
  * 
  * @author Marcin Roguski (Kaelthas)
  */
-/*package*/ class ArrayModifier extends Modifier {
-	private static final Logger LOGGER = Logger.getLogger(ArrayModifier.class.getName());
-	
-	/** Parameters of the modifier. */
-	private Map<String, Object> modifierData = new HashMap<String, Object>();
-	
-	/**
-	 * This constructor reads array data from the modifier structure. The
-	 * stored data is a map of parameters for array modifier. No additional data
-	 * is loaded.
-	 * 
-	 * @param objectStructure
-	 *            the structure of the object
-	 * @param modifierStructure
-	 *            the structure of the modifier
-	 * @param blenderContext
-	 *            the blender context
-	 * @throws BlenderFileException
-	 *             this exception is thrown when the blender file is somehow
-	 *             corrupted
-	 */
-	@SuppressWarnings("unchecked")
-	public ArrayModifier(Structure modifierStructure, BlenderContext blenderContext) throws BlenderFileException {
-		if(this.validate(modifierStructure, blenderContext)) {
-	        Number fittype = (Number) modifierStructure.getFieldValue("fit_type");
-	        modifierData.put("fittype", fittype);
-	        switch (fittype.intValue()) {
-	            case 0:// FIXED COUNT
-	            	modifierData.put("count", modifierStructure.getFieldValue("count"));
-	                break;
-	            case 1:// FIXED LENGTH
-	            	modifierData.put("length", modifierStructure.getFieldValue("length"));
-	                break;
-	            case 2:// FITCURVE
-	                Pointer pCurveOb = (Pointer) modifierStructure.getFieldValue("curve_ob");
-	                float length = 0;
-	                if (pCurveOb.isNotNull()) {
-	                    Structure curveStructure = pCurveOb.fetchData(blenderContext.getInputStream()).get(0);
-	                    ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class);
-	                    Node curveObject = (Node) objectHelper.toObject(curveStructure, blenderContext);
-	                    Set<Number> referencesToCurveLengths = new HashSet<Number>(curveObject.getChildren().size());
-	                    for (Spatial spatial : curveObject.getChildren()) {
-	                        if (spatial instanceof Geometry) {
-	                            Mesh mesh = ((Geometry) spatial).getMesh();
-	                            if (mesh instanceof Curve) {
-	                                length += ((Curve) mesh).getLength();
-	                            } else {
-	                                //if bevel object has several parts then each mesh will have the same reference
-	                                //to length value (and we should use only one)
-	                                Number curveLength = spatial.getUserData("curveLength");
-	                                if (curveLength != null && !referencesToCurveLengths.contains(curveLength)) {
-	                                    length += curveLength.floatValue();
-	                                    referencesToCurveLengths.add(curveLength);
-	                                }
-	                            }
-	                        }
-	                    }
-	                }
-	                modifierData.put("length", Float.valueOf(length));
-	                modifierData.put("fittype", Integer.valueOf(1));// treat it like FIXED LENGTH
-	                break;
-	            default:
-	                assert false : "Unknown array modifier fit type: " + fittype;
-	        }
-	
-	        // offset parameters
-	        int offsettype = ((Number) modifierStructure.getFieldValue("offset_type")).intValue();
-	        if ((offsettype & 0x01) != 0) {// Constant offset
-	            DynamicArray<Number> offsetArray = (DynamicArray<Number>) modifierStructure.getFieldValue("offset");
-	            float[] offset = new float[]{offsetArray.get(0).floatValue(), offsetArray.get(1).floatValue(), offsetArray.get(2).floatValue()};
-	            modifierData.put("offset", offset);
-	        }
-	        if ((offsettype & 0x02) != 0) {// Relative offset
-	            DynamicArray<Number> scaleArray = (DynamicArray<Number>) modifierStructure.getFieldValue("scale");
-	            float[] scale = new float[]{scaleArray.get(0).floatValue(), scaleArray.get(1).floatValue(), scaleArray.get(2).floatValue()};
-	            modifierData.put("scale", scale);
-	        }
-	        if ((offsettype & 0x04) != 0) {// Object offset
-	            Pointer pOffsetObject = (Pointer) modifierStructure.getFieldValue("offset_ob");
-	            if (pOffsetObject.isNotNull()) {
-	            	modifierData.put("offsetob", pOffsetObject);
-	            }
-	        }
-	
-	        // start cap and end cap
-	        Pointer pStartCap = (Pointer) modifierStructure.getFieldValue("start_cap");
-	        if (pStartCap.isNotNull()) {
-	        	modifierData.put("startcap", pStartCap);
-	        }
-	        Pointer pEndCap = (Pointer) modifierStructure.getFieldValue("end_cap");
-	        if (pEndCap.isNotNull()) {
-	        	modifierData.put("endcap", pEndCap);
-	        }
-		}
-	}
-	
-	@Override
-	public Node apply(Node node, BlenderContext blenderContext) {
-		if(invalid) {
-			LOGGER.log(Level.WARNING, "Array modifier is invalid! Cannot be applied to: {0}", node.getName());
-			return node;
-		}
+/* package */class ArrayModifier extends Modifier {
+    private static final Logger LOGGER       = Logger.getLogger(ArrayModifier.class.getName());
+
+    /** Parameters of the modifier. */
+    private Map<String, Object> modifierData = new HashMap<String, Object>();
+
+    /**
+     * This constructor reads array data from the modifier structure. The
+     * stored data is a map of parameters for array modifier. No additional data
+     * is loaded.
+     * 
+     * @param objectStructure
+     *            the structure of the object
+     * @param modifierStructure
+     *            the structure of the modifier
+     * @param blenderContext
+     *            the blender context
+     * @throws BlenderFileException
+     *             this exception is thrown when the blender file is somehow
+     *             corrupted
+     */
+    @SuppressWarnings("unchecked")
+    public ArrayModifier(Structure modifierStructure, BlenderContext blenderContext) throws BlenderFileException {
+        if (this.validate(modifierStructure, blenderContext)) {
+            Number fittype = (Number) modifierStructure.getFieldValue("fit_type");
+            modifierData.put("fittype", fittype);
+            switch (fittype.intValue()) {
+                case 0:// FIXED COUNT
+                    modifierData.put("count", modifierStructure.getFieldValue("count"));
+                    break;
+                case 1:// FIXED LENGTH
+                    modifierData.put("length", modifierStructure.getFieldValue("length"));
+                    break;
+                case 2:// FITCURVE
+                    Pointer pCurveOb = (Pointer) modifierStructure.getFieldValue("curve_ob");
+                    float length = 0;
+                    if (pCurveOb.isNotNull()) {
+                        Structure curveStructure = pCurveOb.fetchData(blenderContext.getInputStream()).get(0);
+                        ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class);
+                        Node curveObject = (Node) objectHelper.toObject(curveStructure, blenderContext);
+                        Set<Number> referencesToCurveLengths = new HashSet<Number>(curveObject.getChildren().size());
+                        for (Spatial spatial : curveObject.getChildren()) {
+                            if (spatial instanceof Geometry) {
+                                Mesh mesh = ((Geometry) spatial).getMesh();
+                                if (mesh instanceof Curve) {
+                                    length += ((Curve) mesh).getLength();
+                                } else {
+                                    // if bevel object has several parts then each mesh will have the same reference
+                                    // to length value (and we should use only one)
+                                    Number curveLength = spatial.getUserData("curveLength");
+                                    if (curveLength != null && !referencesToCurveLengths.contains(curveLength)) {
+                                        length += curveLength.floatValue();
+                                        referencesToCurveLengths.add(curveLength);
+                                    }
+                                }
+                            }
+                        }
+                    }
+                    modifierData.put("length", Float.valueOf(length));
+                    modifierData.put("fittype", Integer.valueOf(1));// treat it like FIXED LENGTH
+                    break;
+                default:
+                    assert false : "Unknown array modifier fit type: " + fittype;
+            }
+
+            // offset parameters
+            int offsettype = ((Number) modifierStructure.getFieldValue("offset_type")).intValue();
+            if ((offsettype & 0x01) != 0) {// Constant offset
+                DynamicArray<Number> offsetArray = (DynamicArray<Number>) modifierStructure.getFieldValue("offset");
+                float[] offset = new float[] { offsetArray.get(0).floatValue(), offsetArray.get(1).floatValue(), offsetArray.get(2).floatValue() };
+                modifierData.put("offset", offset);
+            }
+            if ((offsettype & 0x02) != 0) {// Relative offset
+                DynamicArray<Number> scaleArray = (DynamicArray<Number>) modifierStructure.getFieldValue("scale");
+                float[] scale = new float[] { scaleArray.get(0).floatValue(), scaleArray.get(1).floatValue(), scaleArray.get(2).floatValue() };
+                modifierData.put("scale", scale);
+            }
+            if ((offsettype & 0x04) != 0) {// Object offset
+                Pointer pOffsetObject = (Pointer) modifierStructure.getFieldValue("offset_ob");
+                if (pOffsetObject.isNotNull()) {
+                    modifierData.put("offsetob", pOffsetObject);
+                }
+            }
+
+            // start cap and end cap
+            Pointer pStartCap = (Pointer) modifierStructure.getFieldValue("start_cap");
+            if (pStartCap.isNotNull()) {
+                modifierData.put("startcap", pStartCap);
+            }
+            Pointer pEndCap = (Pointer) modifierStructure.getFieldValue("end_cap");
+            if (pEndCap.isNotNull()) {
+                modifierData.put("endcap", pEndCap);
+            }
+        }
+    }
+
+    @Override
+    public Node apply(Node node, BlenderContext blenderContext) {
+        if (invalid) {
+            LOGGER.log(Level.WARNING, "Array modifier is invalid! Cannot be applied to: {0}", node.getName());
+            return node;
+        }
         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};
+            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};
+            scale = new float[] { 0.0f, 0.0f, 0.0f };
         } else {
             // getting bounding box
             node.updateModelBound();
@@ -158,7 +158,7 @@ import java.util.logging.Logger;
         }
 
         // adding object's offset
-        float[] objectOffset = new float[]{0.0f, 0.0f, 0.0f};
+        float[] objectOffset = new float[] { 0.0f, 0.0f, 0.0f };
         Pointer pOffsetObject = (Pointer) modifierData.get("offsetob");
         if (pOffsetObject != null) {
             FileBlockHeader offsetObjectBlock = blenderContext.getFileBlock(pOffsetObject.getOldMemoryAddress());
@@ -175,8 +175,8 @@ import java.util.logging.Logger;
         }
 
         // getting start and end caps
-        Node[] caps = new Node[]{null, null};
-        Pointer[] pCaps = new Pointer[]{(Pointer) modifierData.get("startcap"), (Pointer) modifierData.get("endcap")};
+        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) blenderContext.getLoadedFeature(pCaps[i].getOldMemoryAddress(), LoadedFeatureDataType.LOADED_FEATURE);
@@ -238,10 +238,10 @@ import java.util.logging.Logger;
             }
         }
         return node;
-	}
-	
-	@Override
-	public String getType() {
-		return ARRAY_MODIFIER_DATA;
-	}
+    }
+
+    @Override
+    public String getType() {
+        return ARRAY_MODIFIER_DATA;
+    }
 }

+ 82 - 86
engine/src/blender/com/jme3/scene/plugins/blender/modifiers/MirrorModifier.java

@@ -30,58 +30,54 @@ import com.jme3.scene.plugins.blender.objects.ObjectHelper;
  * 
  * @author Marcin Roguski (Kaelthas)
  */
-/*package*/ class MirrorModifier extends Modifier {
-	private static final Logger LOGGER = Logger.getLogger(MirrorModifier.class.getName());
-	
-	/** Parameters of the modifier. */
-	private Map<String, Object> modifierData = new HashMap<String, Object>();
-	
-	/**
-	 * This constructor reads mirror data from the modifier structure. The
-	 * stored data is a map of parameters for mirror modifier. No additional data
-	 * is loaded.
-	 * When the modifier is applied it is necessary to get the newly created node.
-	 * 
-	 * @param objectStructure
-	 *            the structure of the object
-	 * @param modifierStructure
-	 *            the structure of the modifier
-	 * @param blenderContext
-	 *            the blender context
-	 * @throws BlenderFileException
-	 *             this exception is thrown when the blender file is somehow
-	 *             corrupted
-	 */
-	public MirrorModifier(Structure modifierStructure, BlenderContext blenderContext) {
-		if(this.validate(modifierStructure, blenderContext)) {
-			modifierData.put("flag", modifierStructure.getFieldValue("flag"));
-			modifierData.put("tolerance", modifierStructure.getFieldValue("tolerance"));
-	        Pointer pMirrorOb = (Pointer) modifierStructure.getFieldValue("mirror_ob");
-	        if (pMirrorOb.isNotNull()) {
-	        	modifierData.put("mirrorob", pMirrorOb);
-	        }
-		}
-	}
-	
-	@Override
-	public Node apply(Node node, BlenderContext blenderContext) {
-		if(invalid) {
-			LOGGER.log(Level.WARNING, "Mirror modifier is invalid! Cannot be applied to: {0}", node.getName());
-			return node;
-		}
-		
+/* package */class MirrorModifier extends Modifier {
+    private static final Logger LOGGER       = Logger.getLogger(MirrorModifier.class.getName());
+
+    /** Parameters of the modifier. */
+    private Map<String, Object> modifierData = new HashMap<String, Object>();
+
+    /**
+     * This constructor reads mirror data from the modifier structure. The
+     * stored data is a map of parameters for mirror modifier. No additional data
+     * is loaded.
+     * When the modifier is applied it is necessary to get the newly created node.
+     * 
+     * @param objectStructure
+     *            the structure of the object
+     * @param modifierStructure
+     *            the structure of the modifier
+     * @param blenderContext
+     *            the blender context
+     * @throws BlenderFileException
+     *             this exception is thrown when the blender file is somehow
+     *             corrupted
+     */
+    public MirrorModifier(Structure modifierStructure, BlenderContext blenderContext) {
+        if (this.validate(modifierStructure, blenderContext)) {
+            modifierData.put("flag", modifierStructure.getFieldValue("flag"));
+            modifierData.put("tolerance", modifierStructure.getFieldValue("tolerance"));
+            Pointer pMirrorOb = (Pointer) modifierStructure.getFieldValue("mirror_ob");
+            if (pMirrorOb.isNotNull()) {
+                modifierData.put("mirrorob", pMirrorOb);
+            }
+        }
+    }
+
+    @Override
+    public Node apply(Node node, BlenderContext blenderContext) {
+        if (invalid) {
+            LOGGER.log(Level.WARNING, "Mirror modifier is invalid! Cannot be applied to: {0}", node.getName());
+            return node;
+        }
+
         int flag = ((Number) modifierData.get("flag")).intValue();
-        float[] mirrorFactor = new float[]{
-            (flag & 0x08) != 0 ? -1.0f : 1.0f,
-            (flag & 0x10) != 0 ? -1.0f : 1.0f,
-            (flag & 0x20) != 0 ? -1.0f : 1.0f
-        };
-        if(blenderContext.getBlenderKey().isFixUpAxis()) {
-        	float temp = mirrorFactor[1];
-        	mirrorFactor[1] = mirrorFactor[2];
-        	mirrorFactor[2] = temp;
+        float[] mirrorFactor = new float[] { (flag & 0x08) != 0 ? -1.0f : 1.0f, (flag & 0x10) != 0 ? -1.0f : 1.0f, (flag & 0x20) != 0 ? -1.0f : 1.0f };
+        if (blenderContext.getBlenderKey().isFixUpAxis()) {
+            float temp = mirrorFactor[1];
+            mirrorFactor[1] = mirrorFactor[2];
+            mirrorFactor[2] = temp;
         }
-        float[] center = new float[]{0.0f, 0.0f, 0.0f};
+        float[] center = new float[] { 0.0f, 0.0f, 0.0f };
         Pointer pObject = (Pointer) modifierData.get("mirrorob");
         if (pObject != null) {
             Structure objectStructure;
@@ -102,7 +98,7 @@ import com.jme3.scene.plugins.blender.objects.ObjectHelper;
         float tolerance = ((Number) modifierData.get("tolerance")).floatValue();
         boolean mirrorU = (flag & 0x01) != 0;
         boolean mirrorV = (flag & 0x02) != 0;
-//		boolean mirrorVGroup = (flag & 0x20) != 0;
+        // boolean mirrorVGroup = (flag & 0x20) != 0;
 
         Set<Integer> modifiedIndexes = new HashSet<Integer>();
         List<Geometry> geometriesToAdd = new ArrayList<Geometry>();
@@ -122,50 +118,50 @@ import com.jme3.scene.plugins.blender.objects.ObjectHelper;
                         FloatBuffer cloneNormals = clone.getFloatBuffer(Type.Normal);
                         FloatBuffer cloneBindPoseNormals = clone.getFloatBuffer(Type.BindPoseNormal);
                         Buffer cloneIndexes = clone.getBuffer(Type.Index).getData();
-                        
-						for (int i = 0; i < cloneIndexes.limit(); ++i) {
-                        	int index = cloneIndexes instanceof ShortBuffer ?  ((ShortBuffer)cloneIndexes).get(i) : ((IntBuffer)cloneIndexes).get(i);
-                        	if(!modifiedIndexes.contains((int)index)) {
-                        		modifiedIndexes.add((int)index);
-                        		int valueIndex = index * 3 + mirrorIndex;
-                            	
-                            	float value = clonePosition.get(valueIndex);
+
+                        for (int i = 0; i < cloneIndexes.limit(); ++i) {
+                            int index = cloneIndexes instanceof ShortBuffer ? ((ShortBuffer) cloneIndexes).get(i) : ((IntBuffer) cloneIndexes).get(i);
+                            if (!modifiedIndexes.contains((int) index)) {
+                                modifiedIndexes.add((int) index);
+                                int valueIndex = index * 3 + mirrorIndex;
+
+                                float value = clonePosition.get(valueIndex);
                                 float d = center[mirrorIndex] - value;
 
                                 if (Math.abs(d) <= tolerance) {
                                     clonePosition.put(valueIndex, center[mirrorIndex]);
-                                    if(cloneBindPosePosition != null) {
-                                    	cloneBindPosePosition.put(valueIndex, center[mirrorIndex]);
+                                    if (cloneBindPosePosition != null) {
+                                        cloneBindPosePosition.put(valueIndex, center[mirrorIndex]);
                                     }
                                     position.put(valueIndex, center[mirrorIndex]);
-                                    if(bindPosePosition != null) {
-                                    	bindPosePosition.put(valueIndex, center[mirrorIndex]);
+                                    if (bindPosePosition != null) {
+                                        bindPosePosition.put(valueIndex, center[mirrorIndex]);
                                     }
                                 } else {
                                     clonePosition.put(valueIndex, value + 2.0f * d);
-                                    if(cloneBindPosePosition != null) {
-                                    	cloneBindPosePosition.put(valueIndex, value + 2.0f * d);
+                                    if (cloneBindPosePosition != null) {
+                                        cloneBindPosePosition.put(valueIndex, value + 2.0f * d);
                                     }
                                 }
                                 cloneNormals.put(valueIndex, -cloneNormals.get(valueIndex));
-                                if(cloneBindPoseNormals != null) {
-                                	cloneBindPoseNormals.put(valueIndex, -cloneNormals.get(valueIndex));
+                                if (cloneBindPoseNormals != null) {
+                                    cloneBindPoseNormals.put(valueIndex, -cloneNormals.get(valueIndex));
                                 }
-                        	}
+                            }
                         }
                         modifiedIndexes.clear();
-                        
-                        //flipping index order
-						for (int i = 0; i < cloneIndexes.limit(); i += 3) {
-                        	if(cloneIndexes instanceof ShortBuffer) {
-                        		short index = ((ShortBuffer)cloneIndexes).get(i + 2);
-                        		((ShortBuffer)cloneIndexes).put(i + 2, ((ShortBuffer)cloneIndexes).get(i + 1));
-                        		((ShortBuffer)cloneIndexes).put(i + 1, index);
-                        	} else {
-                        		int index = ((IntBuffer)cloneIndexes).get(i + 2);
-                        		((IntBuffer)cloneIndexes).put(i + 2, ((IntBuffer)cloneIndexes).get(i + 1));
-                        		((IntBuffer)cloneIndexes).put(i + 1, index);
-                        	}
+
+                        // flipping index order
+                        for (int i = 0; i < cloneIndexes.limit(); i += 3) {
+                            if (cloneIndexes instanceof ShortBuffer) {
+                                short index = ((ShortBuffer) cloneIndexes).get(i + 2);
+                                ((ShortBuffer) cloneIndexes).put(i + 2, ((ShortBuffer) cloneIndexes).get(i + 1));
+                                ((ShortBuffer) cloneIndexes).put(i + 1, index);
+                            } else {
+                                int index = ((IntBuffer) cloneIndexes).get(i + 2);
+                                ((IntBuffer) cloneIndexes).put(i + 2, ((IntBuffer) cloneIndexes).get(i + 1));
+                                ((IntBuffer) cloneIndexes).put(i + 1, index);
+                            }
                         }
 
                         if (mirrorU && clone.getBuffer(Type.TexCoord) != null) {
@@ -195,10 +191,10 @@ import com.jme3.scene.plugins.blender.objects.ObjectHelper;
             }
         }
         return node;
-	}
-	
-	@Override
-	public String getType() {
-		return Modifier.MIRROR_MODIFIER_DATA;
-	}
+    }
+
+    @Override
+    public String getType() {
+        return Modifier.MIRROR_MODIFIER_DATA;
+    }
 }

+ 59 - 59
engine/src/blender/com/jme3/scene/plugins/blender/modifiers/Modifier.java

@@ -14,65 +14,65 @@ import com.jme3.scene.plugins.blender.file.Structure;
  * @author Marcin Roguski (Kaelthas)
  */
 public abstract 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";
-	public static final String MIRROR_MODIFIER_DATA = "MirrorModifierData";
-	public static final String SUBSURF_MODIFIER_DATA = "SubsurfModifierData";
-	public static final String OBJECT_ANIMATION_MODIFIER_DATA = "ObjectAnimationModifierData";
+    public static final String ARRAY_MODIFIER_DATA            = "ArrayModifierData";
+    public static final String ARMATURE_MODIFIER_DATA         = "ArmatureModifierData";
+    public static final String PARTICLE_MODIFIER_DATA         = "ParticleSystemModifierData";
+    public static final String MIRROR_MODIFIER_DATA           = "MirrorModifierData";
+    public static final String SUBSURF_MODIFIER_DATA          = "SubsurfModifierData";
+    public static final String OBJECT_ANIMATION_MODIFIER_DATA = "ObjectAnimationModifierData";
 
-	/** This variable indicates if the modifier is invalid (<b>true</b>) or not (<b>false</b>). */
-	protected boolean invalid;
-	/** 
-	 * A variable that tells if the modifier causes modification. Some modifiers like ArmatureModifier might have no
-	 * Armature object attached and thus not really modifying the feature. In such cases it is good to know if it is
-	 * sense to add the modifier to the list of object's modifiers.
-	 */
-	protected boolean modifying = true;
-	
-	/**
-	 * This method applies the modifier to the given node.
-	 * 
-	 * @param node
-	 *            the node that will have modifier applied
-	 * @param blenderContext
-	 *            the blender context
-	 * @return the node with applied modifier
-	 */
-	public abstract Node apply(Node node, BlenderContext blenderContext);
+    /** This variable indicates if the modifier is invalid (<b>true</b>) or not (<b>false</b>). */
+    protected boolean          invalid;
+    /**
+     * A variable that tells if the modifier causes modification. Some modifiers like ArmatureModifier might have no
+     * Armature object attached and thus not really modifying the feature. In such cases it is good to know if it is
+     * sense to add the modifier to the list of object's modifiers.
+     */
+    protected boolean          modifying                      = true;
 
-	/**
-	 * This method returns blender's type of modifier.
-	 * 
-	 * @return blender's type of modifier
-	 */
-	public abstract String getType();
-	
-	/**
-	 * Determines if the modifier can be applied multiple times over one mesh.
-	 * At this moment only armature and object animation modifiers cannot be
-	 * applied multiple times.
-	 * 
-	 * @param modifierType
-	 *            the type name of the modifier
-	 * @return <b>true</b> if the modifier can be applied many times and
-	 *         <b>false</b> otherwise
-	 */
-	public static boolean canBeAppliedMultipleTimes(String modifierType) {
-		return !(ARMATURE_MODIFIER_DATA.equals(modifierType) || OBJECT_ANIMATION_MODIFIER_DATA.equals(modifierType));
-	}
-	
-	protected boolean validate(Structure modifierStructure, BlenderContext blenderContext) {
-		Structure modifierData = (Structure)modifierStructure.getFieldValue("modifier");
-		Pointer pError = (Pointer) modifierData.getFieldValue("error");
-		invalid = pError.isNotNull();
-		return !invalid;
-	}
-	
-	/**
-	 * @return <b>true</b> if the modifier causes feature's modification or <b>false</b> if not
-	 */
-	public boolean isModifying() {
-		return modifying;
-	}
+    /**
+     * This method applies the modifier to the given node.
+     * 
+     * @param node
+     *            the node that will have modifier applied
+     * @param blenderContext
+     *            the blender context
+     * @return the node with applied modifier
+     */
+    public abstract Node apply(Node node, BlenderContext blenderContext);
+
+    /**
+     * This method returns blender's type of modifier.
+     * 
+     * @return blender's type of modifier
+     */
+    public abstract String getType();
+
+    /**
+     * Determines if the modifier can be applied multiple times over one mesh.
+     * At this moment only armature and object animation modifiers cannot be
+     * applied multiple times.
+     * 
+     * @param modifierType
+     *            the type name of the modifier
+     * @return <b>true</b> if the modifier can be applied many times and
+     *         <b>false</b> otherwise
+     */
+    public static boolean canBeAppliedMultipleTimes(String modifierType) {
+        return !(ARMATURE_MODIFIER_DATA.equals(modifierType) || OBJECT_ANIMATION_MODIFIER_DATA.equals(modifierType));
+    }
+
+    protected boolean validate(Structure modifierStructure, BlenderContext blenderContext) {
+        Structure modifierData = (Structure) modifierStructure.getFieldValue("modifier");
+        Pointer pError = (Pointer) modifierData.getFieldValue("error");
+        invalid = pError.isNotNull();
+        return !invalid;
+    }
+
+    /**
+     * @return <b>true</b> if the modifier causes feature's modification or <b>false</b> if not
+     */
+    public boolean isModifying() {
+        return modifying;
+    }
 }

+ 132 - 132
engine/src/blender/com/jme3/scene/plugins/blender/modifiers/ModifierHelper.java

@@ -53,143 +53,143 @@ import java.util.logging.Logger;
  */
 public class ModifierHelper extends AbstractBlenderHelper {
 
-	private static final Logger	LOGGER	= Logger.getLogger(ModifierHelper.class.getName());
+    private static final Logger LOGGER = Logger.getLogger(ModifierHelper.class.getName());
 
-	/**
-	 * This constructor parses the given blender version and stores the result.
-	 * Some functionalities may differ in different blender versions.
-	 * 
-	 * @param blenderVersion
-	 *            the version read from the blend file
-	 * @param fixUpAxis
-	 *            a variable that indicates if the Y asxis is the UP axis or not
-	 */
-	public ModifierHelper(String blenderVersion, boolean fixUpAxis) {
-		super(blenderVersion, fixUpAxis);
-	}
+    /**
+     * This constructor parses the given blender version and stores the result.
+     * Some functionalities may differ in different blender versions.
+     * 
+     * @param blenderVersion
+     *            the version read from the blend file
+     * @param fixUpAxis
+     *            a variable that indicates if the Y asxis is the UP axis or not
+     */
+    public ModifierHelper(String blenderVersion, boolean fixUpAxis) {
+        super(blenderVersion, fixUpAxis);
+    }
 
-	/**
-	 * This method reads the given object's modifiers.
-	 * 
-	 * @param objectStructure
-	 *            the object structure
-	 * @param blenderContext
-	 *            the blender context
-	 * @throws BlenderFileException
-	 *             this exception is thrown when the blender file is somehow
-	 *             corrupted
-	 */
-	public Collection<Modifier> readModifiers(Structure objectStructure, BlenderContext blenderContext) throws BlenderFileException {
-		Set<String> alreadyReadModifiers = new HashSet<String>();
-		Collection<Modifier> result = new ArrayList<Modifier>();
-		Structure modifiersListBase = (Structure) objectStructure.getFieldValue("modifiers");
-		List<Structure> modifiers = modifiersListBase.evaluateListBase(blenderContext);
-		for (Structure modifierStructure : modifiers) {
-			String modifierType = modifierStructure.getType();
-			if(!Modifier.canBeAppliedMultipleTimes(modifierType) && alreadyReadModifiers.contains(modifierType)) {
-				LOGGER.log(Level.WARNING, "Modifier {0} can only be applied once to object: {1}", new Object[] { modifierType, objectStructure.getName() });
-			} else {
-				Modifier modifier = null;
-				if (Modifier.ARRAY_MODIFIER_DATA.equals(modifierStructure.getType())) {
-					modifier = new ArrayModifier(modifierStructure, blenderContext);
-				} else if (Modifier.MIRROR_MODIFIER_DATA.equals(modifierStructure.getType())) {
-					modifier = new MirrorModifier(modifierStructure, blenderContext);
-				} else if (Modifier.ARMATURE_MODIFIER_DATA.equals(modifierStructure.getType())) {
-					modifier = new ArmatureModifier(objectStructure, modifierStructure, blenderContext);
-				} else if (Modifier.PARTICLE_MODIFIER_DATA.equals(modifierStructure.getType())) {
-					modifier = new ParticlesModifier(modifierStructure, blenderContext);
-				}
+    /**
+     * This method reads the given object's modifiers.
+     * 
+     * @param objectStructure
+     *            the object structure
+     * @param blenderContext
+     *            the blender context
+     * @throws BlenderFileException
+     *             this exception is thrown when the blender file is somehow
+     *             corrupted
+     */
+    public Collection<Modifier> readModifiers(Structure objectStructure, BlenderContext blenderContext) throws BlenderFileException {
+        Set<String> alreadyReadModifiers = new HashSet<String>();
+        Collection<Modifier> result = new ArrayList<Modifier>();
+        Structure modifiersListBase = (Structure) objectStructure.getFieldValue("modifiers");
+        List<Structure> modifiers = modifiersListBase.evaluateListBase(blenderContext);
+        for (Structure modifierStructure : modifiers) {
+            String modifierType = modifierStructure.getType();
+            if (!Modifier.canBeAppliedMultipleTimes(modifierType) && alreadyReadModifiers.contains(modifierType)) {
+                LOGGER.log(Level.WARNING, "Modifier {0} can only be applied once to object: {1}", new Object[] { modifierType, objectStructure.getName() });
+            } else {
+                Modifier modifier = null;
+                if (Modifier.ARRAY_MODIFIER_DATA.equals(modifierStructure.getType())) {
+                    modifier = new ArrayModifier(modifierStructure, blenderContext);
+                } else if (Modifier.MIRROR_MODIFIER_DATA.equals(modifierStructure.getType())) {
+                    modifier = new MirrorModifier(modifierStructure, blenderContext);
+                } else if (Modifier.ARMATURE_MODIFIER_DATA.equals(modifierStructure.getType())) {
+                    modifier = new ArmatureModifier(objectStructure, modifierStructure, blenderContext);
+                } else if (Modifier.PARTICLE_MODIFIER_DATA.equals(modifierStructure.getType())) {
+                    modifier = new ParticlesModifier(modifierStructure, blenderContext);
+                }
 
-				if (modifier != null) {
-					if(modifier.isModifying()) {
-						result.add(modifier);
-						blenderContext.addModifier(objectStructure.getOldMemoryAddress(), modifier);
-						alreadyReadModifiers.add(modifierType);
-					} else {
-						LOGGER.log(Level.WARNING, "The modifier {0} will cause no changes in the model. It will be ignored!", modifierStructure.getName());
-					}
-				} else {
-					LOGGER.log(Level.WARNING, "Unsupported modifier type: {0}", modifierStructure.getType());
-				}
-			}
-		}
+                if (modifier != null) {
+                    if (modifier.isModifying()) {
+                        result.add(modifier);
+                        blenderContext.addModifier(objectStructure.getOldMemoryAddress(), modifier);
+                        alreadyReadModifiers.add(modifierType);
+                    } else {
+                        LOGGER.log(Level.WARNING, "The modifier {0} will cause no changes in the model. It will be ignored!", modifierStructure.getName());
+                    }
+                } else {
+                    LOGGER.log(Level.WARNING, "Unsupported modifier type: {0}", modifierStructure.getType());
+                }
+            }
+        }
 
-		// at the end read object's animation modifier (object animation is
-		// either described by action or by ipo of the object)
-		Modifier modifier;
-		if (blenderVersion <= 249) {
-			modifier = this.readAnimationModifier249(objectStructure, blenderContext);
-		} else {
-			modifier = this.readAnimationModifier250(objectStructure, blenderContext);
-		}
-		if (modifier != null) {
-			result.add(modifier);
-		}
-		return result;
-	}
+        // at the end read object's animation modifier (object animation is
+        // either described by action or by ipo of the object)
+        Modifier modifier;
+        if (blenderVersion <= 249) {
+            modifier = this.readAnimationModifier249(objectStructure, blenderContext);
+        } else {
+            modifier = this.readAnimationModifier250(objectStructure, blenderContext);
+        }
+        if (modifier != null) {
+            result.add(modifier);
+        }
+        return result;
+    }
 
-	@Override
-	public boolean shouldBeLoaded(Structure structure, BlenderContext blenderContext) {
-		return true;
-	}
+    @Override
+    public boolean shouldBeLoaded(Structure structure, BlenderContext blenderContext) {
+        return true;
+    }
 
-	/**
-	 * This method reads the object's animation modifier for blender version
-	 * 2.49 and lower.
-	 * 
-	 * @param objectStructure
-	 *            the object's structure
-	 * @param blenderContext
-	 *            the blender context
-	 * @return loaded modifier
-	 * @throws BlenderFileException
-	 *             this exception is thrown when the blender file is somehow
-	 *             corrupted
-	 */
-	private Modifier readAnimationModifier249(Structure objectStructure, BlenderContext blenderContext) throws BlenderFileException {
-		Modifier result = null;
-		Pointer pIpo = (Pointer) objectStructure.getFieldValue("ipo");
-		if (pIpo.isNotNull()) {
-			IpoHelper ipoHelper = blenderContext.getHelper(IpoHelper.class);
-			Structure ipoStructure = pIpo.fetchData(blenderContext.getInputStream()).get(0);
-			Ipo ipo = ipoHelper.fromIpoStructure(ipoStructure, blenderContext);
-			if(ipo != null) {
-				result = new ObjectAnimationModifier(ipo, objectStructure.getName(), objectStructure.getOldMemoryAddress(), blenderContext);
-				blenderContext.addModifier(objectStructure.getOldMemoryAddress(), result);
-			}
-		}
-		return result;
-	}
+    /**
+     * This method reads the object's animation modifier for blender version
+     * 2.49 and lower.
+     * 
+     * @param objectStructure
+     *            the object's structure
+     * @param blenderContext
+     *            the blender context
+     * @return loaded modifier
+     * @throws BlenderFileException
+     *             this exception is thrown when the blender file is somehow
+     *             corrupted
+     */
+    private Modifier readAnimationModifier249(Structure objectStructure, BlenderContext blenderContext) throws BlenderFileException {
+        Modifier result = null;
+        Pointer pIpo = (Pointer) objectStructure.getFieldValue("ipo");
+        if (pIpo.isNotNull()) {
+            IpoHelper ipoHelper = blenderContext.getHelper(IpoHelper.class);
+            Structure ipoStructure = pIpo.fetchData(blenderContext.getInputStream()).get(0);
+            Ipo ipo = ipoHelper.fromIpoStructure(ipoStructure, blenderContext);
+            if (ipo != null) {
+                result = new ObjectAnimationModifier(ipo, objectStructure.getName(), objectStructure.getOldMemoryAddress(), blenderContext);
+                blenderContext.addModifier(objectStructure.getOldMemoryAddress(), result);
+            }
+        }
+        return result;
+    }
 
-	/**
-	 * This method reads the object's animation modifier for blender version
-	 * 2.50 and higher.
-	 * 
-	 * @param objectStructure
-	 *            the object's structure
-	 * @param blenderContext
-	 *            the blender context
-	 * @return loaded modifier
-	 * @throws BlenderFileException
-	 *             this exception is thrown when the blender file is somehow
-	 *             corrupted
-	 */
-	private Modifier readAnimationModifier250(Structure objectStructure, BlenderContext blenderContext) throws BlenderFileException {
-		Modifier result = null;
-		Pointer pAnimData = (Pointer) objectStructure.getFieldValue("adt");
-		if (pAnimData.isNotNull()) {
-			Structure animData = pAnimData.fetchData(blenderContext.getInputStream()).get(0);
-			Pointer pAction = (Pointer) animData.getFieldValue("action");
-			if (pAction.isNotNull()) {
-				Structure actionStructure = pAction.fetchData(blenderContext.getInputStream()).get(0);
-				IpoHelper ipoHelper = blenderContext.getHelper(IpoHelper.class);
-				Ipo ipo = ipoHelper.fromAction(actionStructure, blenderContext);
-				if(ipo != null) {//ipo can be null if it has no curves applied, ommit such modifier then
-					result = new ObjectAnimationModifier(ipo, actionStructure.getName(), objectStructure.getOldMemoryAddress(), blenderContext);
-					blenderContext.addModifier(objectStructure.getOldMemoryAddress(), result);
-				}
-			}
-		}
-		return result;
-	}
+    /**
+     * This method reads the object's animation modifier for blender version
+     * 2.50 and higher.
+     * 
+     * @param objectStructure
+     *            the object's structure
+     * @param blenderContext
+     *            the blender context
+     * @return loaded modifier
+     * @throws BlenderFileException
+     *             this exception is thrown when the blender file is somehow
+     *             corrupted
+     */
+    private Modifier readAnimationModifier250(Structure objectStructure, BlenderContext blenderContext) throws BlenderFileException {
+        Modifier result = null;
+        Pointer pAnimData = (Pointer) objectStructure.getFieldValue("adt");
+        if (pAnimData.isNotNull()) {
+            Structure animData = pAnimData.fetchData(blenderContext.getInputStream()).get(0);
+            Pointer pAction = (Pointer) animData.getFieldValue("action");
+            if (pAction.isNotNull()) {
+                Structure actionStructure = pAction.fetchData(blenderContext.getInputStream()).get(0);
+                IpoHelper ipoHelper = blenderContext.getHelper(IpoHelper.class);
+                Ipo ipo = ipoHelper.fromAction(actionStructure, blenderContext);
+                if (ipo != null) {// ipo can be null if it has no curves applied, ommit such modifier then
+                    result = new ObjectAnimationModifier(ipo, actionStructure.getName(), objectStructure.getOldMemoryAddress(), blenderContext);
+                    blenderContext.addModifier(objectStructure.getOldMemoryAddress(), result);
+                }
+            }
+        }
+        return result;
+    }
 }

+ 62 - 62
engine/src/blender/com/jme3/scene/plugins/blender/modifiers/ObjectAnimationModifier.java

@@ -22,73 +22,73 @@ import com.jme3.scene.plugins.ogre.AnimData;
  * @author Marcin Roguski (Kaelthas)
  */
 /* package */class ObjectAnimationModifier extends Modifier {
-	private static final Logger	LOGGER	= Logger.getLogger(ObjectAnimationModifier.class.getName());
+    private static final Logger LOGGER = Logger.getLogger(ObjectAnimationModifier.class.getName());
 
-	/** Loaded animation data. */
-	private AnimData			animData;
+    /** Loaded animation data. */
+    private AnimData            animData;
 
-	/**
-	 * This constructor reads animation of the object itself (without bones) and
-	 * stores it as an ArmatureModifierData modifier. The animation is returned
-	 * as a modifier. It should be later applied regardless other modifiers. The
-	 * reason for this is that object may not have modifiers added but it's
-	 * animation should be working. The stored modifier is an anim data and
-	 * additional data is given object's OMA.
-	 * 
-	 * @param ipo
-	 *            the object's interpolation curves
-	 * @param objectAnimationName
-	 *            the name of object's animation
-	 * @param objectOMA
-	 *            the OMA of the object
-	 * @param blenderContext
-	 *            the blender context
-	 * @throws BlenderFileException
-	 *             this exception is thrown when the blender file is somehow
-	 *             corrupted
-	 */
-	public ObjectAnimationModifier(Ipo ipo, String objectAnimationName, Long objectOMA, BlenderContext blenderContext) throws BlenderFileException {
-		int fps = blenderContext.getBlenderKey().getFps();
-		
-		Spatial object = (Spatial) blenderContext.getLoadedFeature(objectOMA, LoadedFeatureDataType.LOADED_FEATURE);
-		// calculating track
-		SpatialTrack track = (SpatialTrack) ipo.calculateTrack(-1, object.getLocalRotation(), 0, ipo.getLastFrame(), fps, true);
+    /**
+     * This constructor reads animation of the object itself (without bones) and
+     * stores it as an ArmatureModifierData modifier. The animation is returned
+     * as a modifier. It should be later applied regardless other modifiers. The
+     * reason for this is that object may not have modifiers added but it's
+     * animation should be working. The stored modifier is an anim data and
+     * additional data is given object's OMA.
+     * 
+     * @param ipo
+     *            the object's interpolation curves
+     * @param objectAnimationName
+     *            the name of object's animation
+     * @param objectOMA
+     *            the OMA of the object
+     * @param blenderContext
+     *            the blender context
+     * @throws BlenderFileException
+     *             this exception is thrown when the blender file is somehow
+     *             corrupted
+     */
+    public ObjectAnimationModifier(Ipo ipo, String objectAnimationName, Long objectOMA, BlenderContext blenderContext) throws BlenderFileException {
+        int fps = blenderContext.getBlenderKey().getFps();
 
-		Animation animation = new Animation(objectAnimationName, ipo.getLastFrame() / (float)fps);
-		animation.setTracks(new SpatialTrack[] { track });
-		ArrayList<Animation> animations = new ArrayList<Animation>(1);
-		animations.add(animation);
+        Spatial object = (Spatial) blenderContext.getLoadedFeature(objectOMA, LoadedFeatureDataType.LOADED_FEATURE);
+        // calculating track
+        SpatialTrack track = (SpatialTrack) ipo.calculateTrack(-1, object.getLocalRotation(), 0, ipo.getLastFrame(), fps, true);
 
-		animData = new AnimData(null, animations);
-		blenderContext.setAnimData(objectOMA, animData);
-	}
+        Animation animation = new Animation(objectAnimationName, ipo.getLastFrame() / (float) fps);
+        animation.setTracks(new SpatialTrack[] { track });
+        ArrayList<Animation> animations = new ArrayList<Animation>(1);
+        animations.add(animation);
 
-	@Override
-	public Node apply(Node node, BlenderContext blenderContext) {
-		if (invalid) {
-			LOGGER.log(Level.WARNING, "Armature modifier is invalid! Cannot be applied to: {0}", node.getName());
-		}// if invalid, animData will be null
-		if (animData != null) {
-			// INFO: constraints for this modifier are applied in the
-			// ObjectHelper when the whole object is loaded
-			ArrayList<Animation> animList = animData.anims;
-			if (animList != null && animList.size() > 0) {
-				HashMap<String, Animation> anims = new HashMap<String, Animation>();
-				for (int i = 0; i < animList.size(); ++i) {
-					Animation animation = animList.get(i);
-					anims.put(animation.getName(), animation);
-				}
+        animData = new AnimData(null, animations);
+        blenderContext.setAnimData(objectOMA, animData);
+    }
 
-				AnimControl control = new AnimControl(null);
-				control.setAnimations(anims);
-				node.addControl(control);
-			}
-		}
-		return node;
-	}
+    @Override
+    public Node apply(Node node, BlenderContext blenderContext) {
+        if (invalid) {
+            LOGGER.log(Level.WARNING, "Armature modifier is invalid! Cannot be applied to: {0}", node.getName());
+        }// if invalid, animData will be null
+        if (animData != null) {
+            // INFO: constraints for this modifier are applied in the
+            // ObjectHelper when the whole object is loaded
+            ArrayList<Animation> animList = animData.anims;
+            if (animList != null && animList.size() > 0) {
+                HashMap<String, Animation> anims = new HashMap<String, Animation>();
+                for (int i = 0; i < animList.size(); ++i) {
+                    Animation animation = animList.get(i);
+                    anims.put(animation.getName(), animation);
+                }
 
-	@Override
-	public String getType() {
-		return Modifier.OBJECT_ANIMATION_MODIFIER_DATA;
-	}
+                AnimControl control = new AnimControl(null);
+                control.setAnimations(anims);
+                node.addControl(control);
+            }
+        }
+        return node;
+    }
+
+    @Override
+    public String getType() {
+        return Modifier.OBJECT_ANIMATION_MODIFIER_DATA;
+    }
 }

+ 67 - 68
engine/src/blender/com/jme3/scene/plugins/blender/modifiers/ParticlesModifier.java

@@ -25,77 +25,76 @@ import java.util.logging.Logger;
  * @author Marcin Roguski (Kaelthas)
  */
 /* package */class ParticlesModifier extends Modifier {
-	private static final Logger LOGGER = Logger.getLogger(MirrorModifier.class.getName());
-	
-	/** Loaded particles emitter. */
-	private ParticleEmitter particleEmitter;
-	
-	/**
-	 * This constructor reads the particles system structure and stores it in
-	 * order to apply it later to the node.
-	 * 
-	 * @param modifierStructure
-	 *            the structure of the modifier
-	 * @param blenderContext
-	 *            the blender context
-	 * @throws BlenderFileException
-	 *             an exception is throw wneh there are problems with the
-	 *             blender file
-	 */
-	public ParticlesModifier(Structure modifierStructure, BlenderContext blenderContext) throws BlenderFileException {
-		if(this.validate(modifierStructure, blenderContext)) {
-			Pointer pParticleSystem = (Pointer) modifierStructure.getFieldValue("psys");
-			if (pParticleSystem.isNotNull()) {
-				ParticlesHelper particlesHelper = blenderContext.getHelper(ParticlesHelper.class);
-				Structure particleSystem = pParticleSystem.fetchData(blenderContext.getInputStream()).get(0);
-				particleEmitter = particlesHelper.toParticleEmitter(particleSystem, blenderContext);
-			}
-		}
-	}
+    private static final Logger LOGGER = Logger.getLogger(MirrorModifier.class.getName());
 
-	@Override
-	public Node apply(Node node, BlenderContext blenderContext) {
-		if(invalid) {
-			LOGGER.log(Level.WARNING, "Particles modifier is invalid! Cannot be applied to: {0}", node.getName());
-			return node;
-		}
-		
-		MaterialHelper materialHelper = blenderContext.getHelper(MaterialHelper.class);
-		ParticleEmitter emitter = particleEmitter.clone();
+    /** Loaded particles emitter. */
+    private ParticleEmitter     particleEmitter;
 
-		// 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));
+    /**
+     * This constructor reads the particles system structure and stores it in
+     * order to apply it later to the node.
+     * 
+     * @param modifierStructure
+     *            the structure of the modifier
+     * @param blenderContext
+     *            the blender context
+     * @throws BlenderFileException
+     *             an exception is throw wneh there are problems with the
+     *             blender file
+     */
+    public ParticlesModifier(Structure modifierStructure, BlenderContext blenderContext) throws BlenderFileException {
+        if (this.validate(modifierStructure, blenderContext)) {
+            Pointer pParticleSystem = (Pointer) modifierStructure.getFieldValue("psys");
+            if (pParticleSystem.isNotNull()) {
+                ParticlesHelper particlesHelper = blenderContext.getHelper(ParticlesHelper.class);
+                Structure particleSystem = pParticleSystem.fetchData(blenderContext.getInputStream()).get(0);
+                particleEmitter = particlesHelper.toParticleEmitter(particleSystem, blenderContext);
+            }
+        }
+    }
 
-		// 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, blenderContext);
-					emitter.setMaterial(material);// TODO: divide into several pieces
-				}
-			}
-		}
-		if (meshes.size() > 0 && emitterShape instanceof EmitterMeshVertexShape) {
-			((EmitterMeshVertexShape) emitterShape).setMeshes(meshes);
-		}
+    @Override
+    public Node apply(Node node, BlenderContext blenderContext) {
+        if (invalid) {
+            LOGGER.log(Level.WARNING, "Particles modifier is invalid! Cannot be applied to: {0}", node.getName());
+            return node;
+        }
 
-		node.attachChild(emitter);
-		return node;
-	}
+        MaterialHelper materialHelper = blenderContext.getHelper(MaterialHelper.class);
+        ParticleEmitter emitter = particleEmitter.clone();
 
-	@Override
-	public String getType() {
-		return Modifier.PARTICLE_MODIFIER_DATA;
-	}
+        // 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, blenderContext);
+                    emitter.setMaterial(material);// TODO: divide into several pieces
+                }
+            }
+        }
+        if (meshes.size() > 0 && emitterShape instanceof EmitterMeshVertexShape) {
+            ((EmitterMeshVertexShape) emitterShape).setMeshes(meshes);
+        }
+
+        node.attachChild(emitter);
+        return node;
+    }
+
+    @Override
+    public String getType() {
+        return Modifier.PARTICLE_MODIFIER_DATA;
+    }
 }

+ 306 - 307
engine/src/blender/com/jme3/scene/plugins/blender/objects/ObjectHelper.java

@@ -68,317 +68,316 @@ import com.jme3.scene.plugins.blender.modifiers.ModifierHelper;
  * @author Marcin Roguski (Kaelthas)
  */
 public class ObjectHelper extends AbstractBlenderHelper {
-	private static final Logger			LOGGER		= Logger.getLogger(ObjectHelper.class.getName());
-
-	protected static final int		OBJECT_TYPE_EMPTY			= 0;
-	protected static final int		OBJECT_TYPE_MESH			= 1;
-	protected static final int		OBJECT_TYPE_CURVE			= 2;
-	protected static final int		OBJECT_TYPE_SURF			= 3;
-	protected static final int		OBJECT_TYPE_TEXT			= 4;
-	protected static final int		OBJECT_TYPE_METABALL		= 5;
-	protected static final int		OBJECT_TYPE_LAMP			= 10;
-	protected static final int		OBJECT_TYPE_CAMERA			= 11;
-	protected static final int		OBJECT_TYPE_WAVE			= 21;
-	protected static final int		OBJECT_TYPE_LATTICE			= 22;
-	protected static final int		OBJECT_TYPE_ARMATURE		= 25;
-	
-	/**
-	 * This constructor parses the given blender version and stores the result. Some functionalities may differ in
-	 * different blender versions.
-	 * @param blenderVersion
-	 *        the version read from the blend file
-	 * @param fixUpAxis
-     *        a variable that indicates if the Y asxis is the UP axis or not
-	 */
-	public ObjectHelper(String blenderVersion, boolean fixUpAxis) {
-		super(blenderVersion, fixUpAxis);
-	}
-
-	/**
-	 * This method reads the given structure and createn an object that represents the data.
-	 * @param objectStructure
-	 *            the object's structure
-	 * @param blenderContext
-	 *            the blender context
-	 * @return blener's object representation
-	 * @throws BlenderFileException
-	 *             an exception is thrown when the given data is inapropriate
-	 */
-	public Object toObject(Structure objectStructure, BlenderContext blenderContext) throws BlenderFileException {
-		Object loadedResult = blenderContext.getLoadedFeature(objectStructure.getOldMemoryAddress(), LoadedFeatureDataType.LOADED_FEATURE);
-		if(loadedResult != null) {
-			return loadedResult;
-		}
-
-		blenderContext.pushParent(objectStructure);
-
-		//get object data
-		int type = ((Number)objectStructure.getFieldValue("type")).intValue();
-		String name = objectStructure.getName();
-		LOGGER.log(Level.FINE, "Loading obejct: {0}", name);
-
-		int restrictflag = ((Number)objectStructure.getFieldValue("restrictflag")).intValue();
-		boolean visible = (restrictflag & 0x01) != 0;
-		Node result = null;
-
-		Pointer pParent = (Pointer)objectStructure.getFieldValue("parent");
-		Object parent = blenderContext.getLoadedFeature(pParent.getOldMemoryAddress(), LoadedFeatureDataType.LOADED_FEATURE);
-		if(parent == null && pParent.isNotNull()) {
-			Structure parentStructure = pParent.fetchData(blenderContext.getInputStream()).get(0);
-			parent = this.toObject(parentStructure, blenderContext);
-		}
-		
-		Transform t = this.getTransformation(objectStructure, blenderContext);
-		
-		try {
-			switch(type) {
-				case OBJECT_TYPE_EMPTY:
-					LOGGER.log(Level.FINE, "Importing empty.");
-					Node empty = new Node(name);
-					empty.setLocalTransform(t);
-					if(parent instanceof Node) {
-						((Node) parent).attachChild(empty);
-					}
-					result = empty;
-					break;
-				case OBJECT_TYPE_MESH:
-					LOGGER.log(Level.FINE, "Importing mesh.");
-					Node node = new Node(name);
-					node.setCullHint(visible ? CullHint.Always : CullHint.Inherit);
-
-					//reading mesh
-					MeshHelper meshHelper = blenderContext.getHelper(MeshHelper.class);
-					Pointer pMesh = (Pointer)objectStructure.getFieldValue("data");
-					List<Structure> meshesArray = pMesh.fetchData(blenderContext.getInputStream());
-					List<Geometry> geometries = meshHelper.toMesh(meshesArray.get(0), blenderContext);
-					if (geometries != null){
-                        for(Geometry geometry : geometries) {
+    private static final Logger LOGGER               = Logger.getLogger(ObjectHelper.class.getName());
+
+    protected static final int  OBJECT_TYPE_EMPTY    = 0;
+    protected static final int  OBJECT_TYPE_MESH     = 1;
+    protected static final int  OBJECT_TYPE_CURVE    = 2;
+    protected static final int  OBJECT_TYPE_SURF     = 3;
+    protected static final int  OBJECT_TYPE_TEXT     = 4;
+    protected static final int  OBJECT_TYPE_METABALL = 5;
+    protected static final int  OBJECT_TYPE_LAMP     = 10;
+    protected static final int  OBJECT_TYPE_CAMERA   = 11;
+    protected static final int  OBJECT_TYPE_WAVE     = 21;
+    protected static final int  OBJECT_TYPE_LATTICE  = 22;
+    protected static final int  OBJECT_TYPE_ARMATURE = 25;
+
+    /**
+     * This constructor parses the given blender version and stores the result. Some functionalities may differ in
+     * different blender versions.
+     * @param blenderVersion
+     *            the version read from the blend file
+     * @param fixUpAxis
+     *            a variable that indicates if the Y asxis is the UP axis or not
+     */
+    public ObjectHelper(String blenderVersion, boolean fixUpAxis) {
+        super(blenderVersion, fixUpAxis);
+    }
+
+    /**
+     * This method reads the given structure and createn an object that represents the data.
+     * @param objectStructure
+     *            the object's structure
+     * @param blenderContext
+     *            the blender context
+     * @return blener's object representation
+     * @throws BlenderFileException
+     *             an exception is thrown when the given data is inapropriate
+     */
+    public Object toObject(Structure objectStructure, BlenderContext blenderContext) throws BlenderFileException {
+        Object loadedResult = blenderContext.getLoadedFeature(objectStructure.getOldMemoryAddress(), LoadedFeatureDataType.LOADED_FEATURE);
+        if (loadedResult != null) {
+            return loadedResult;
+        }
+
+        blenderContext.pushParent(objectStructure);
+
+        // get object data
+        int type = ((Number) objectStructure.getFieldValue("type")).intValue();
+        String name = objectStructure.getName();
+        LOGGER.log(Level.FINE, "Loading obejct: {0}", name);
+
+        int restrictflag = ((Number) objectStructure.getFieldValue("restrictflag")).intValue();
+        boolean visible = (restrictflag & 0x01) != 0;
+        Node result = null;
+
+        Pointer pParent = (Pointer) objectStructure.getFieldValue("parent");
+        Object parent = blenderContext.getLoadedFeature(pParent.getOldMemoryAddress(), LoadedFeatureDataType.LOADED_FEATURE);
+        if (parent == null && pParent.isNotNull()) {
+            Structure parentStructure = pParent.fetchData(blenderContext.getInputStream()).get(0);
+            parent = this.toObject(parentStructure, blenderContext);
+        }
+
+        Transform t = this.getTransformation(objectStructure, blenderContext);
+
+        try {
+            switch (type) {
+                case OBJECT_TYPE_EMPTY:
+                    LOGGER.log(Level.FINE, "Importing empty.");
+                    Node empty = new Node(name);
+                    empty.setLocalTransform(t);
+                    if (parent instanceof Node) {
+                        ((Node) parent).attachChild(empty);
+                    }
+                    result = empty;
+                    break;
+                case OBJECT_TYPE_MESH:
+                    LOGGER.log(Level.FINE, "Importing mesh.");
+                    Node node = new Node(name);
+                    node.setCullHint(visible ? CullHint.Always : CullHint.Inherit);
+
+                    // reading mesh
+                    MeshHelper meshHelper = blenderContext.getHelper(MeshHelper.class);
+                    Pointer pMesh = (Pointer) objectStructure.getFieldValue("data");
+                    List<Structure> meshesArray = pMesh.fetchData(blenderContext.getInputStream());
+                    List<Geometry> geometries = meshHelper.toMesh(meshesArray.get(0), blenderContext);
+                    if (geometries != null) {
+                        for (Geometry geometry : geometries) {
                             node.attachChild(geometry);
                         }
                     }
-					node.setLocalTransform(t);
-
-					//setting the parent
-					if(parent instanceof Node) {
-						((Node)parent).attachChild(node);
-					}
-					result = node;
-					break;
-				case OBJECT_TYPE_SURF:
-				case OBJECT_TYPE_CURVE:
-					LOGGER.log(Level.FINE, "Importing curve/nurb.");
-					Pointer pCurve = (Pointer)objectStructure.getFieldValue("data");
-					if(pCurve.isNotNull()) {
-						CurvesHelper curvesHelper = blenderContext.getHelper(CurvesHelper.class);
-						Structure curveData = pCurve.fetchData(blenderContext.getInputStream()).get(0);
-						List<Geometry> curves = curvesHelper.toCurve(curveData, blenderContext);
-						result = new Node(name);
-						for(Geometry curve : curves) {
-							((Node)result).attachChild(curve);
-						}
-						((Node)result).setLocalTransform(t);
-					}
-					break;
-				case OBJECT_TYPE_LAMP:
-					LOGGER.log(Level.FINE, "Importing lamp.");
-					Pointer pLamp = (Pointer)objectStructure.getFieldValue("data");
-					if(pLamp.isNotNull()) {
-						LightHelper lightHelper = blenderContext.getHelper(LightHelper.class);
-						List<Structure> lampsArray = pLamp.fetchData(blenderContext.getInputStream());
-						LightNode light = lightHelper.toLight(lampsArray.get(0), blenderContext);
-						if(light!=null) {
-							light.setName(name);
-							light.setLocalTransform(t);
-						}
-						result = light;
-					}
-					break;
-				case OBJECT_TYPE_CAMERA:
-					Pointer pCamera = (Pointer)objectStructure.getFieldValue("data");
-					if(pCamera.isNotNull()) {
-						CameraHelper cameraHelper = blenderContext.getHelper(CameraHelper.class);
-						List<Structure> camerasArray = pCamera.fetchData(blenderContext.getInputStream());
-						CameraNode camera = cameraHelper.toCamera(camerasArray.get(0), blenderContext);
-						camera.setName(name);
-						camera.setLocalTransform(t);
-						result = camera;
-					}
-					break;
-				case OBJECT_TYPE_ARMATURE:
-					//need to create an empty node to properly create parent-children relationships between nodes
-					Node armature = new Node(name);
-					armature.setLocalTransform(t);
-					armature.setUserData(ArmatureHelper.ARMETURE_NODE_MARKER, Boolean.TRUE);
-					
-					if(parent instanceof Node) {
-						((Node)parent).attachChild(armature);
-					}
-					result = armature;
-					break;
-				default:
-					LOGGER.log(Level.WARNING, "Unknown object type: {0}", type);
-			}
-		} finally {
-			blenderContext.popParent();
-		}
-		
-		if(result != null) {
-			result.updateModelBound();//I prefer do compute bounding box here than read it from the file
-			
-			blenderContext.addLoadedFeatures(objectStructure.getOldMemoryAddress(), name, objectStructure, result);
-			
-			//applying modifiers
-			LOGGER.log(Level.FINE, "Reading and applying object's modifiers.");
-			ModifierHelper modifierHelper = blenderContext.getHelper(ModifierHelper.class);
-			Collection<Modifier> modifiers = modifierHelper.readModifiers(objectStructure, blenderContext);
-			for(Modifier modifier : modifiers) {
-				modifier.apply(result, blenderContext);
-			}
-			
-			//loading constraints connected with this object
-			ConstraintHelper constraintHelper = blenderContext.getHelper(ConstraintHelper.class);
-			constraintHelper.loadConstraints(objectStructure, blenderContext);
-			
-			//reading custom properties
-			if(blenderContext.getBlenderKey().isLoadObjectProperties()) {
-				Properties properties = this.loadProperties(objectStructure, blenderContext);
-				//the loaded property is a group property, so we need to get each value and set it to Spatial
-				if(result instanceof Spatial && properties != null && properties.getValue() != null) {
-					this.applyProperties((Spatial) result, properties);
-				}
-			}
-		}
-		return result;
-	}
-	
-	/**
-	 * This method calculates local transformation for the object. Parentage is taken under consideration.
-	 * @param objectStructure
-	 *        the object's structure
-	 * @return objects transformation relative to its parent
-	 */
-	@SuppressWarnings("unchecked")
-	public Transform getTransformation(Structure objectStructure, BlenderContext blenderContext) {
-		//these are transformations in global space
-		DynamicArray<Number> loc = (DynamicArray<Number>)objectStructure.getFieldValue("loc");
-		DynamicArray<Number> size = (DynamicArray<Number>)objectStructure.getFieldValue("size");
-		DynamicArray<Number> rot = (DynamicArray<Number>)objectStructure.getFieldValue("rot");
-
-		//load parent inverse matrix
-		Pointer pParent = (Pointer) objectStructure.getFieldValue("parent");
-		Matrix4f parentInv = pParent.isNull() ? Matrix4f.IDENTITY : this.getMatrix(objectStructure, "parentinv");
-		
-		//create the global matrix (without the scale)
-		Matrix4f globalMatrix = new Matrix4f();
-		globalMatrix.setTranslation(loc.get(0).floatValue(), loc.get(1).floatValue(), loc.get(2).floatValue());
-		globalMatrix.setRotationQuaternion(new Quaternion().fromAngles(rot.get(0).floatValue(), rot.get(1).floatValue(), rot.get(2).floatValue()));
-		//compute local matrix
-		Matrix4f localMatrix = parentInv.mult(globalMatrix);
-
-		Vector3f translation = localMatrix.toTranslationVector();
-		Quaternion rotation = localMatrix.toRotationQuat();
-		Vector3f scale = this.getScale(parentInv).multLocal(size.get(0).floatValue(), size.get(1).floatValue(), size.get(2).floatValue());
-		
-		if(fixUpAxis) {
-			float y = translation.y;
-			translation.y = translation.z;
-			translation.z = -y;
-			
-			y = rotation.getY();
-			float z = rotation.getZ();
-			rotation.set(rotation.getX(), z, -y, rotation.getW());
-			
-			y=scale.y;
-			scale.y = scale.z;
-			scale.z = y;
-		}
-		
-		//create the result
-		Transform t = new Transform(translation, rotation);
-		t.setScale(scale);
-		return t;
-	}
-
-	/**
-	 * This method returns the matrix of a given name for the given structure.
-	 * The matrix is NOT transformed if Y axis is up - the raw data is loaded from the blender file.
-	 * @param structure
-	 *        the structure with matrix data
-	 * @param matrixName
-	 * 		  the name of the matrix
-	 * @return the required matrix
-	 */
-	public Matrix4f getMatrix(Structure structure, String matrixName) {
-		return this.getMatrix(structure, matrixName, false);
-	}
-	
-	/**
-	 * This method returns the matrix of a given name for the given structure.
-	 * It takes up axis into consideration.
-	 * @param structure
-	 *        the structure with matrix data
-	 * @param matrixName
-	 * 		  the name of the matrix
-	 * @return the required matrix
-	 */
-	@SuppressWarnings("unchecked")
-	public Matrix4f getMatrix(Structure structure, String matrixName, boolean applyFixUpAxis) {
-		Matrix4f result = new Matrix4f();
-		DynamicArray<Number> obmat = (DynamicArray<Number>)structure.getFieldValue(matrixName);
-		int rowAndColumnSize = Math.abs((int)Math.sqrt(obmat.getTotalSize()));//the matrix must be square
-		for(int i = 0; i < rowAndColumnSize; ++i) {
-			for(int j = 0; j < rowAndColumnSize; ++j) {
-				result.set(i, j, obmat.get(j, i).floatValue());
-			}
-		}
-		if(applyFixUpAxis && fixUpAxis) {
-        	Vector3f translation = result.toTranslationVector();
+                    node.setLocalTransform(t);
+
+                    // setting the parent
+                    if (parent instanceof Node) {
+                        ((Node) parent).attachChild(node);
+                    }
+                    result = node;
+                    break;
+                case OBJECT_TYPE_SURF:
+                case OBJECT_TYPE_CURVE:
+                    LOGGER.log(Level.FINE, "Importing curve/nurb.");
+                    Pointer pCurve = (Pointer) objectStructure.getFieldValue("data");
+                    if (pCurve.isNotNull()) {
+                        CurvesHelper curvesHelper = blenderContext.getHelper(CurvesHelper.class);
+                        Structure curveData = pCurve.fetchData(blenderContext.getInputStream()).get(0);
+                        List<Geometry> curves = curvesHelper.toCurve(curveData, blenderContext);
+                        result = new Node(name);
+                        for (Geometry curve : curves) {
+                            ((Node) result).attachChild(curve);
+                        }
+                        ((Node) result).setLocalTransform(t);
+                    }
+                    break;
+                case OBJECT_TYPE_LAMP:
+                    LOGGER.log(Level.FINE, "Importing lamp.");
+                    Pointer pLamp = (Pointer) objectStructure.getFieldValue("data");
+                    if (pLamp.isNotNull()) {
+                        LightHelper lightHelper = blenderContext.getHelper(LightHelper.class);
+                        List<Structure> lampsArray = pLamp.fetchData(blenderContext.getInputStream());
+                        LightNode light = lightHelper.toLight(lampsArray.get(0), blenderContext);
+                        if (light != null) {
+                            light.setName(name);
+                            light.setLocalTransform(t);
+                        }
+                        result = light;
+                    }
+                    break;
+                case OBJECT_TYPE_CAMERA:
+                    Pointer pCamera = (Pointer) objectStructure.getFieldValue("data");
+                    if (pCamera.isNotNull()) {
+                        CameraHelper cameraHelper = blenderContext.getHelper(CameraHelper.class);
+                        List<Structure> camerasArray = pCamera.fetchData(blenderContext.getInputStream());
+                        CameraNode camera = cameraHelper.toCamera(camerasArray.get(0), blenderContext);
+                        camera.setName(name);
+                        camera.setLocalTransform(t);
+                        result = camera;
+                    }
+                    break;
+                case OBJECT_TYPE_ARMATURE:
+                    // need to create an empty node to properly create parent-children relationships between nodes
+                    Node armature = new Node(name);
+                    armature.setLocalTransform(t);
+                    armature.setUserData(ArmatureHelper.ARMETURE_NODE_MARKER, Boolean.TRUE);
+
+                    if (parent instanceof Node) {
+                        ((Node) parent).attachChild(armature);
+                    }
+                    result = armature;
+                    break;
+                default:
+                    LOGGER.log(Level.WARNING, "Unknown object type: {0}", type);
+            }
+        } finally {
+            blenderContext.popParent();
+        }
+
+        if (result != null) {
+            result.updateModelBound();// I prefer do compute bounding box here than read it from the file
+
+            blenderContext.addLoadedFeatures(objectStructure.getOldMemoryAddress(), name, objectStructure, result);
+
+            // applying modifiers
+            LOGGER.log(Level.FINE, "Reading and applying object's modifiers.");
+            ModifierHelper modifierHelper = blenderContext.getHelper(ModifierHelper.class);
+            Collection<Modifier> modifiers = modifierHelper.readModifiers(objectStructure, blenderContext);
+            for (Modifier modifier : modifiers) {
+                modifier.apply(result, blenderContext);
+            }
+
+            // loading constraints connected with this object
+            ConstraintHelper constraintHelper = blenderContext.getHelper(ConstraintHelper.class);
+            constraintHelper.loadConstraints(objectStructure, blenderContext);
+
+            // reading custom properties
+            if (blenderContext.getBlenderKey().isLoadObjectProperties()) {
+                Properties properties = this.loadProperties(objectStructure, blenderContext);
+                // the loaded property is a group property, so we need to get each value and set it to Spatial
+                if (result instanceof Spatial && properties != null && properties.getValue() != null) {
+                    this.applyProperties((Spatial) result, properties);
+                }
+            }
+        }
+        return result;
+    }
+
+    /**
+     * This method calculates local transformation for the object. Parentage is taken under consideration.
+     * @param objectStructure
+     *            the object's structure
+     * @return objects transformation relative to its parent
+     */
+    @SuppressWarnings("unchecked")
+    public Transform getTransformation(Structure objectStructure, BlenderContext blenderContext) {
+        // these are transformations in global space
+        DynamicArray<Number> loc = (DynamicArray<Number>) objectStructure.getFieldValue("loc");
+        DynamicArray<Number> size = (DynamicArray<Number>) objectStructure.getFieldValue("size");
+        DynamicArray<Number> rot = (DynamicArray<Number>) objectStructure.getFieldValue("rot");
+
+        // load parent inverse matrix
+        Pointer pParent = (Pointer) objectStructure.getFieldValue("parent");
+        Matrix4f parentInv = pParent.isNull() ? Matrix4f.IDENTITY : this.getMatrix(objectStructure, "parentinv");
+
+        // create the global matrix (without the scale)
+        Matrix4f globalMatrix = new Matrix4f();
+        globalMatrix.setTranslation(loc.get(0).floatValue(), loc.get(1).floatValue(), loc.get(2).floatValue());
+        globalMatrix.setRotationQuaternion(new Quaternion().fromAngles(rot.get(0).floatValue(), rot.get(1).floatValue(), rot.get(2).floatValue()));
+        // compute local matrix
+        Matrix4f localMatrix = parentInv.mult(globalMatrix);
+
+        Vector3f translation = localMatrix.toTranslationVector();
+        Quaternion rotation = localMatrix.toRotationQuat();
+        Vector3f scale = this.getScale(parentInv).multLocal(size.get(0).floatValue(), size.get(1).floatValue(), size.get(2).floatValue());
+
+        if (fixUpAxis) {
+            float y = translation.y;
+            translation.y = translation.z;
+            translation.z = -y;
+
+            y = rotation.getY();
+            float z = rotation.getZ();
+            rotation.set(rotation.getX(), z, -y, rotation.getW());
+
+            y = scale.y;
+            scale.y = scale.z;
+            scale.z = y;
+        }
+
+        // create the result
+        Transform t = new Transform(translation, rotation);
+        t.setScale(scale);
+        return t;
+    }
+
+    /**
+     * This method returns the matrix of a given name for the given structure.
+     * The matrix is NOT transformed if Y axis is up - the raw data is loaded from the blender file.
+     * @param structure
+     *            the structure with matrix data
+     * @param matrixName
+     *            the name of the matrix
+     * @return the required matrix
+     */
+    public Matrix4f getMatrix(Structure structure, String matrixName) {
+        return this.getMatrix(structure, matrixName, false);
+    }
+
+    /**
+     * This method returns the matrix of a given name for the given structure.
+     * It takes up axis into consideration.
+     * @param structure
+     *            the structure with matrix data
+     * @param matrixName
+     *            the name of the matrix
+     * @return the required matrix
+     */
+    @SuppressWarnings("unchecked")
+    public Matrix4f getMatrix(Structure structure, String matrixName, boolean applyFixUpAxis) {
+        Matrix4f result = new Matrix4f();
+        DynamicArray<Number> obmat = (DynamicArray<Number>) structure.getFieldValue(matrixName);
+        int rowAndColumnSize = Math.abs((int) Math.sqrt(obmat.getTotalSize()));// the matrix must be square
+        for (int i = 0; i < rowAndColumnSize; ++i) {
+            for (int j = 0; j < rowAndColumnSize; ++j) {
+                result.set(i, j, obmat.get(j, i).floatValue());
+            }
+        }
+        if (applyFixUpAxis && fixUpAxis) {
+            Vector3f translation = result.toTranslationVector();
             Quaternion rotation = result.toRotationQuat();
             Vector3f scale = this.getScale(result);
-            
-			float y = translation.y;
-			translation.y = translation.z;
-			translation.z = -y;
-			
-			y = rotation.getY();
-			float z = rotation.getZ();
-			rotation.set(rotation.getX(), z, -y, rotation.getW());
-			
-			y=scale.y;
-			scale.y = scale.z;
-			scale.z = y;
-			
-			result.loadIdentity();
-			result.setTranslation(translation);
-			result.setRotationQuaternion(rotation);
-			result.setScale(scale);
+
+            float y = translation.y;
+            translation.y = translation.z;
+            translation.z = -y;
+
+            y = rotation.getY();
+            float z = rotation.getZ();
+            rotation.set(rotation.getX(), z, -y, rotation.getW());
+
+            y = scale.y;
+            scale.y = scale.z;
+            scale.z = y;
+
+            result.loadIdentity();
+            result.setTranslation(translation);
+            result.setRotationQuaternion(rotation);
+            result.setScale(scale);
         }
-		return result;
-	}
-
-	/**
-	 * This method returns the scale from the given matrix.
-	 * 
-	 * @param matrix
-	 *            the transformation matrix
-	 * @return the scale from the given matrix
-	 */
-	public Vector3f getScale(Matrix4f matrix) {
-		float scaleX = (float) Math.sqrt(matrix.m00 * matrix.m00 + matrix.m10 * matrix.m10 + matrix.m20 * matrix.m20);
-		float scaleY = (float) Math.sqrt(matrix.m01 * matrix.m01 + matrix.m11 * matrix.m11 + matrix.m21 * matrix.m21);
-		float scaleZ = (float) Math.sqrt(matrix.m02 * matrix.m02 + matrix.m12 * matrix.m12 + matrix.m22 * matrix.m22);
-		return new Vector3f(scaleX, scaleY, scaleZ);
-	}
-	
-	@Override
-	public void clearState() {
-		fixUpAxis = false;
-	}
-	
-	@Override
-	public boolean shouldBeLoaded(Structure structure, BlenderContext blenderContext) {
-		int lay = ((Number) structure.getFieldValue("lay")).intValue();
-        return (lay & blenderContext.getBlenderKey().getLayersToLoad()) != 0
-                && (blenderContext.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.OBJECTS) != 0;
-	}
+        return result;
+    }
+
+    /**
+     * This method returns the scale from the given matrix.
+     * 
+     * @param matrix
+     *            the transformation matrix
+     * @return the scale from the given matrix
+     */
+    public Vector3f getScale(Matrix4f matrix) {
+        float scaleX = (float) Math.sqrt(matrix.m00 * matrix.m00 + matrix.m10 * matrix.m10 + matrix.m20 * matrix.m20);
+        float scaleY = (float) Math.sqrt(matrix.m01 * matrix.m01 + matrix.m11 * matrix.m11 + matrix.m21 * matrix.m21);
+        float scaleZ = (float) Math.sqrt(matrix.m02 * matrix.m02 + matrix.m12 * matrix.m12 + matrix.m22 * matrix.m22);
+        return new Vector3f(scaleX, scaleY, scaleZ);
+    }
+
+    @Override
+    public void clearState() {
+        fixUpAxis = false;
+    }
+
+    @Override
+    public boolean shouldBeLoaded(Structure structure, BlenderContext blenderContext) {
+        int lay = ((Number) structure.getFieldValue("lay")).intValue();
+        return (lay & blenderContext.getBlenderKey().getLayersToLoad()) != 0 && (blenderContext.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.OBJECTS) != 0;
+    }
 }

+ 345 - 345
engine/src/blender/com/jme3/scene/plugins/blender/objects/Properties.java

@@ -17,364 +17,364 @@ import java.util.Map;
  * @author Marcin Roguski (Kaelthas)
  */
 public class Properties implements Cloneable {
-	// property type
-	public static final int			IDP_STRING			= 0;
-	public static final int			IDP_INT				= 1;
-	public static final int			IDP_FLOAT			= 2;
-	public static final int			IDP_ARRAY			= 5;
-	public static final int			IDP_GROUP			= 6;
-	// public static final int IDP_ID = 7;//this is not implemented in blender (yet)
-	public static final int			IDP_DOUBLE			= 8;
-	// the following are valid for blender 2.5x+
-	public static final int			IDP_IDPARRAY		= 9;
-	public static final int			IDP_NUMTYPES		= 10;
+    // property type
+    public static final int       IDP_STRING        = 0;
+    public static final int       IDP_INT           = 1;
+    public static final int       IDP_FLOAT         = 2;
+    public static final int       IDP_ARRAY         = 5;
+    public static final int       IDP_GROUP         = 6;
+    // public static final int IDP_ID = 7;//this is not implemented in blender (yet)
+    public static final int       IDP_DOUBLE        = 8;
+    // the following are valid for blender 2.5x+
+    public static final int       IDP_IDPARRAY      = 9;
+    public static final int       IDP_NUMTYPES      = 10;
 
-	protected static final String	RNA_PROPERTY_NAME	= "_RNA_UI";
-	/** Default name of the property (used if the name is not specified in blender file). */
-	protected static final String	DEFAULT_NAME		= "Unnamed property";
+    protected static final String RNA_PROPERTY_NAME = "_RNA_UI";
+    /** Default name of the property (used if the name is not specified in blender file). */
+    protected static final String DEFAULT_NAME      = "Unnamed property";
 
-	/** The name of the property. */
-	private String					name;
-	/** The type of the property. */
-	private int						type;
-	/** The subtype of the property. Defines the type of array's elements. */
-	private int						subType;
-	/** The value of the property. */
-	private Object					value;
-	/** The description of the property. */
-	private String					description;
+    /** The name of the property. */
+    private String                name;
+    /** The type of the property. */
+    private int                   type;
+    /** The subtype of the property. Defines the type of array's elements. */
+    private int                   subType;
+    /** The value of the property. */
+    private Object                value;
+    /** The description of the property. */
+    private String                description;
 
-	/**
-	 * This method loads the property from the belnder file.
-	 * @param idPropertyStructure
-	 *        the ID structure constining the property
-	 * @param blenderContext
-	 *        the blender context
-	 * @throws BlenderFileException
-	 *         an exception is thrown when the belnder file is somehow invalid
-	 */
-	public void load(Structure idPropertyStructure, BlenderContext blenderContext) throws BlenderFileException {
-		name = idPropertyStructure.getFieldValue("name").toString();
-		if (name == null || name.length() == 0) {
-			name = DEFAULT_NAME;
-		}
-		subType = ((Number) idPropertyStructure.getFieldValue("subtype")).intValue();
-		type = ((Number) idPropertyStructure.getFieldValue("type")).intValue();
+    /**
+     * This method loads the property from the belnder file.
+     * @param idPropertyStructure
+     *            the ID structure constining the property
+     * @param blenderContext
+     *            the blender context
+     * @throws BlenderFileException
+     *             an exception is thrown when the belnder file is somehow invalid
+     */
+    public void load(Structure idPropertyStructure, BlenderContext blenderContext) throws BlenderFileException {
+        name = idPropertyStructure.getFieldValue("name").toString();
+        if (name == null || name.length() == 0) {
+            name = DEFAULT_NAME;
+        }
+        subType = ((Number) idPropertyStructure.getFieldValue("subtype")).intValue();
+        type = ((Number) idPropertyStructure.getFieldValue("type")).intValue();
 
-		// reading the data
-		Structure data = (Structure) idPropertyStructure.getFieldValue("data");
-		int len = ((Number) idPropertyStructure.getFieldValue("len")).intValue();
-		switch (type) {
-			case IDP_STRING: {
-				Pointer pointer = (Pointer) data.getFieldValue("pointer");
-				BlenderInputStream bis = blenderContext.getInputStream();
-				FileBlockHeader dataFileBlock = blenderContext.getFileBlock(pointer.getOldMemoryAddress());
-				bis.setPosition(dataFileBlock.getBlockPosition());
-				value = bis.readString();
-				break;
-			}
-			case IDP_INT:
-				int intValue = ((Number) data.getFieldValue("val")).intValue();
-				value = Integer.valueOf(intValue);
-				break;
-			case IDP_FLOAT:
-				int floatValue = ((Number) data.getFieldValue("val")).intValue();
-				value = Float.valueOf(Float.intBitsToFloat(floatValue));
-				break;
-			case IDP_ARRAY: {
-				Pointer pointer = (Pointer) data.getFieldValue("pointer");
-				BlenderInputStream bis = blenderContext.getInputStream();
-				FileBlockHeader dataFileBlock = blenderContext.getFileBlock(pointer.getOldMemoryAddress());
-				bis.setPosition(dataFileBlock.getBlockPosition());
-				int elementAmount = dataFileBlock.getSize();
-				switch (subType) {
-					case IDP_INT:
-						elementAmount /= 4;
-						int[] intList = new int[elementAmount];
-						for (int i = 0; i < elementAmount; ++i) {
-							intList[i] = bis.readInt();
-						}
-						value = intList;
-						break;
-					case IDP_FLOAT:
-						elementAmount /= 4;
-						float[] floatList = new float[elementAmount];
-						for (int i = 0; i < elementAmount; ++i) {
-							floatList[i] = bis.readFloat();
-						}
-						value = floatList;
-						break;
-					case IDP_DOUBLE:
-						elementAmount /= 8;
-						double[] doubleList = new double[elementAmount];
-						for (int i = 0; i < elementAmount; ++i) {
-							doubleList[i] = bis.readDouble();
-						}
-						value = doubleList;
-						break;
-					default:
-						throw new IllegalStateException("Invalid array subtype: " + subType);
-				}
-			}
-			case IDP_GROUP:
-				Structure group = (Structure) data.getFieldValue("group");
-				List<Structure> dataList = group.evaluateListBase(blenderContext);
-				List<Properties> subProperties = new ArrayList<Properties>(len);
-				for (Structure d : dataList) {
-					Properties properties = new Properties();
-					properties.load(d, blenderContext);
-					subProperties.add(properties);
-				}
-				value = subProperties;
-				break;
-			case IDP_DOUBLE:
-				int doublePart1 = ((Number) data.getFieldValue("val")).intValue();
-				int doublePart2 = ((Number) data.getFieldValue("val2")).intValue();
-				long doubleVal = (long) doublePart2 << 32 | doublePart1;
-				value = Double.valueOf(Double.longBitsToDouble(doubleVal));
-				break;
-			case IDP_IDPARRAY: {
-				Pointer pointer = (Pointer) data.getFieldValue("pointer");
-				List<Structure> arrays = pointer.fetchData(blenderContext.getInputStream());
-				List<Object> result = new ArrayList<Object>(arrays.size());
-				Properties temp = new Properties();
-				for (Structure array : arrays) {
-					temp.load(array, blenderContext);
-					result.add(temp.value);
-				}
-				this.value = result;
-				break;
-			}
-			case IDP_NUMTYPES:
-				throw new UnsupportedOperationException();
-				// case IDP_ID://not yet implemented in blender
-				// return null;
-			default:
-				throw new IllegalStateException("Unknown custom property type: " + type);
-		}
-		this.completeLoading();
-	}
+        // reading the data
+        Structure data = (Structure) idPropertyStructure.getFieldValue("data");
+        int len = ((Number) idPropertyStructure.getFieldValue("len")).intValue();
+        switch (type) {
+            case IDP_STRING: {
+                Pointer pointer = (Pointer) data.getFieldValue("pointer");
+                BlenderInputStream bis = blenderContext.getInputStream();
+                FileBlockHeader dataFileBlock = blenderContext.getFileBlock(pointer.getOldMemoryAddress());
+                bis.setPosition(dataFileBlock.getBlockPosition());
+                value = bis.readString();
+                break;
+            }
+            case IDP_INT:
+                int intValue = ((Number) data.getFieldValue("val")).intValue();
+                value = Integer.valueOf(intValue);
+                break;
+            case IDP_FLOAT:
+                int floatValue = ((Number) data.getFieldValue("val")).intValue();
+                value = Float.valueOf(Float.intBitsToFloat(floatValue));
+                break;
+            case IDP_ARRAY: {
+                Pointer pointer = (Pointer) data.getFieldValue("pointer");
+                BlenderInputStream bis = blenderContext.getInputStream();
+                FileBlockHeader dataFileBlock = blenderContext.getFileBlock(pointer.getOldMemoryAddress());
+                bis.setPosition(dataFileBlock.getBlockPosition());
+                int elementAmount = dataFileBlock.getSize();
+                switch (subType) {
+                    case IDP_INT:
+                        elementAmount /= 4;
+                        int[] intList = new int[elementAmount];
+                        for (int i = 0; i < elementAmount; ++i) {
+                            intList[i] = bis.readInt();
+                        }
+                        value = intList;
+                        break;
+                    case IDP_FLOAT:
+                        elementAmount /= 4;
+                        float[] floatList = new float[elementAmount];
+                        for (int i = 0; i < elementAmount; ++i) {
+                            floatList[i] = bis.readFloat();
+                        }
+                        value = floatList;
+                        break;
+                    case IDP_DOUBLE:
+                        elementAmount /= 8;
+                        double[] doubleList = new double[elementAmount];
+                        for (int i = 0; i < elementAmount; ++i) {
+                            doubleList[i] = bis.readDouble();
+                        }
+                        value = doubleList;
+                        break;
+                    default:
+                        throw new IllegalStateException("Invalid array subtype: " + subType);
+                }
+            }
+            case IDP_GROUP:
+                Structure group = (Structure) data.getFieldValue("group");
+                List<Structure> dataList = group.evaluateListBase(blenderContext);
+                List<Properties> subProperties = new ArrayList<Properties>(len);
+                for (Structure d : dataList) {
+                    Properties properties = new Properties();
+                    properties.load(d, blenderContext);
+                    subProperties.add(properties);
+                }
+                value = subProperties;
+                break;
+            case IDP_DOUBLE:
+                int doublePart1 = ((Number) data.getFieldValue("val")).intValue();
+                int doublePart2 = ((Number) data.getFieldValue("val2")).intValue();
+                long doubleVal = (long) doublePart2 << 32 | doublePart1;
+                value = Double.valueOf(Double.longBitsToDouble(doubleVal));
+                break;
+            case IDP_IDPARRAY: {
+                Pointer pointer = (Pointer) data.getFieldValue("pointer");
+                List<Structure> arrays = pointer.fetchData(blenderContext.getInputStream());
+                List<Object> result = new ArrayList<Object>(arrays.size());
+                Properties temp = new Properties();
+                for (Structure array : arrays) {
+                    temp.load(array, blenderContext);
+                    result.add(temp.value);
+                }
+                this.value = result;
+                break;
+            }
+            case IDP_NUMTYPES:
+                throw new UnsupportedOperationException();
+                // case IDP_ID://not yet implemented in blender
+                // return null;
+            default:
+                throw new IllegalStateException("Unknown custom property type: " + type);
+        }
+        this.completeLoading();
+    }
 
-	/**
-	 * This method returns the name of the property.
-	 * @return the name of the property
-	 */
-	public String getName() {
-		return name;
-	}
+    /**
+     * This method returns the name of the property.
+     * @return the name of the property
+     */
+    public String getName() {
+        return name;
+    }
 
-	/**
-	 * This method returns the description of the property.
-	 * @return the description of the property
-	 */
-	public String getDescription() {
-		return description;
-	}
+    /**
+     * This method returns the description of the property.
+     * @return the description of the property
+     */
+    public String getDescription() {
+        return description;
+    }
 
-	/**
-	 * This method returns the type of the property.
-	 * @return the type of the property
-	 */
-	public int getType() {
-		return type;
-	}
+    /**
+     * This method returns the type of the property.
+     * @return the type of the property
+     */
+    public int getType() {
+        return type;
+    }
 
-	/**
-	 * This method returns the value of the property.
-	 * The type of the value depends on the type of the property.
-	 * @return the value of the property
-	 */
-	public Object getValue() {
-		return value;
-	}
-	
-	/**
-	 * @return the names of properties that are stored withing this property 
-	 * (assuming this property is of IDP_GROUP type) 
-	 */
-	@SuppressWarnings("unchecked")
-	public List<String> getSubPropertiesNames() {
-		List<String> result = null;
-		if(this.type == IDP_GROUP) {
-			List<Properties> properties = (List<Properties>)this.value;
-			if(properties != null && properties.size() > 0) {
-				result = new ArrayList<String>(properties.size());
-				for(Properties property : properties) {
-					result.add(property.getName());
-				}
-			}
-		}
-		return result;
-	}
-	
-	/**
-	 * This method returns the same as getValue if the current property is of
-	 * other type than IDP_GROUP and its name matches 'propertyName' param. If
-	 * this property is a group property the method tries to find subproperty
-	 * value of the given name. The first found value is returnes os <b>use this
-	 * method wisely</b>. If no property of a given name is foung - <b>null</b>
-	 * is returned.
-	 * 
-	 * @param propertyName
-	 *            the name of the property
-	 * @return found property value or <b>null</b>
-	 */
-	@SuppressWarnings("unchecked")
-	public Object findValue(String propertyName) {
-		if (name.equals(propertyName)) {
-			return value;
-		} else {
-			if (type == IDP_GROUP) {
-				List<Properties> props = (List<Properties>) value;
-				for (Properties p : props) {
-					Object v = p.findValue(propertyName);
-					if (v != null) {
-						return v;
-					}
-				}
-			}
-		}
-		return null;
-	}
+    /**
+     * This method returns the value of the property.
+     * The type of the value depends on the type of the property.
+     * @return the value of the property
+     */
+    public Object getValue() {
+        return value;
+    }
 
-	@Override
-	public String toString() {
-		StringBuilder sb = new StringBuilder();
-		this.append(sb, new StringBuilder());
-		return sb.toString();
-	}
+    /**
+     * @return the names of properties that are stored withing this property
+     *         (assuming this property is of IDP_GROUP type)
+     */
+    @SuppressWarnings("unchecked")
+    public List<String> getSubPropertiesNames() {
+        List<String> result = null;
+        if (this.type == IDP_GROUP) {
+            List<Properties> properties = (List<Properties>) this.value;
+            if (properties != null && properties.size() > 0) {
+                result = new ArrayList<String>(properties.size());
+                for (Properties property : properties) {
+                    result.add(property.getName());
+                }
+            }
+        }
+        return result;
+    }
 
-	/**
-	 * This method appends the data of the property to the given string buffer.
-	 * @param sb
-	 *        string buffer
-	 * @param indent
-	 *        indent buffer
-	 */
-	@SuppressWarnings("unchecked")
-	private void append(StringBuilder sb, StringBuilder indent) {
-		sb.append(indent).append("name: ").append(name).append("\n\r");
-		sb.append(indent).append("type: ").append(type).append("\n\r");
-		sb.append(indent).append("subType: ").append(subType).append("\n\r");
-		sb.append(indent).append("description: ").append(description).append("\n\r");
-		indent.append('\t');
-		sb.append(indent).append("value: ");
-		if (value instanceof Properties) {
-			((Properties) value).append(sb, indent);
-		} else if (value instanceof List) {
-			for (Object v : (List<Object>) value) {
-				if (v instanceof Properties) {
-					sb.append(indent).append("{\n\r");
-					indent.append('\t');
-					((Properties) v).append(sb, indent);
-					indent.deleteCharAt(indent.length() - 1);
-					sb.append(indent).append("}\n\r");
-				} else {
-					sb.append(v);
-				}
-			}
-		} else {
-			sb.append(value);
-		}
-		sb.append("\n\r");
-		indent.deleteCharAt(indent.length() - 1);
-	}
+    /**
+     * This method returns the same as getValue if the current property is of
+     * other type than IDP_GROUP and its name matches 'propertyName' param. If
+     * this property is a group property the method tries to find subproperty
+     * value of the given name. The first found value is returnes os <b>use this
+     * method wisely</b>. If no property of a given name is foung - <b>null</b>
+     * is returned.
+     * 
+     * @param propertyName
+     *            the name of the property
+     * @return found property value or <b>null</b>
+     */
+    @SuppressWarnings("unchecked")
+    public Object findValue(String propertyName) {
+        if (name.equals(propertyName)) {
+            return value;
+        } else {
+            if (type == IDP_GROUP) {
+                List<Properties> props = (List<Properties>) value;
+                for (Properties p : props) {
+                    Object v = p.findValue(propertyName);
+                    if (v != null) {
+                        return v;
+                    }
+                }
+            }
+        }
+        return null;
+    }
 
-	/**
-	 * This method should be called after the properties loading.
-	 * It loads the properties from the _RNA_UI property and removes this property from the
-	 * result list.
-	 */
-	@SuppressWarnings("unchecked")
-	protected void completeLoading() {
-		if (this.type == IDP_GROUP) {
-			List<Properties> groupProperties = (List<Properties>) this.value;
-			Properties rnaUI = null;
-			for (Properties properties : groupProperties) {
-				if (properties.name.equals(RNA_PROPERTY_NAME) && properties.type == IDP_GROUP) {
-					rnaUI = properties;
-					break;
-				}
-			}
-			if (rnaUI != null) {
-				// removing the RNA from the result list
-				groupProperties.remove(rnaUI);
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        this.append(sb, new StringBuilder());
+        return sb.toString();
+    }
 
-				// loading the descriptions
-				Map<String, String> descriptions = new HashMap<String, String>(groupProperties.size());
-				List<Properties> propertiesRNA = (List<Properties>) rnaUI.value;
-				for (Properties properties : propertiesRNA) {
-					String name = properties.name;
-					String description = null;
-					List<Properties> rnaData = (List<Properties>) properties.value;
-					for (Properties rna : rnaData) {
-						if ("description".equalsIgnoreCase(rna.name)) {
-							description = (String) rna.value;
-							break;
-						}
-					}
-					descriptions.put(name, description);
-				}
+    /**
+     * This method appends the data of the property to the given string buffer.
+     * @param sb
+     *            string buffer
+     * @param indent
+     *            indent buffer
+     */
+    @SuppressWarnings("unchecked")
+    private void append(StringBuilder sb, StringBuilder indent) {
+        sb.append(indent).append("name: ").append(name).append("\n\r");
+        sb.append(indent).append("type: ").append(type).append("\n\r");
+        sb.append(indent).append("subType: ").append(subType).append("\n\r");
+        sb.append(indent).append("description: ").append(description).append("\n\r");
+        indent.append('\t');
+        sb.append(indent).append("value: ");
+        if (value instanceof Properties) {
+            ((Properties) value).append(sb, indent);
+        } else if (value instanceof List) {
+            for (Object v : (List<Object>) value) {
+                if (v instanceof Properties) {
+                    sb.append(indent).append("{\n\r");
+                    indent.append('\t');
+                    ((Properties) v).append(sb, indent);
+                    indent.deleteCharAt(indent.length() - 1);
+                    sb.append(indent).append("}\n\r");
+                } else {
+                    sb.append(v);
+                }
+            }
+        } else {
+            sb.append(value);
+        }
+        sb.append("\n\r");
+        indent.deleteCharAt(indent.length() - 1);
+    }
 
-				// applying the descriptions
-				for (Properties properties : groupProperties) {
-					properties.description = descriptions.get(properties.name);
-				}
-			}
-		}
-	}
+    /**
+     * This method should be called after the properties loading.
+     * It loads the properties from the _RNA_UI property and removes this property from the
+     * result list.
+     */
+    @SuppressWarnings("unchecked")
+    protected void completeLoading() {
+        if (this.type == IDP_GROUP) {
+            List<Properties> groupProperties = (List<Properties>) this.value;
+            Properties rnaUI = null;
+            for (Properties properties : groupProperties) {
+                if (properties.name.equals(RNA_PROPERTY_NAME) && properties.type == IDP_GROUP) {
+                    rnaUI = properties;
+                    break;
+                }
+            }
+            if (rnaUI != null) {
+                // removing the RNA from the result list
+                groupProperties.remove(rnaUI);
 
-	@Override
-	public int hashCode() {
-		final int prime = 31;
-		int result = 1;
-		result = prime * result + (description == null ? 0 : description.hashCode());
-		result = prime * result + (name == null ? 0 : name.hashCode());
-		result = prime * result + subType;
-		result = prime * result + type;
-		result = prime * result + (value == null ? 0 : value.hashCode());
-		return result;
-	}
+                // loading the descriptions
+                Map<String, String> descriptions = new HashMap<String, String>(groupProperties.size());
+                List<Properties> propertiesRNA = (List<Properties>) rnaUI.value;
+                for (Properties properties : propertiesRNA) {
+                    String name = properties.name;
+                    String description = null;
+                    List<Properties> rnaData = (List<Properties>) properties.value;
+                    for (Properties rna : rnaData) {
+                        if ("description".equalsIgnoreCase(rna.name)) {
+                            description = (String) rna.value;
+                            break;
+                        }
+                    }
+                    descriptions.put(name, description);
+                }
 
-	@Override
-	public boolean equals(Object obj) {
-		if (this == obj) {
-			return true;
-		}
-		if (obj == null) {
-			return false;
-		}
-		if (this.getClass() != obj.getClass()) {
-			return false;
-		}
-		Properties other = (Properties) obj;
-		if (description == null) {
-			if (other.description != null) {
-				return false;
-			}
-		} else if (!description.equals(other.description)) {
-			return false;
-		}
-		if (name == null) {
-			if (other.name != null) {
-				return false;
-			}
-		} else if (!name.equals(other.name)) {
-			return false;
-		}
-		if (subType != other.subType) {
-			return false;
-		}
-		if (type != other.type) {
-			return false;
-		}
-		if (value == null) {
-			if (other.value != null) {
-				return false;
-			}
-		} else if (!value.equals(other.value)) {
-			return false;
-		}
-		return true;
-	}
+                // applying the descriptions
+                for (Properties properties : groupProperties) {
+                    properties.description = descriptions.get(properties.name);
+                }
+            }
+        }
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + (description == null ? 0 : description.hashCode());
+        result = prime * result + (name == null ? 0 : name.hashCode());
+        result = prime * result + subType;
+        result = prime * result + type;
+        result = prime * result + (value == null ? 0 : value.hashCode());
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (this.getClass() != obj.getClass()) {
+            return false;
+        }
+        Properties other = (Properties) obj;
+        if (description == null) {
+            if (other.description != null) {
+                return false;
+            }
+        } else if (!description.equals(other.description)) {
+            return false;
+        }
+        if (name == null) {
+            if (other.name != null) {
+                return false;
+            }
+        } else if (!name.equals(other.name)) {
+            return false;
+        }
+        if (subType != other.subType) {
+            return false;
+        }
+        if (type != other.type) {
+            return false;
+        }
+        if (value == null) {
+            if (other.value != null) {
+                return false;
+            }
+        } else if (!value.equals(other.value)) {
+            return false;
+        }
+        return true;
+    }
 }

+ 175 - 175
engine/src/blender/com/jme3/scene/plugins/blender/particles/ParticlesHelper.java

@@ -18,179 +18,179 @@ import com.jme3.scene.plugins.blender.file.Structure;
 import java.util.logging.Logger;
 
 public class ParticlesHelper extends AbstractBlenderHelper {
-	private static final Logger			LOGGER		= Logger.getLogger(ParticlesHelper.class.getName());
-	
-	// part->type
-	public static final int PART_EMITTER	=	0;
-	public static final int PART_REACTOR	=	1;
-	public static final int PART_HAIR		=	2;
-	public static final int PART_FLUID		=	3;
-	
-	// part->flag
-	public static final int PART_REACT_STA_END	=1;
-	public static final int PART_REACT_MULTIPLE	=2;
-	public static final int PART_LOOP			=4;
-	//public static final int PART_LOOP_INSTANT	=8;
-	public static final int PART_HAIR_GEOMETRY	=16;
-	public static final int PART_UNBORN			=32;		//show unborn particles
-	public static final int PART_DIED			=64;		//show died particles
-	public static final int PART_TRAND			=128;	
-	public static final int PART_EDISTR			=256;		// particle/face from face areas
-	public static final int PART_STICKY			=512;		//collided particles can stick to collider
-	public static final int PART_DIE_ON_COL		=1<<12;
-	public static final int PART_SIZE_DEFL		=1<<13; 	// swept sphere deflections
-	public static final int PART_ROT_DYN		=1<<14;	// dynamic rotation
-	public static final int PART_SIZEMASS		=1<<16;
-	public static final int PART_ABS_LENGTH		=1<<15;
-	public static final int PART_ABS_TIME		=1<<17;
-	public static final int PART_GLOB_TIME		=1<<18;
-	public static final int PART_BOIDS_2D		=1<<19;
-	public static final int PART_BRANCHING		=1<<20;
-	public static final int PART_ANIM_BRANCHING	=1<<21;
-	public static final int PART_SELF_EFFECT	=1<<22;
-	public static final int PART_SYMM_BRANCHING	=1<<24;
-	public static final int PART_HAIR_BSPLINE	=1024;
-	public static final int PART_GRID_INVERT	=1<<26;
-	public static final int PART_CHILD_EFFECT	=1<<27;
-	public static final int PART_CHILD_SEAMS	=1<<28;
-	public static final int PART_CHILD_RENDER	=1<<29;
-	public static final int PART_CHILD_GUIDE	=1<<30;
-	
-	// part->from
-	public static final int PART_FROM_VERT		=0;
-	public static final int PART_FROM_FACE		=1;
-	public static final int PART_FROM_VOLUME	=2;
-	public static final int PART_FROM_PARTICLE	=3;
-	public static final int PART_FROM_CHILD		=4;
-	
-	// part->phystype
-	public static final int PART_PHYS_NO	=	0;
-	public static final int PART_PHYS_NEWTON=	1;
-	public static final int PART_PHYS_KEYED	=	2;
-	public static final int PART_PHYS_BOIDS	=	3;
-	
-	// part->draw_as
-	public static final int PART_DRAW_NOT	=	0;
-	public static final int PART_DRAW_DOT	=	1;
-	public static final int PART_DRAW_CIRC	=	2;
-	public static final int PART_DRAW_CROSS	=	3;
-	public static final int PART_DRAW_AXIS	=	4;
-	public static final int PART_DRAW_LINE	=	5;
-	public static final int PART_DRAW_PATH	=	6;
-	public static final int PART_DRAW_OB	=	7;
-	public static final int PART_DRAW_GR	=	8;
-	public static final int PART_DRAW_BB	=	9;
-	
-	/**
-	 * This constructor parses the given blender version and stores the result. Some functionalities may differ in
-	 * different blender versions.
-	 * @param blenderVersion
-	 *        the version read from the blend file
-	 * @param fixUpAxis
-     *        a variable that indicates if the Y asxis is the UP axis or not
-	 */
-	public ParticlesHelper(String blenderVersion, boolean fixUpAxis) {
-		super(blenderVersion, fixUpAxis);
-	}
-
-	@SuppressWarnings("unchecked")
-	public ParticleEmitter toParticleEmitter(Structure particleSystem, BlenderContext blenderContext) throws BlenderFileException {
-		ParticleEmitter result = null;
-		Pointer pParticleSettings = (Pointer) particleSystem.getFieldValue("part");
-		if(pParticleSettings.isNotNull()) {
-			Structure particleSettings = pParticleSettings.fetchData(blenderContext.getInputStream()).get(0);
-			
-			int totPart = ((Number) particleSettings.getFieldValue("totpart")).intValue();
-			
-			//draw type will be stored temporarily in the name (it is used during modifier applying operation)
-			int drawAs = ((Number)particleSettings.getFieldValue("draw_as")).intValue();
-			char nameSuffix;//P - point, L - line, N - None, B - Bilboard
-			switch(drawAs) {
-				case PART_DRAW_NOT:
-					nameSuffix = 'N';
-					totPart = 0;//no need to generate particles in this case
-					break;
-				case PART_DRAW_BB:
-					nameSuffix = 'B';
-					break;
-				case PART_DRAW_OB:
-				case PART_DRAW_GR:
-					nameSuffix = 'P';
-					LOGGER.warning("Neither object nor group particles supported yet! Using point representation instead!");//TODO: support groups and aobjects
-					break;
-				case PART_DRAW_LINE:
-					nameSuffix = 'L';
-					LOGGER.warning("Lines not yet supported! Using point representation instead!");//TODO: support lines
-				default://all others are rendered as points in blender
-					nameSuffix = 'P';
-			}
-			result = new ParticleEmitter(particleSettings.getName()+nameSuffix, Type.Triangle, totPart);
-			if(nameSuffix=='N') {
-				return result;//no need to set anything else
-			}
-			
-			//setting the emitters shape (the shapes meshes will be set later during modifier applying operation)
-			int from = ((Number)particleSettings.getFieldValue("from")).intValue();
-			switch(from) {
-				case PART_FROM_VERT:
-					result.setShape(new EmitterMeshVertexShape());
-					break;
-				case PART_FROM_FACE:
-					result.setShape(new EmitterMeshFaceShape());
-					break;
-				case PART_FROM_VOLUME:
-					result.setShape(new EmitterMeshConvexHullShape());
-					break;
-				default:
-					LOGGER.warning("Default shape used! Unknown emitter shape value ('from' parameter: " + from + ')');
-			}
-			
-			//reading acceleration
-			DynamicArray<Number> acc = (DynamicArray<Number>) particleSettings.getFieldValue("acc");
-			result.setGravity(-acc.get(0).floatValue(), -acc.get(1).floatValue(), -acc.get(2).floatValue());
-			
-			//setting the colors
-			result.setEndColor(new ColorRGBA(1f, 1f, 1f, 1f));
-			result.setStartColor(new ColorRGBA(1f, 1f, 1f, 1f));
-			
-			//reading size
-			float sizeFactor = nameSuffix=='B' ? 1.0f : 0.3f;
-			float size = ((Number)particleSettings.getFieldValue("size")).floatValue() * sizeFactor;
-			result.setStartSize(size);
-			result.setEndSize(size);
-			
-			//reading lifetime
-			int fps = blenderContext.getBlenderKey().getFps();
-			float lifetime = ((Number)particleSettings.getFieldValue("lifetime")).floatValue() / fps;
-			float randlife = ((Number)particleSettings.getFieldValue("randlife")).floatValue() / fps;
-			result.setLowLife(lifetime * (1.0f - randlife));
-		    result.setHighLife(lifetime);
-		    
-		    //preparing influencer
-		    ParticleInfluencer influencer;
-		    int phystype = ((Number)particleSettings.getFieldValue("phystype")).intValue();
-		    switch(phystype) {
-		    	case PART_PHYS_NEWTON:
-		    		influencer = new NewtonianParticleInfluencer();
-		    		((NewtonianParticleInfluencer)influencer).setNormalVelocity(((Number)particleSettings.getFieldValue("normfac")).floatValue());
-		    		((NewtonianParticleInfluencer)influencer).setVelocityVariation(((Number)particleSettings.getFieldValue("randfac")).floatValue());
-		    		((NewtonianParticleInfluencer)influencer).setSurfaceTangentFactor(((Number)particleSettings.getFieldValue("tanfac")).floatValue());
-		    		((NewtonianParticleInfluencer)influencer).setSurfaceTangentRotation(((Number)particleSettings.getFieldValue("tanphase")).floatValue());
-		    		break;
-		    	case PART_PHYS_BOIDS:
-		    	case PART_PHYS_KEYED://TODO: support other influencers
-		    		LOGGER.warning("Boids and Keyed particles physic not yet supported! Empty influencer used!");
-		    	case PART_PHYS_NO:
-	    		default:
-	    			influencer = new EmptyParticleInfluencer();
-		    }
-		    result.setParticleInfluencer(influencer);
-		}
-		return result;
-	}
-	
-	@Override
-	public boolean shouldBeLoaded(Structure structure, BlenderContext blenderContext) {
-		return true;
-	}
+    private static final Logger LOGGER              = Logger.getLogger(ParticlesHelper.class.getName());
+
+    // part->type
+    public static final int     PART_EMITTER        = 0;
+    public static final int     PART_REACTOR        = 1;
+    public static final int     PART_HAIR           = 2;
+    public static final int     PART_FLUID          = 3;
+
+    // part->flag
+    public static final int     PART_REACT_STA_END  = 1;
+    public static final int     PART_REACT_MULTIPLE = 2;
+    public static final int     PART_LOOP           = 4;
+    // public static final int PART_LOOP_INSTANT =8;
+    public static final int     PART_HAIR_GEOMETRY  = 16;
+    public static final int     PART_UNBORN         = 32;                                               // show unborn particles
+    public static final int     PART_DIED           = 64;                                               // show died particles
+    public static final int     PART_TRAND          = 128;
+    public static final int     PART_EDISTR         = 256;                                              // particle/face from face areas
+    public static final int     PART_STICKY         = 512;                                              // collided particles can stick to collider
+    public static final int     PART_DIE_ON_COL     = 1 << 12;
+    public static final int     PART_SIZE_DEFL      = 1 << 13;                                          // swept sphere deflections
+    public static final int     PART_ROT_DYN        = 1 << 14;                                          // dynamic rotation
+    public static final int     PART_SIZEMASS       = 1 << 16;
+    public static final int     PART_ABS_LENGTH     = 1 << 15;
+    public static final int     PART_ABS_TIME       = 1 << 17;
+    public static final int     PART_GLOB_TIME      = 1 << 18;
+    public static final int     PART_BOIDS_2D       = 1 << 19;
+    public static final int     PART_BRANCHING      = 1 << 20;
+    public static final int     PART_ANIM_BRANCHING = 1 << 21;
+    public static final int     PART_SELF_EFFECT    = 1 << 22;
+    public static final int     PART_SYMM_BRANCHING = 1 << 24;
+    public static final int     PART_HAIR_BSPLINE   = 1024;
+    public static final int     PART_GRID_INVERT    = 1 << 26;
+    public static final int     PART_CHILD_EFFECT   = 1 << 27;
+    public static final int     PART_CHILD_SEAMS    = 1 << 28;
+    public static final int     PART_CHILD_RENDER   = 1 << 29;
+    public static final int     PART_CHILD_GUIDE    = 1 << 30;
+
+    // part->from
+    public static final int     PART_FROM_VERT      = 0;
+    public static final int     PART_FROM_FACE      = 1;
+    public static final int     PART_FROM_VOLUME    = 2;
+    public static final int     PART_FROM_PARTICLE  = 3;
+    public static final int     PART_FROM_CHILD     = 4;
+
+    // part->phystype
+    public static final int     PART_PHYS_NO        = 0;
+    public static final int     PART_PHYS_NEWTON    = 1;
+    public static final int     PART_PHYS_KEYED     = 2;
+    public static final int     PART_PHYS_BOIDS     = 3;
+
+    // part->draw_as
+    public static final int     PART_DRAW_NOT       = 0;
+    public static final int     PART_DRAW_DOT       = 1;
+    public static final int     PART_DRAW_CIRC      = 2;
+    public static final int     PART_DRAW_CROSS     = 3;
+    public static final int     PART_DRAW_AXIS      = 4;
+    public static final int     PART_DRAW_LINE      = 5;
+    public static final int     PART_DRAW_PATH      = 6;
+    public static final int     PART_DRAW_OB        = 7;
+    public static final int     PART_DRAW_GR        = 8;
+    public static final int     PART_DRAW_BB        = 9;
+
+    /**
+     * This constructor parses the given blender version and stores the result. Some functionalities may differ in
+     * different blender versions.
+     * @param blenderVersion
+     *            the version read from the blend file
+     * @param fixUpAxis
+     *            a variable that indicates if the Y asxis is the UP axis or not
+     */
+    public ParticlesHelper(String blenderVersion, boolean fixUpAxis) {
+        super(blenderVersion, fixUpAxis);
+    }
+
+    @SuppressWarnings("unchecked")
+    public ParticleEmitter toParticleEmitter(Structure particleSystem, BlenderContext blenderContext) throws BlenderFileException {
+        ParticleEmitter result = null;
+        Pointer pParticleSettings = (Pointer) particleSystem.getFieldValue("part");
+        if (pParticleSettings.isNotNull()) {
+            Structure particleSettings = pParticleSettings.fetchData(blenderContext.getInputStream()).get(0);
+
+            int totPart = ((Number) particleSettings.getFieldValue("totpart")).intValue();
+
+            // draw type will be stored temporarily in the name (it is used during modifier applying operation)
+            int drawAs = ((Number) particleSettings.getFieldValue("draw_as")).intValue();
+            char nameSuffix;// P - point, L - line, N - None, B - Bilboard
+            switch (drawAs) {
+                case PART_DRAW_NOT:
+                    nameSuffix = 'N';
+                    totPart = 0;// no need to generate particles in this case
+                    break;
+                case PART_DRAW_BB:
+                    nameSuffix = 'B';
+                    break;
+                case PART_DRAW_OB:
+                case PART_DRAW_GR:
+                    nameSuffix = 'P';
+                    LOGGER.warning("Neither object nor group particles supported yet! Using point representation instead!");// TODO: support groups and aobjects
+                    break;
+                case PART_DRAW_LINE:
+                    nameSuffix = 'L';
+                    LOGGER.warning("Lines not yet supported! Using point representation instead!");// TODO: support lines
+                default:// all others are rendered as points in blender
+                    nameSuffix = 'P';
+            }
+            result = new ParticleEmitter(particleSettings.getName() + nameSuffix, Type.Triangle, totPart);
+            if (nameSuffix == 'N') {
+                return result;// no need to set anything else
+            }
+
+            // setting the emitters shape (the shapes meshes will be set later during modifier applying operation)
+            int from = ((Number) particleSettings.getFieldValue("from")).intValue();
+            switch (from) {
+                case PART_FROM_VERT:
+                    result.setShape(new EmitterMeshVertexShape());
+                    break;
+                case PART_FROM_FACE:
+                    result.setShape(new EmitterMeshFaceShape());
+                    break;
+                case PART_FROM_VOLUME:
+                    result.setShape(new EmitterMeshConvexHullShape());
+                    break;
+                default:
+                    LOGGER.warning("Default shape used! Unknown emitter shape value ('from' parameter: " + from + ')');
+            }
+
+            // reading acceleration
+            DynamicArray<Number> acc = (DynamicArray<Number>) particleSettings.getFieldValue("acc");
+            result.setGravity(-acc.get(0).floatValue(), -acc.get(1).floatValue(), -acc.get(2).floatValue());
+
+            // setting the colors
+            result.setEndColor(new ColorRGBA(1f, 1f, 1f, 1f));
+            result.setStartColor(new ColorRGBA(1f, 1f, 1f, 1f));
+
+            // reading size
+            float sizeFactor = nameSuffix == 'B' ? 1.0f : 0.3f;
+            float size = ((Number) particleSettings.getFieldValue("size")).floatValue() * sizeFactor;
+            result.setStartSize(size);
+            result.setEndSize(size);
+
+            // reading lifetime
+            int fps = blenderContext.getBlenderKey().getFps();
+            float lifetime = ((Number) particleSettings.getFieldValue("lifetime")).floatValue() / fps;
+            float randlife = ((Number) particleSettings.getFieldValue("randlife")).floatValue() / fps;
+            result.setLowLife(lifetime * (1.0f - randlife));
+            result.setHighLife(lifetime);
+
+            // preparing influencer
+            ParticleInfluencer influencer;
+            int phystype = ((Number) particleSettings.getFieldValue("phystype")).intValue();
+            switch (phystype) {
+                case PART_PHYS_NEWTON:
+                    influencer = new NewtonianParticleInfluencer();
+                    ((NewtonianParticleInfluencer) influencer).setNormalVelocity(((Number) particleSettings.getFieldValue("normfac")).floatValue());
+                    ((NewtonianParticleInfluencer) influencer).setVelocityVariation(((Number) particleSettings.getFieldValue("randfac")).floatValue());
+                    ((NewtonianParticleInfluencer) influencer).setSurfaceTangentFactor(((Number) particleSettings.getFieldValue("tanfac")).floatValue());
+                    ((NewtonianParticleInfluencer) influencer).setSurfaceTangentRotation(((Number) particleSettings.getFieldValue("tanphase")).floatValue());
+                    break;
+                case PART_PHYS_BOIDS:
+                case PART_PHYS_KEYED:// TODO: support other influencers
+                    LOGGER.warning("Boids and Keyed particles physic not yet supported! Empty influencer used!");
+                case PART_PHYS_NO:
+                default:
+                    influencer = new EmptyParticleInfluencer();
+            }
+            result.setParticleInfluencer(influencer);
+        }
+        return result;
+    }
+
+    @Override
+    public boolean shouldBeLoaded(Structure structure, BlenderContext blenderContext) {
+        return true;
+    }
 }

+ 287 - 287
engine/src/blender/com/jme3/scene/plugins/blender/textures/ColorBand.java

@@ -48,310 +48,310 @@ import java.util.logging.Logger;
  * @author Marcin Roguski (Kaelthas)
  */
 public class ColorBand {
-	private static final Logger	LOGGER			= Logger.getLogger(ColorBand.class.getName());
+    private static final Logger LOGGER       = Logger.getLogger(ColorBand.class.getName());
 
-	// interpolation types
-	public static final int		IPO_LINEAR		= 0;
-	public static final int		IPO_EASE		= 1;
-	public static final int		IPO_BSPLINE		= 2;
-	public static final int		IPO_CARDINAL	= 3;
-	public static final int		IPO_CONSTANT	= 4;
+    // interpolation types
+    public static final int     IPO_LINEAR   = 0;
+    public static final int     IPO_EASE     = 1;
+    public static final int     IPO_BSPLINE  = 2;
+    public static final int     IPO_CARDINAL = 3;
+    public static final int     IPO_CONSTANT = 4;
 
-	private int					cursorsAmount, ipoType;
-	private ColorBandData[]		data;
+    private int                 cursorsAmount, ipoType;
+    private ColorBandData[]     data;
 
-	/**
-	 * Constructor. Loads the data from the given structure.
-         * @param tex
-         * @param blenderContext 
-         */
-	@SuppressWarnings("unchecked")
-	public ColorBand(Structure tex, BlenderContext blenderContext) {
-		int flag = ((Number) tex.getFieldValue("flag")).intValue();
-		if ((flag & GeneratedTexture.TEX_COLORBAND) != 0) {
-			Pointer pColorband = (Pointer) tex.getFieldValue("coba");
-			try {
-				Structure colorbandStructure = pColorband.fetchData(blenderContext.getInputStream()).get(0);
-				this.cursorsAmount = ((Number) colorbandStructure.getFieldValue("tot")).intValue();
-				this.ipoType = ((Number) colorbandStructure.getFieldValue("ipotype")).intValue();
-				this.data = new ColorBandData[this.cursorsAmount];
-				DynamicArray<Structure> data = (DynamicArray<Structure>) colorbandStructure.getFieldValue("data");
-				for (int i = 0; i < this.cursorsAmount; ++i) {
-					this.data[i] = new ColorBandData(data.get(i));
-				}
-			} catch (BlenderFileException e) {
-				LOGGER.log(Level.WARNING, "Cannot fetch the colorband structure. The reason: {0}", e.getLocalizedMessage());
-			}
-		}
-	}
+    /**
+     * Constructor. Loads the data from the given structure.
+     * @param tex
+     * @param blenderContext
+     */
+    @SuppressWarnings("unchecked")
+    public ColorBand(Structure tex, BlenderContext blenderContext) {
+        int flag = ((Number) tex.getFieldValue("flag")).intValue();
+        if ((flag & GeneratedTexture.TEX_COLORBAND) != 0) {
+            Pointer pColorband = (Pointer) tex.getFieldValue("coba");
+            try {
+                Structure colorbandStructure = pColorband.fetchData(blenderContext.getInputStream()).get(0);
+                this.cursorsAmount = ((Number) colorbandStructure.getFieldValue("tot")).intValue();
+                this.ipoType = ((Number) colorbandStructure.getFieldValue("ipotype")).intValue();
+                this.data = new ColorBandData[this.cursorsAmount];
+                DynamicArray<Structure> data = (DynamicArray<Structure>) colorbandStructure.getFieldValue("data");
+                for (int i = 0; i < this.cursorsAmount; ++i) {
+                    this.data[i] = new ColorBandData(data.get(i));
+                }
+            } catch (BlenderFileException e) {
+                LOGGER.log(Level.WARNING, "Cannot fetch the colorband structure. The reason: {0}", e.getLocalizedMessage());
+            }
+        }
+    }
+
+    /**
+     * This method determines if the colorband has any transparencies or is not
+     * transparent at all.
+     * 
+     * @return <b>true</b> if the colorband has transparencies and <b>false</b>
+     *         otherwise
+     */
+    public boolean hasTransparencies() {
+        if (data != null) {
+            for (ColorBandData colorBandData : data) {
+                if (colorBandData.a < 1.0f) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
 
-	/**
-	 * This method determines if the colorband has any transparencies or is not
-	 * transparent at all.
-	 * 
-	 * @return <b>true</b> if the colorband has transparencies and <b>false</b>
-	 *         otherwise
-	 */
-	public boolean hasTransparencies() {
-		if (data != null) {
-			for (ColorBandData colorBandData : data) {
-				if (colorBandData.a < 1.0f) {
-					return true;
-				}
-			}
-		}
-		return false;
-	}
+    /**
+     * This method computes the values of the colorband.
+     * 
+     * @return an array of 1001 elements and each element is float[4] object
+     *         containing rgba values
+     */
+    public float[][] computeValues() {
+        float[][] result = null;
+        if (data != null) {
+            result = new float[1001][4];// 1001 - amount of possible cursor
+                                        // positions; 4 = [r, g, b, a]
 
-	/**
-	 * This method computes the values of the colorband.
-	 * 
-	 * @return an array of 1001 elements and each element is float[4] object
-	 *         containing rgba values
-	 */
-	public float[][] computeValues() {
-		float[][] result = null;
-		if (data != null) {
-			result = new float[1001][4];// 1001 - amount of possible cursor
-										// positions; 4 = [r, g, b, a]
+            if (data.length == 1) {// special case; use only one color for all
+                                   // types of colorband interpolation
+                for (int i = 0; i < result.length; ++i) {
+                    result[i][0] = data[0].r;
+                    result[i][1] = data[0].g;
+                    result[i][2] = data[0].b;
+                    result[i][3] = data[0].a;
+                }
+            } else {
+                int currentCursor = 0;
+                ColorBandData currentData = data[0];
+                ColorBandData nextData = data[0];
+                switch (ipoType) {
+                    case ColorBand.IPO_LINEAR:
+                        float rDiff = 0,
+                        gDiff = 0,
+                        bDiff = 0,
+                        aDiff = 0,
+                        posDiff;
+                        for (int i = 0; i < result.length; ++i) {
+                            posDiff = i - currentData.pos;
+                            result[i][0] = currentData.r + rDiff * posDiff;
+                            result[i][1] = currentData.g + gDiff * posDiff;
+                            result[i][2] = currentData.b + bDiff * posDiff;
+                            result[i][3] = currentData.a + aDiff * posDiff;
+                            if (nextData.pos == i) {
+                                currentData = data[currentCursor++];
+                                if (currentCursor < data.length) {
+                                    nextData = data[currentCursor];
+                                    // calculate differences
+                                    int d = nextData.pos - currentData.pos;
+                                    rDiff = (nextData.r - currentData.r) / d;
+                                    gDiff = (nextData.g - currentData.g) / d;
+                                    bDiff = (nextData.b - currentData.b) / d;
+                                    aDiff = (nextData.a - currentData.a) / d;
+                                } else {
+                                    rDiff = gDiff = bDiff = aDiff = 0;
+                                }
+                            }
+                        }
+                        break;
+                    case ColorBand.IPO_BSPLINE:
+                    case ColorBand.IPO_CARDINAL:
+                        Map<Integer, ColorBandData> cbDataMap = new TreeMap<Integer, ColorBandData>();
+                        for (int i = 0; i < data.length; ++i) {
+                            cbDataMap.put(Integer.valueOf(i), data[i]);
+                        }
 
-			if (data.length == 1) {// special case; use only one color for all
-									// types of colorband interpolation
-				for (int i = 0; i < result.length; ++i) {
-					result[i][0] = data[0].r;
-					result[i][1] = data[0].g;
-					result[i][2] = data[0].b;
-					result[i][3] = data[0].a;
-				}
-			} else {
-				int currentCursor = 0;
-				ColorBandData currentData = data[0];
-				ColorBandData nextData = data[0];
-				switch (ipoType) {
-					case ColorBand.IPO_LINEAR:
-						float rDiff = 0,
-						gDiff = 0,
-						bDiff = 0,
-						aDiff = 0,
-						posDiff;
-						for (int i = 0; i < result.length; ++i) {
-							posDiff = i - currentData.pos;
-							result[i][0] = currentData.r + rDiff * posDiff;
-							result[i][1] = currentData.g + gDiff * posDiff;
-							result[i][2] = currentData.b + bDiff * posDiff;
-							result[i][3] = currentData.a + aDiff * posDiff;
-							if (nextData.pos == i) {
-								currentData = data[currentCursor++];
-								if (currentCursor < data.length) {
-									nextData = data[currentCursor];
-									// calculate differences
-									int d = nextData.pos - currentData.pos;
-									rDiff = (nextData.r - currentData.r) / d;
-									gDiff = (nextData.g - currentData.g) / d;
-									bDiff = (nextData.b - currentData.b) / d;
-									aDiff = (nextData.a - currentData.a) / d;
-								} else {
-									rDiff = gDiff = bDiff = aDiff = 0;
-								}
-							}
-						}
-						break;
-					case ColorBand.IPO_BSPLINE:
-					case ColorBand.IPO_CARDINAL:
-						Map<Integer, ColorBandData> cbDataMap = new TreeMap<Integer, ColorBandData>();
-						for (int i = 0; i < data.length; ++i) {
-							cbDataMap.put(Integer.valueOf(i), data[i]);
-						}
+                        if (data[0].pos == 0) {
+                            cbDataMap.put(Integer.valueOf(-1), data[0]);
+                        } else {
+                            ColorBandData cbData = data[0].clone();
+                            cbData.pos = 0;
+                            cbDataMap.put(Integer.valueOf(-1), cbData);
+                            cbDataMap.put(Integer.valueOf(-2), cbData);
+                        }
 
-						if (data[0].pos == 0) {
-							cbDataMap.put(Integer.valueOf(-1), data[0]);
-						} else {
-							ColorBandData cbData = data[0].clone();
-							cbData.pos = 0;
-							cbDataMap.put(Integer.valueOf(-1), cbData);
-							cbDataMap.put(Integer.valueOf(-2), cbData);
-						}
+                        if (data[data.length - 1].pos == 1000) {
+                            cbDataMap.put(Integer.valueOf(data.length), data[data.length - 1]);
+                        } else {
+                            ColorBandData cbData = data[data.length - 1].clone();
+                            cbData.pos = 1000;
+                            cbDataMap.put(Integer.valueOf(data.length), cbData);
+                            cbDataMap.put(Integer.valueOf(data.length + 1), cbData);
+                        }
 
-						if (data[data.length - 1].pos == 1000) {
-							cbDataMap.put(Integer.valueOf(data.length), data[data.length - 1]);
-						} else {
-							ColorBandData cbData = data[data.length - 1].clone();
-							cbData.pos = 1000;
-							cbDataMap.put(Integer.valueOf(data.length), cbData);
-							cbDataMap.put(Integer.valueOf(data.length + 1), cbData);
-						}
+                        float[] ipoFactors = new float[4];
+                        float f;
 
-						float[] ipoFactors = new float[4];
-						float f;
+                        ColorBandData data0 = this.getColorbandData(currentCursor - 2, cbDataMap);
+                        ColorBandData data1 = this.getColorbandData(currentCursor - 1, cbDataMap);
+                        ColorBandData data2 = this.getColorbandData(currentCursor, cbDataMap);
+                        ColorBandData data3 = this.getColorbandData(currentCursor + 1, cbDataMap);
 
-						ColorBandData data0 = this.getColorbandData(currentCursor - 2, cbDataMap);
-						ColorBandData data1 = this.getColorbandData(currentCursor - 1, cbDataMap);
-						ColorBandData data2 = this.getColorbandData(currentCursor, cbDataMap);
-						ColorBandData data3 = this.getColorbandData(currentCursor + 1, cbDataMap);
+                        for (int i = 0; i < result.length; ++i) {
+                            if (data2.pos != data1.pos) {
+                                f = (i - data2.pos) / (float) (data1.pos - data2.pos);
+                                f = FastMath.clamp(f, 0.0f, 1.0f);
+                            } else {
+                                f = 0.0f;
+                            }
+                            this.getIpoData(f, ipoFactors);
+                            result[i][0] = ipoFactors[3] * data0.r + ipoFactors[2] * data1.r + ipoFactors[1] * data2.r + ipoFactors[0] * data3.r;
+                            result[i][1] = ipoFactors[3] * data0.g + ipoFactors[2] * data1.g + ipoFactors[1] * data2.g + ipoFactors[0] * data3.g;
+                            result[i][2] = ipoFactors[3] * data0.b + ipoFactors[2] * data1.b + ipoFactors[1] * data2.b + ipoFactors[0] * data3.b;
+                            result[i][3] = ipoFactors[3] * data0.a + ipoFactors[2] * data1.a + ipoFactors[1] * data2.a + ipoFactors[0] * data3.a;
+                            result[i][0] = FastMath.clamp(result[i][0], 0.0f, 1.0f);
+                            result[i][1] = FastMath.clamp(result[i][1], 0.0f, 1.0f);
+                            result[i][2] = FastMath.clamp(result[i][2], 0.0f, 1.0f);
+                            result[i][3] = FastMath.clamp(result[i][3], 0.0f, 1.0f);
 
-						for (int i = 0; i < result.length; ++i) {
-							if (data2.pos != data1.pos) {
-								f = (i - data2.pos) / (float) (data1.pos - data2.pos);
-								f = FastMath.clamp(f, 0.0f, 1.0f);
-							} else {
-								f = 0.0f;
-							}
-							this.getIpoData(f, ipoFactors);
-							result[i][0] = ipoFactors[3] * data0.r + ipoFactors[2] * data1.r + ipoFactors[1] * data2.r + ipoFactors[0] * data3.r;
-							result[i][1] = ipoFactors[3] * data0.g + ipoFactors[2] * data1.g + ipoFactors[1] * data2.g + ipoFactors[0] * data3.g;
-							result[i][2] = ipoFactors[3] * data0.b + ipoFactors[2] * data1.b + ipoFactors[1] * data2.b + ipoFactors[0] * data3.b;
-							result[i][3] = ipoFactors[3] * data0.a + ipoFactors[2] * data1.a + ipoFactors[1] * data2.a + ipoFactors[0] * data3.a;
-							result[i][0] = FastMath.clamp(result[i][0], 0.0f, 1.0f);
-							result[i][1] = FastMath.clamp(result[i][1], 0.0f, 1.0f);
-							result[i][2] = FastMath.clamp(result[i][2], 0.0f, 1.0f);
-							result[i][3] = FastMath.clamp(result[i][3], 0.0f, 1.0f);
+                            if (nextData.pos == i) {
+                                ++currentCursor;
+                                data0 = cbDataMap.get(currentCursor - 2);
+                                data1 = cbDataMap.get(currentCursor - 1);
+                                data2 = cbDataMap.get(currentCursor);
+                                data3 = cbDataMap.get(currentCursor + 1);
+                            }
+                        }
+                        break;
+                    case ColorBand.IPO_EASE:
+                        float d,
+                        a,
+                        b,
+                        d2;
+                        for (int i = 0; i < result.length; ++i) {
+                            if (nextData.pos != currentData.pos) {
+                                d = (i - currentData.pos) / (float) (nextData.pos - currentData.pos);
+                                d2 = d * d;
+                                a = 3.0f * d2 - 2.0f * d * d2;
+                                b = 1.0f - a;
+                            } else {
+                                d = a = 0.0f;
+                                b = 1.0f;
+                            }
 
-							if (nextData.pos == i) {
-								++currentCursor;
-								data0 = cbDataMap.get(currentCursor - 2);
-								data1 = cbDataMap.get(currentCursor - 1);
-								data2 = cbDataMap.get(currentCursor);
-								data3 = cbDataMap.get(currentCursor + 1);
-							}
-						}
-						break;
-					case ColorBand.IPO_EASE:
-						float d,
-						a,
-						b,
-						d2;
-						for (int i = 0; i < result.length; ++i) {
-							if (nextData.pos != currentData.pos) {
-								d = (i - currentData.pos) / (float) (nextData.pos - currentData.pos);
-								d2 = d * d;
-								a = 3.0f * d2 - 2.0f * d * d2;
-								b = 1.0f - a;
-							} else {
-								d = a = 0.0f;
-								b = 1.0f;
-							}
+                            result[i][0] = b * currentData.r + a * nextData.r;
+                            result[i][1] = b * currentData.g + a * nextData.g;
+                            result[i][2] = b * currentData.b + a * nextData.b;
+                            result[i][3] = b * currentData.a + a * nextData.a;
+                            if (nextData.pos == i) {
+                                currentData = data[currentCursor++];
+                                if (currentCursor < data.length) {
+                                    nextData = data[currentCursor];
+                                }
+                            }
+                        }
+                        break;
+                    case ColorBand.IPO_CONSTANT:
+                        for (int i = 0; i < result.length; ++i) {
+                            result[i][0] = currentData.r;
+                            result[i][1] = currentData.g;
+                            result[i][2] = currentData.b;
+                            result[i][3] = currentData.a;
+                            if (nextData.pos == i) {
+                                currentData = data[currentCursor++];
+                                if (currentCursor < data.length) {
+                                    nextData = data[currentCursor];
+                                }
+                            }
+                        }
+                        break;
+                    default:
+                        throw new IllegalStateException("Unknown interpolation type: " + ipoType);
+                }
+            }
+        }
+        return result;
+    }
 
-							result[i][0] = b * currentData.r + a * nextData.r;
-							result[i][1] = b * currentData.g + a * nextData.g;
-							result[i][2] = b * currentData.b + a * nextData.b;
-							result[i][3] = b * currentData.a + a * nextData.a;
-							if (nextData.pos == i) {
-								currentData = data[currentCursor++];
-								if (currentCursor < data.length) {
-									nextData = data[currentCursor];
-								}
-							}
-						}
-						break;
-					case ColorBand.IPO_CONSTANT:
-						for (int i = 0; i < result.length; ++i) {
-							result[i][0] = currentData.r;
-							result[i][1] = currentData.g;
-							result[i][2] = currentData.b;
-							result[i][3] = currentData.a;
-							if (nextData.pos == i) {
-								currentData = data[currentCursor++];
-								if (currentCursor < data.length) {
-									nextData = data[currentCursor];
-								}
-							}
-						}
-						break;
-					default:
-						throw new IllegalStateException("Unknown interpolation type: " + ipoType);
-				}
-			}
-		}
-		return result;
-	}
-	
-	private ColorBandData getColorbandData(int index, Map<Integer, ColorBandData> cbDataMap) {
-		ColorBandData result = cbDataMap.get(index);
-		if(result == null) {
-			result = new ColorBandData();
-		}
-		return result;
-	}
+    private ColorBandData getColorbandData(int index, Map<Integer, ColorBandData> cbDataMap) {
+        ColorBandData result = cbDataMap.get(index);
+        if (result == null) {
+            result = new ColorBandData();
+        }
+        return result;
+    }
 
-	/**
-	 * This method returns the data for either B-spline of Cardinal
-	 * interpolation.
-	 * 
-	 * @param d
-	 *            distance factor for the current intensity
-	 * @param ipoFactors
-	 *            table to store the results (size of the table must be at least
-	 *            4)
-	 */
-	private void getIpoData(float d, float[] ipoFactors) {
-		float d2 = d * d;
-		float d3 = d2 * d;
-		if (ipoType == ColorBand.IPO_BSPLINE) {
-			ipoFactors[0] = -0.71f * d3 + 1.42f * d2 - 0.71f * d;
-			ipoFactors[1] = 1.29f * d3 - 2.29f * d2 + 1.0f;
-			ipoFactors[2] = -1.29f * d3 + 1.58f * d2 + 0.71f * d;
-			ipoFactors[3] = 0.71f * d3 - 0.71f * d2;
-		} else if (ipoType == ColorBand.IPO_CARDINAL) {
-			ipoFactors[0] = -0.16666666f * d3 + 0.5f * d2 - 0.5f * d + 0.16666666f;
-			ipoFactors[1] = 0.5f * d3 - d2 + 0.6666666f;
-			ipoFactors[2] = -0.5f * d3 + 0.5f * d2 + 0.5f * d + 0.16666666f;
-			ipoFactors[3] = 0.16666666f * d3;
-		} else {
-			throw new IllegalStateException("Cannot get interpolation data for other colorband types than B-spline and Cardinal!");
-		}
-	}
+    /**
+     * This method returns the data for either B-spline of Cardinal
+     * interpolation.
+     * 
+     * @param d
+     *            distance factor for the current intensity
+     * @param ipoFactors
+     *            table to store the results (size of the table must be at least
+     *            4)
+     */
+    private void getIpoData(float d, float[] ipoFactors) {
+        float d2 = d * d;
+        float d3 = d2 * d;
+        if (ipoType == ColorBand.IPO_BSPLINE) {
+            ipoFactors[0] = -0.71f * d3 + 1.42f * d2 - 0.71f * d;
+            ipoFactors[1] = 1.29f * d3 - 2.29f * d2 + 1.0f;
+            ipoFactors[2] = -1.29f * d3 + 1.58f * d2 + 0.71f * d;
+            ipoFactors[3] = 0.71f * d3 - 0.71f * d2;
+        } else if (ipoType == ColorBand.IPO_CARDINAL) {
+            ipoFactors[0] = -0.16666666f * d3 + 0.5f * d2 - 0.5f * d + 0.16666666f;
+            ipoFactors[1] = 0.5f * d3 - d2 + 0.6666666f;
+            ipoFactors[2] = -0.5f * d3 + 0.5f * d2 + 0.5f * d + 0.16666666f;
+            ipoFactors[3] = 0.16666666f * d3;
+        } else {
+            throw new IllegalStateException("Cannot get interpolation data for other colorband types than B-spline and Cardinal!");
+        }
+    }
 
-	/**
-	 * Class to store the single colorband cursor data.
-	 * 
-	 * @author Marcin Roguski (Kaelthas)
-	 */
-	private static class ColorBandData implements Cloneable {
-		public final float	r, g, b, a;
-		public int			pos;
+    /**
+     * Class to store the single colorband cursor data.
+     * 
+     * @author Marcin Roguski (Kaelthas)
+     */
+    private static class ColorBandData implements Cloneable {
+        public final float r, g, b, a;
+        public int         pos;
 
-		public ColorBandData() {
-			r = g = b = 0;
-			a = 1;
-		}
-		
-		/**
-		 * Copy constructor.
-		 */
-		private ColorBandData(ColorBandData data) {
-			this.r = data.r;
-			this.g = data.g;
-			this.b = data.b;
-			this.a = data.a;
-			this.pos = data.pos;
-		}
+        public ColorBandData() {
+            r = g = b = 0;
+            a = 1;
+        }
 
-		/**
-		 * Constructor. Loads the data from the given structure.
-		 * 
-		 * @param cbdataStructure
-		 *            the structure containing the CBData object
-		 */
-		public ColorBandData(Structure cbdataStructure) {
-			this.r = ((Number) cbdataStructure.getFieldValue("r")).floatValue();
-			this.g = ((Number) cbdataStructure.getFieldValue("g")).floatValue();
-			this.b = ((Number) cbdataStructure.getFieldValue("b")).floatValue();
-			this.a = ((Number) cbdataStructure.getFieldValue("a")).floatValue();
-			this.pos = (int) (((Number) cbdataStructure.getFieldValue("pos")).floatValue() * 1000.0f);
-		}
+        /**
+         * Copy constructor.
+         */
+        private ColorBandData(ColorBandData data) {
+            this.r = data.r;
+            this.g = data.g;
+            this.b = data.b;
+            this.a = data.a;
+            this.pos = data.pos;
+        }
+
+        /**
+         * Constructor. Loads the data from the given structure.
+         * 
+         * @param cbdataStructure
+         *            the structure containing the CBData object
+         */
+        public ColorBandData(Structure cbdataStructure) {
+            this.r = ((Number) cbdataStructure.getFieldValue("r")).floatValue();
+            this.g = ((Number) cbdataStructure.getFieldValue("g")).floatValue();
+            this.b = ((Number) cbdataStructure.getFieldValue("b")).floatValue();
+            this.a = ((Number) cbdataStructure.getFieldValue("a")).floatValue();
+            this.pos = (int) (((Number) cbdataStructure.getFieldValue("pos")).floatValue() * 1000.0f);
+        }
 
-		@Override
-		public ColorBandData clone() {
-			try {
-				return (ColorBandData) super.clone();
-			} catch (CloneNotSupportedException e) {
-				return new ColorBandData(this);
-			}
-		}
+        @Override
+        public ColorBandData clone() {
+            try {
+                return (ColorBandData) super.clone();
+            } catch (CloneNotSupportedException e) {
+                return new ColorBandData(this);
+            }
+        }
 
-		@Override
-		public String toString() {
-			return "P: " + this.pos + " [" + this.r + ", " + this.g + ", " + this.b + ", " + this.a + "]";
-		}
-	}
+        @Override
+        public String toString() {
+            return "P: " + this.pos + " [" + this.r + ", " + this.g + ", " + this.b + ", " + this.a + "]";
+        }
+    }
 }

+ 507 - 508
engine/src/blender/com/jme3/scene/plugins/blender/textures/CombinedTexture.java

@@ -40,512 +40,511 @@ import com.jme3.texture.Texture2D;
  * @author Marcin Roguski (Kaelthas)
  */
 public class CombinedTexture {
-	private static final Logger LOGGER = Logger.getLogger(CombinedTexture.class.getName());
-	
-	/** The mapping type of the texture. Defined bu MaterialContext.MTEX_COL, MTEX_NOR etc. */
-	private final int mappingType;
-	/** The data for each of the textures. */
-	private List<TextureData>	textureDatas	= new ArrayList<TextureData>();
-	/** The variable indicates if the texture was already triangulated or not. */
-	private boolean				wasTriangulated;
-	/** The result texture. */
-	private Texture				resultTexture;
-	/** The UV values for the result texture. */
-	private List<Vector2f>		resultUVS;
-
-	/**
-	 * Constructor. Stores the texture mapping type (ie. color map, normal map).
-	 * 
-	 * @param mappingType
-	 *            texture mapping type
-	 */
-	public CombinedTexture(int mappingType) {
-		this.mappingType = mappingType;
-	}
-	
-	/**
-	 * This method adds a texture data to the resulting texture.
-	 * 
-	 * @param texture
-	 *            the source texture
-	 * @param textureBlender
-	 *            the texture blender (to mix the texture with its material
-	 *            color)
-	 * @param uvCoordinatesType
-	 *            the type of UV coordinates
-	 * @param projectionType
-	 *            the type of UV coordinates projection (for flat textures)
-	 * @param textureStructure
-	 *            the texture sructure
-	 * @param blenderContext
-	 *            the blender context
-	 */
-	public void add(Texture texture, TextureBlender textureBlender, int uvCoordinatesType, int projectionType, Structure textureStructure, BlenderContext blenderContext) {
-		if (!(texture instanceof GeneratedTexture) && !(texture instanceof Texture2D)) {
-			throw new IllegalArgumentException("Unsupported texture type: " + (texture == null ? "null" : texture.getClass()));
-		}
-		if(!(texture instanceof GeneratedTexture) || blenderContext.getBlenderKey().isLoadGeneratedTextures()) {
-			if(UVCoordinatesGenerator.isTextureCoordinateTypeSupported(UVCoordinatesType.valueOf(uvCoordinatesType))) {
-				TextureData textureData = new TextureData();
-				textureData.texture = texture;
-				textureData.textureBlender = textureBlender;
-				textureData.uvCoordinatesType = UVCoordinatesType.valueOf(uvCoordinatesType);
-				textureData.projectionType = UVProjectionType.valueOf(projectionType);
-				textureData.textureStructure = textureStructure;
-	
-				if (textureDatas.size() > 0 && this.isWithoutAlpha(textureData, blenderContext)) {
-					textureDatas.clear();// clear previous textures, they will be covered anyway
-				}
-				textureDatas.add(textureData);
-			} else {
-				LOGGER.warning("The texture coordinates type is not supported: " + UVCoordinatesType.valueOf(uvCoordinatesType) + ". The texture '" + textureStructure.getName() + "'.");
-			}
-		}
-	}
-
-	/**
-	 * This method flattens the texture and creates a single result of Texture2D
-	 * type.
-	 * 
-	 * @param geometry
-	 *            the geometry the texture is created for
-	 * @param geometriesOMA
-	 *            the old memory address of the geometries list that the given
-	 *            geometry belongs to (needed for bounding box creation)
-	 * @param userDefinedUVCoordinates
-	 *            the UV's defined by user (null or zero length table if none
-	 *            were defined)
-	 * @param blenderContext
-	 *            the blender context
-	 */
-	@SuppressWarnings("unchecked")
-	public void flatten(Geometry geometry, Long geometriesOMA, List<Vector2f> userDefinedUVCoordinates, BlenderContext blenderContext) {
-		TextureHelper textureHelper = blenderContext.getHelper(TextureHelper.class);
-		Mesh mesh = geometry.getMesh();
-		Texture previousTexture = null;
-		UVCoordinatesType masterUVCoordinatesType = null;
-		for (TextureData textureData : textureDatas) {
-			// decompress compressed textures (all will be merged into one texture anyway)
-			if (textureDatas.size() > 1 && textureData.texture.getImage().getFormat().isCompressed()) {
-				textureData.texture.setImage(textureHelper.decompress(textureData.texture.getImage()));
-				textureData.textureBlender = TextureBlenderFactory.alterTextureType(textureData.texture.getImage().getFormat(), textureData.textureBlender);
-			}
-
-			if (previousTexture == null) {// the first texture will lead the others to its shape
-				if (textureData.texture instanceof GeneratedTexture) {
-					resultTexture = ((GeneratedTexture) textureData.texture).triangulate(mesh, geometriesOMA, textureData.uvCoordinatesType, blenderContext);
-				} else if (textureData.texture instanceof Texture2D) {
-					resultTexture = textureData.texture;
-
-					if(textureData.uvCoordinatesType == UVCoordinatesType.TEXCO_UV && userDefinedUVCoordinates != null && userDefinedUVCoordinates.size() > 0) {
-						resultUVS = userDefinedUVCoordinates;
-					} else {
-						List<Geometry> geometries = (List<Geometry>) blenderContext.getLoadedFeature(geometriesOMA, LoadedFeatureDataType.LOADED_FEATURE);
-						resultUVS = UVCoordinatesGenerator.generateUVCoordinatesFor2DTexture(mesh, textureData.uvCoordinatesType, textureData.projectionType, geometries);
-					}
-				}
-				this.blend(resultTexture, textureData.textureBlender, blenderContext);
-
-				previousTexture = resultTexture;
-				masterUVCoordinatesType = textureData.uvCoordinatesType;
-			} else {
-				if (textureData.texture instanceof GeneratedTexture) {
-					if (!(resultTexture instanceof TriangulatedTexture)) {
-						resultTexture = new TriangulatedTexture((Texture2D) resultTexture, resultUVS, blenderContext);
-						resultUVS = null;
-						previousTexture = resultTexture;
-					}
-
-					TriangulatedTexture triangulatedTexture = ((GeneratedTexture) textureData.texture).triangulate(mesh, geometriesOMA, textureData.uvCoordinatesType, blenderContext);
-					triangulatedTexture.castToUVS((TriangulatedTexture) resultTexture, blenderContext);
-					triangulatedTexture.blend(textureData.textureBlender, (TriangulatedTexture) resultTexture, blenderContext);
-					resultTexture = previousTexture = triangulatedTexture;
-				} else if (textureData.texture instanceof Texture2D) {
-					if (masterUVCoordinatesType == textureData.uvCoordinatesType && resultTexture instanceof Texture2D) {
-						this.scale((Texture2D) textureData.texture, resultTexture.getImage().getWidth(), resultTexture.getImage().getHeight());
-						this.merge((Texture2D) resultTexture, (Texture2D) textureData.texture);
-						previousTexture = resultTexture;
-					} else {
-						if (!(resultTexture instanceof TriangulatedTexture)) {
-							resultTexture = new TriangulatedTexture((Texture2D) resultTexture, resultUVS, blenderContext);
-							resultUVS = null;
-						}
-						// first triangulate the current texture
-						List<Vector2f> textureUVS = null;
-						if(textureData.uvCoordinatesType == UVCoordinatesType.TEXCO_UV && userDefinedUVCoordinates != null && userDefinedUVCoordinates.size() > 0) {
-							textureUVS = userDefinedUVCoordinates;
-						} else {
-							List<Geometry> geometries = (List<Geometry>) blenderContext.getLoadedFeature(geometriesOMA, LoadedFeatureDataType.LOADED_FEATURE);
-							textureUVS = UVCoordinatesGenerator.generateUVCoordinatesFor2DTexture(mesh, textureData.uvCoordinatesType, textureData.projectionType, geometries);
-						}
-						TriangulatedTexture triangulatedTexture = new TriangulatedTexture((Texture2D) textureData.texture, textureUVS, blenderContext);
-						// then move the texture to different UV's
-						triangulatedTexture.castToUVS((TriangulatedTexture) resultTexture, blenderContext);
-						((TriangulatedTexture) resultTexture).merge(triangulatedTexture);
-					}
-				}
-			}
-		}
-
-		if (resultTexture instanceof TriangulatedTexture) {
-			if(mappingType == MaterialContext.MTEX_NOR) {
-				for(int i=0;i<((TriangulatedTexture) resultTexture).getFaceTextureCount();++i) {
-					TriangleTextureElement triangleTextureElement = ((TriangulatedTexture) resultTexture).getFaceTextureElement(i);
-					triangleTextureElement.image = textureHelper.convertToNormalMapTexture(triangleTextureElement.image, 1);//TODO: get proper strength factor
-				}
-			}
-			resultUVS = ((TriangulatedTexture) resultTexture).getResultUVS();
-			resultTexture = ((TriangulatedTexture) resultTexture).getResultTexture();
-			wasTriangulated = true;
-		}
-		
-		// setting additional data
-		resultTexture.setWrap(WrapMode.Repeat);
-		// the filters are required if generated textures are used because
-		// otherwise ugly lines appear between the mesh faces
-		resultTexture.setMagFilter(MagFilter.Nearest);
-		resultTexture.setMinFilter(MinFilter.NearestNoMipMaps);
-	}
-
-	/**
-	 * This method blends the texture.
-	 * 
-	 * @param texture
-	 *            the texture to be blended
-	 * @param textureBlender
-	 *            blending definition for the texture
-	 * @param blenderContext
-	 *            the blender context
-	 */
-	private void blend(Texture texture, TextureBlender textureBlender, BlenderContext blenderContext) {
-		if (texture instanceof TriangulatedTexture) {
-			((TriangulatedTexture) texture).blend(textureBlender, null, blenderContext);
-		} else if (texture instanceof Texture2D) {
-			Image blendedImage = textureBlender.blend(texture.getImage(), null, blenderContext);
-			texture.setImage(blendedImage);
-		} else {
-			throw new IllegalArgumentException("Invalid type for texture to blend!");
-		}
-	}
-
-	/**
-	 * This method casts the current image to the basic UV's owner UV's
-	 * coordinates.
-	 * 
-	 * @param basicUVSOwner
-	 *            the owner of the UV's we cast to
-	 * @param blenderContext
-	 *            the blender context
-	 */
-	public void castToUVS(CombinedTexture basicUVSOwner, BlenderContext blenderContext) {
-		if (resultUVS.size() != basicUVSOwner.resultUVS.size()) {
-			throw new IllegalStateException("The amount of UV coordinates must be equal in order to cast one UV's onto another!");
-		}
-		if (!resultUVS.equals(basicUVSOwner.resultUVS)) {
-			if (!basicUVSOwner.wasTriangulated) {
-				throw new IllegalStateException("The given texture must be triangulated!");
-			}
-			if (!this.wasTriangulated) {
-				resultTexture = new TriangulatedTexture((Texture2D) resultTexture, resultUVS, blenderContext);
-				resultUVS = ((TriangulatedTexture) resultTexture).getResultUVS();
-				resultTexture = ((TriangulatedTexture) resultTexture).getResultTexture();
-			}
-			// casting algorithm
-			TextureHelper textureHelper = blenderContext.getHelper(TextureHelper.class);
-			ImageLoader imageLoader = new ImageLoader();
-			List<TriangleTextureElement> faceTextures = new ArrayList<TriangleTextureElement>();
-			List<Vector2f> basicUVS = basicUVSOwner.getResultUVS();
-			int[] imageRectangle = new int[4];// minX, minY, maxX, maxY
-			int[] sourceSize = new int[2], targetSize = new int[2];// width,
-																	// height
-			Vector2f[] destinationUVS = new Vector2f[3];
-			Vector2f[] sourceUVS = new Vector2f[3];
-			List<Vector2f> partImageUVS = Arrays.asList(new Vector2f(), new Vector2f(), new Vector2f());
-			int faceIndex = 0;
-
-			for (int i = 0; i < basicUVS.size(); i += 3) {
-				// destination size nad UVS
-				destinationUVS[0] = basicUVS.get(i);
-				destinationUVS[1] = basicUVS.get(i + 1);
-				destinationUVS[2] = basicUVS.get(i + 2);
-				this.computeImageRectangle(destinationUVS, imageRectangle, basicUVSOwner.resultTexture.getImage().getWidth(), basicUVSOwner.resultTexture.getImage().getHeight(), blenderContext);
-				targetSize[0] = imageRectangle[2] - imageRectangle[0];
-				targetSize[1] = imageRectangle[3] - imageRectangle[1];
-				for (int j = 0; j < 3; ++j) {
-					partImageUVS.get(j).set((basicUVSOwner.resultTexture.getImage().getWidth() * destinationUVS[j].x - imageRectangle[0]) / targetSize[0],
-							(basicUVSOwner.resultTexture.getImage().getHeight() * destinationUVS[j].y - imageRectangle[1]) / targetSize[1]);
-				}
-
-				// source size and UVS (translate UVS to (0,0) and stretch it to
-				// the borders of the image)
-				sourceUVS[0] = resultUVS.get(i);
-				sourceUVS[1] = resultUVS.get(i + 1);
-				sourceUVS[2] = resultUVS.get(i + 2);
-				this.computeImageRectangle(sourceUVS, imageRectangle, resultTexture.getImage().getWidth(), resultTexture.getImage().getHeight(), blenderContext);
-				sourceSize[0] = imageRectangle[2] - imageRectangle[0];
-				sourceSize[1] = imageRectangle[3] - imageRectangle[1];
-				float xTranslateFactor = imageRectangle[0] / (float) resultTexture.getImage().getWidth();
-				float xStreachFactor = resultTexture.getImage().getWidth() / (float) sourceSize[0];
-				float yTranslateFactor = imageRectangle[1] / (float) resultTexture.getImage().getHeight();
-				float yStreachFactor = resultTexture.getImage().getHeight() / (float) sourceSize[1];
-				for (int j = 0; j < 3; ++j) {
-					sourceUVS[j].x = (sourceUVS[j].x - xTranslateFactor) * xStreachFactor;
-					sourceUVS[j].y = (sourceUVS[j].y - yTranslateFactor) * yStreachFactor;
-				}
-
-				AffineTransform affineTransform = textureHelper.createAffineTransform(sourceUVS, partImageUVS.toArray(new Vector2f[3]), sourceSize, targetSize);
-
-				Image image = textureHelper.getSubimage(resultTexture.getImage(), imageRectangle[0], imageRectangle[1], imageRectangle[2], imageRectangle[3]);
-
-				// compute the result texture
-				BufferedImage sourceImage = ImageToAwt.convert(image, false, true, 0);
-
-				BufferedImage targetImage = new BufferedImage(targetSize[0], targetSize[1], sourceImage.getType());
-				Graphics2D g = targetImage.createGraphics();
-				g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
-				g.drawImage(sourceImage, affineTransform, null);
-				g.dispose();
-
-				Image output = imageLoader.load(targetImage, false);
-				faceTextures.add(new TriangleTextureElement(faceIndex++, output, partImageUVS, false, blenderContext));
-			}
-			TriangulatedTexture triangulatedTexture = new TriangulatedTexture(faceTextures, blenderContext);
-			triangulatedTexture.setKeepIdenticalTextures(false);
-			resultTexture = triangulatedTexture.getResultTexture();
-			resultUVS = basicUVS;
-		}
-	}
-	
-	/**
-	 * This method computes the rectangle of an image constrained by the
-	 * triangle UV coordinates.
-	 * 
-	 * @param triangleVertices
-	 *            the triangle UV coordinates
-	 * @param result
-	 *            the array where the result is stored
-	 * @param totalImageWidth
-	 *            the total image width
-	 * @param totalImageHeight
-	 *            the total image height
-	 * @param blenderContext
-	 *            the blender context
-	 */
-	private void computeImageRectangle(Vector2f[] triangleVertices, int[] result, int totalImageWidth, int totalImageHeight, BlenderContext blenderContext) {
-		TextureHelper textureHelper = blenderContext.getHelper(TextureHelper.class);
-
-		float minX = Math.min(triangleVertices[0].x, triangleVertices[1].x);
-		minX = Math.min(minX, triangleVertices[2].x);
-
-		float maxX = Math.max(triangleVertices[0].x, triangleVertices[1].x);
-		maxX = Math.max(maxX, triangleVertices[2].x);
-
-		float minY = Math.min(triangleVertices[0].y, triangleVertices[1].y);
-		minY = Math.min(minY, triangleVertices[2].y);
-
-		float maxY = Math.max(triangleVertices[0].y, triangleVertices[1].y);
-		maxY = Math.max(maxY, triangleVertices[2].y);
-
-		result[0] = textureHelper.getPixelPosition(minX, totalImageWidth);
-		result[1] = textureHelper.getPixelPosition(minY, totalImageHeight);
-		result[2] = textureHelper.getPixelPosition(maxX, totalImageWidth);
-		result[3] = textureHelper.getPixelPosition(maxY, totalImageHeight);
-	}
-	
-	/**
-	 * @return the result texture
-	 */
-	public Texture getResultTexture() {
-		return resultTexture;
-	}
-
-	/**
-	 * @return the result UV coordinates
-	 */
-	public List<Vector2f> getResultUVS() {
-		return resultUVS;
-	}
-	
-	/**
-	 * @return the amount of added textures
-	 */
-	public int getTexturesCount() {
-		return textureDatas.size();
-	}
-
-	/**
-	 * @return <b>true</b> if the texture has at least one generated texture component and <b>false</b> otherwise
-	 */
-	public boolean hasGeneratedTextures() {
-		if(textureDatas != null) {
-			for(TextureData textureData : textureDatas) {
-				if(textureData.texture instanceof GeneratedTexture) {
-					return true;
-				}
-			}
-		}
-		return false;
-	}
-	
-	/**
-	 * This method merges two given textures. The result is stored in the
-	 * 'target' texture.
-	 * 
-	 * @param target
-	 *            the target texture
-	 * @param source
-	 *            the source texture
-	 */
-	private void merge(Texture2D target, Texture2D source) {
-		if(target.getImage().getDepth() != source.getImage().getDepth()) {
-			throw new IllegalArgumentException("Cannot merge images with different depths!");
-		}
-		Image sourceImage = source.getImage();
-		Image targetImage = target.getImage();
-		PixelInputOutput sourceIO = PixelIOFactory.getPixelIO(sourceImage.getFormat());
-		PixelInputOutput targetIO = PixelIOFactory.getPixelIO(targetImage.getFormat());
-		TexturePixel sourcePixel = new TexturePixel();
-		TexturePixel targetPixel = new TexturePixel();
-		int depth = target.getImage().getDepth() == 0 ? 1 : target.getImage().getDepth();
-		
-		for (int layerIndex = 0; layerIndex < depth; ++layerIndex) {
-			for (int x = 0; x < sourceImage.getWidth(); ++x) {
-				for (int y = 0; y < sourceImage.getHeight(); ++y) {
-					sourceIO.read(sourceImage, layerIndex, sourcePixel, x, y);
-					targetIO.read(targetImage, layerIndex, targetPixel, x, y);
-					targetPixel.merge(sourcePixel);
-					targetIO.write(targetImage, layerIndex, targetPixel, x, y);
-				}
-			}
-		}
-	}
-
-	/**
-	 * This method determines if the given texture has no alpha channel.
-	 * 
-	 * @param texture
-	 *            the texture to check for alpha channel
-	 * @return <b>true</b> if the texture has no alpha channel and <b>false</b>
-	 *         otherwise
-	 */
-	private boolean isWithoutAlpha(TextureData textureData, BlenderContext blenderContext) {
-		ColorBand colorBand = new ColorBand(textureData.textureStructure, blenderContext);
-		if (!colorBand.hasTransparencies()) {
-			int type = ((Number) textureData.textureStructure.getFieldValue("type")).intValue();
-			if (type == TextureHelper.TEX_MAGIC) {
-				return true;
-			}
-			if (type == TextureHelper.TEX_VORONOI) {
-				int voronoiColorType = ((Number) textureData.textureStructure.getFieldValue("vn_coltype")).intValue();
-				return voronoiColorType != 0;// voronoiColorType == 0:
-												// intensity, voronoiColorType
-												// != 0: col1, col2 or col3
-			}
-			if (type == TextureHelper.TEX_CLOUDS) {
-				int sType = ((Number) textureData.textureStructure.getFieldValue("stype")).intValue();
-				return sType == 1;// sType==0: without colors, sType==1: with
-									// colors
-			}
-
-			// checking the flat textures for alpha values presence
-			if (type == TextureHelper.TEX_IMAGE) {
-				Image image = textureData.texture.getImage();
-				switch (image.getFormat()) {
-					case BGR8:
-					case DXT1:
-					case Luminance16:
-					case Luminance16F:
-					case Luminance32F:
-					case Luminance8:
-					case RGB10:
-					case RGB111110F:
-					case RGB16:
-					case RGB16F:
-					case RGB32F:
-					case RGB565:
-					case RGB8:
-						return true;// these types have no alpha by definition
-					case ABGR8:
-					case DXT3:
-					case DXT5:
-					case Luminance16Alpha16:
-					case Luminance16FAlpha16F:
-					case Luminance8Alpha8:
-					case RGBA16:
-					case RGBA16F:
-					case RGBA32F:
-					case RGBA8:// with these types it is better to make sure if the texture is or is not transparent
-						PixelInputOutput pixelInputOutput = PixelIOFactory.getPixelIO(image.getFormat());
-						TexturePixel pixel = new TexturePixel();
-						int depth = image.getDepth() == 0 ? 1 : image.getDepth();
-						for (int layerIndex = 0; layerIndex < depth; ++layerIndex) {
-							for (int x = 0; x < image.getWidth(); ++x) {
-								for (int y = 0; y < image.getHeight(); ++y) {
-									pixelInputOutput.read(image, layerIndex, pixel, x, y);
-									if (pixel.alpha < 1.0f) {
-										return false;
-									}
-								}
-							}
-						}
-						return true;
-				}
-			}
-		}
-		return false;
-	}
-
-	/**
-	 * This method scales the given texture to the given size.
-	 * 
-	 * @param texture
-	 *            the texture to be scaled
-	 * @param width
-	 *            new width of the texture
-	 * @param height
-	 *            new height of the texture
-	 */
-	private void scale(Texture2D texture, int width, int height) {
-		// first determine if scaling is required
-		boolean scaleRequired = texture.getImage().getWidth() != width || texture.getImage().getHeight() != height;
-
-		if (scaleRequired) {
-			Image image = texture.getImage();
-			BufferedImage sourceImage = ImageToAwt.convert(image, false, true, 0);
-
-			int sourceWidth = sourceImage.getWidth();
-			int sourceHeight = sourceImage.getHeight();
-
-			BufferedImage targetImage = new BufferedImage(width, height, sourceImage.getType());
-
-			Graphics2D g = targetImage.createGraphics();
-			g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
-			g.drawImage(sourceImage, 0, 0, width, height, 0, 0, sourceWidth, sourceHeight, null);
-			g.dispose();
-
-			Image output = new ImageLoader().load(targetImage, false);
-			image.setWidth(width);
-			image.setHeight(height);
-			image.setData(output.getData(0));
-			image.setFormat(output.getFormat());
-		}
-	}
-
-	/**
-	 * A simple class to aggregate the texture data (improves code quality).
-	 * 
-	 * @author Marcin Roguski (Kaelthas)
-	 */
-	private static class TextureData {
-		/** The texture. */
-		public Texture				texture;
-		/** The texture blender (to mix the texture with its material color). */
-		public TextureBlender		textureBlender;
-		/** The type of UV coordinates. */
-		public UVCoordinatesType	uvCoordinatesType;
-		/** The type of UV coordinates projection (for flat textures). */
-		public UVProjectionType	projectionType;
-		/** The texture sructure. */
-		public Structure			textureStructure;
-	}
+    private static final Logger LOGGER       = Logger.getLogger(CombinedTexture.class.getName());
+
+    /** The mapping type of the texture. Defined bu MaterialContext.MTEX_COL, MTEX_NOR etc. */
+    private final int           mappingType;
+    /** The data for each of the textures. */
+    private List<TextureData>   textureDatas = new ArrayList<TextureData>();
+    /** The variable indicates if the texture was already triangulated or not. */
+    private boolean             wasTriangulated;
+    /** The result texture. */
+    private Texture             resultTexture;
+    /** The UV values for the result texture. */
+    private List<Vector2f>      resultUVS;
+
+    /**
+     * Constructor. Stores the texture mapping type (ie. color map, normal map).
+     * 
+     * @param mappingType
+     *            texture mapping type
+     */
+    public CombinedTexture(int mappingType) {
+        this.mappingType = mappingType;
+    }
+
+    /**
+     * This method adds a texture data to the resulting texture.
+     * 
+     * @param texture
+     *            the source texture
+     * @param textureBlender
+     *            the texture blender (to mix the texture with its material
+     *            color)
+     * @param uvCoordinatesType
+     *            the type of UV coordinates
+     * @param projectionType
+     *            the type of UV coordinates projection (for flat textures)
+     * @param textureStructure
+     *            the texture sructure
+     * @param blenderContext
+     *            the blender context
+     */
+    public void add(Texture texture, TextureBlender textureBlender, int uvCoordinatesType, int projectionType, Structure textureStructure, BlenderContext blenderContext) {
+        if (!(texture instanceof GeneratedTexture) && !(texture instanceof Texture2D)) {
+            throw new IllegalArgumentException("Unsupported texture type: " + (texture == null ? "null" : texture.getClass()));
+        }
+        if (!(texture instanceof GeneratedTexture) || blenderContext.getBlenderKey().isLoadGeneratedTextures()) {
+            if (UVCoordinatesGenerator.isTextureCoordinateTypeSupported(UVCoordinatesType.valueOf(uvCoordinatesType))) {
+                TextureData textureData = new TextureData();
+                textureData.texture = texture;
+                textureData.textureBlender = textureBlender;
+                textureData.uvCoordinatesType = UVCoordinatesType.valueOf(uvCoordinatesType);
+                textureData.projectionType = UVProjectionType.valueOf(projectionType);
+                textureData.textureStructure = textureStructure;
+
+                if (textureDatas.size() > 0 && this.isWithoutAlpha(textureData, blenderContext)) {
+                    textureDatas.clear();// clear previous textures, they will be covered anyway
+                }
+                textureDatas.add(textureData);
+            } else {
+                LOGGER.warning("The texture coordinates type is not supported: " + UVCoordinatesType.valueOf(uvCoordinatesType) + ". The texture '" + textureStructure.getName() + "'.");
+            }
+        }
+    }
+
+    /**
+     * This method flattens the texture and creates a single result of Texture2D
+     * type.
+     * 
+     * @param geometry
+     *            the geometry the texture is created for
+     * @param geometriesOMA
+     *            the old memory address of the geometries list that the given
+     *            geometry belongs to (needed for bounding box creation)
+     * @param userDefinedUVCoordinates
+     *            the UV's defined by user (null or zero length table if none
+     *            were defined)
+     * @param blenderContext
+     *            the blender context
+     */
+    @SuppressWarnings("unchecked")
+    public void flatten(Geometry geometry, Long geometriesOMA, List<Vector2f> userDefinedUVCoordinates, BlenderContext blenderContext) {
+        TextureHelper textureHelper = blenderContext.getHelper(TextureHelper.class);
+        Mesh mesh = geometry.getMesh();
+        Texture previousTexture = null;
+        UVCoordinatesType masterUVCoordinatesType = null;
+        for (TextureData textureData : textureDatas) {
+            // decompress compressed textures (all will be merged into one texture anyway)
+            if (textureDatas.size() > 1 && textureData.texture.getImage().getFormat().isCompressed()) {
+                textureData.texture.setImage(textureHelper.decompress(textureData.texture.getImage()));
+                textureData.textureBlender = TextureBlenderFactory.alterTextureType(textureData.texture.getImage().getFormat(), textureData.textureBlender);
+            }
+
+            if (previousTexture == null) {// the first texture will lead the others to its shape
+                if (textureData.texture instanceof GeneratedTexture) {
+                    resultTexture = ((GeneratedTexture) textureData.texture).triangulate(mesh, geometriesOMA, textureData.uvCoordinatesType, blenderContext);
+                } else if (textureData.texture instanceof Texture2D) {
+                    resultTexture = textureData.texture;
+
+                    if (textureData.uvCoordinatesType == UVCoordinatesType.TEXCO_UV && userDefinedUVCoordinates != null && userDefinedUVCoordinates.size() > 0) {
+                        resultUVS = userDefinedUVCoordinates;
+                    } else {
+                        List<Geometry> geometries = (List<Geometry>) blenderContext.getLoadedFeature(geometriesOMA, LoadedFeatureDataType.LOADED_FEATURE);
+                        resultUVS = UVCoordinatesGenerator.generateUVCoordinatesFor2DTexture(mesh, textureData.uvCoordinatesType, textureData.projectionType, geometries);
+                    }
+                }
+                this.blend(resultTexture, textureData.textureBlender, blenderContext);
+
+                previousTexture = resultTexture;
+                masterUVCoordinatesType = textureData.uvCoordinatesType;
+            } else {
+                if (textureData.texture instanceof GeneratedTexture) {
+                    if (!(resultTexture instanceof TriangulatedTexture)) {
+                        resultTexture = new TriangulatedTexture((Texture2D) resultTexture, resultUVS, blenderContext);
+                        resultUVS = null;
+                        previousTexture = resultTexture;
+                    }
+
+                    TriangulatedTexture triangulatedTexture = ((GeneratedTexture) textureData.texture).triangulate(mesh, geometriesOMA, textureData.uvCoordinatesType, blenderContext);
+                    triangulatedTexture.castToUVS((TriangulatedTexture) resultTexture, blenderContext);
+                    triangulatedTexture.blend(textureData.textureBlender, (TriangulatedTexture) resultTexture, blenderContext);
+                    resultTexture = previousTexture = triangulatedTexture;
+                } else if (textureData.texture instanceof Texture2D) {
+                    if (masterUVCoordinatesType == textureData.uvCoordinatesType && resultTexture instanceof Texture2D) {
+                        this.scale((Texture2D) textureData.texture, resultTexture.getImage().getWidth(), resultTexture.getImage().getHeight());
+                        this.merge((Texture2D) resultTexture, (Texture2D) textureData.texture);
+                        previousTexture = resultTexture;
+                    } else {
+                        if (!(resultTexture instanceof TriangulatedTexture)) {
+                            resultTexture = new TriangulatedTexture((Texture2D) resultTexture, resultUVS, blenderContext);
+                            resultUVS = null;
+                        }
+                        // first triangulate the current texture
+                        List<Vector2f> textureUVS = null;
+                        if (textureData.uvCoordinatesType == UVCoordinatesType.TEXCO_UV && userDefinedUVCoordinates != null && userDefinedUVCoordinates.size() > 0) {
+                            textureUVS = userDefinedUVCoordinates;
+                        } else {
+                            List<Geometry> geometries = (List<Geometry>) blenderContext.getLoadedFeature(geometriesOMA, LoadedFeatureDataType.LOADED_FEATURE);
+                            textureUVS = UVCoordinatesGenerator.generateUVCoordinatesFor2DTexture(mesh, textureData.uvCoordinatesType, textureData.projectionType, geometries);
+                        }
+                        TriangulatedTexture triangulatedTexture = new TriangulatedTexture((Texture2D) textureData.texture, textureUVS, blenderContext);
+                        // then move the texture to different UV's
+                        triangulatedTexture.castToUVS((TriangulatedTexture) resultTexture, blenderContext);
+                        ((TriangulatedTexture) resultTexture).merge(triangulatedTexture);
+                    }
+                }
+            }
+        }
+
+        if (resultTexture instanceof TriangulatedTexture) {
+            if (mappingType == MaterialContext.MTEX_NOR) {
+                for (int i = 0; i < ((TriangulatedTexture) resultTexture).getFaceTextureCount(); ++i) {
+                    TriangleTextureElement triangleTextureElement = ((TriangulatedTexture) resultTexture).getFaceTextureElement(i);
+                    triangleTextureElement.image = textureHelper.convertToNormalMapTexture(triangleTextureElement.image, 1);// TODO: get proper strength factor
+                }
+            }
+            resultUVS = ((TriangulatedTexture) resultTexture).getResultUVS();
+            resultTexture = ((TriangulatedTexture) resultTexture).getResultTexture();
+            wasTriangulated = true;
+        }
+
+        // setting additional data
+        resultTexture.setWrap(WrapMode.Repeat);
+        // the filters are required if generated textures are used because
+        // otherwise ugly lines appear between the mesh faces
+        resultTexture.setMagFilter(MagFilter.Nearest);
+        resultTexture.setMinFilter(MinFilter.NearestNoMipMaps);
+    }
+
+    /**
+     * This method blends the texture.
+     * 
+     * @param texture
+     *            the texture to be blended
+     * @param textureBlender
+     *            blending definition for the texture
+     * @param blenderContext
+     *            the blender context
+     */
+    private void blend(Texture texture, TextureBlender textureBlender, BlenderContext blenderContext) {
+        if (texture instanceof TriangulatedTexture) {
+            ((TriangulatedTexture) texture).blend(textureBlender, null, blenderContext);
+        } else if (texture instanceof Texture2D) {
+            Image blendedImage = textureBlender.blend(texture.getImage(), null, blenderContext);
+            texture.setImage(blendedImage);
+        } else {
+            throw new IllegalArgumentException("Invalid type for texture to blend!");
+        }
+    }
+
+    /**
+     * This method casts the current image to the basic UV's owner UV's
+     * coordinates.
+     * 
+     * @param basicUVSOwner
+     *            the owner of the UV's we cast to
+     * @param blenderContext
+     *            the blender context
+     */
+    public void castToUVS(CombinedTexture basicUVSOwner, BlenderContext blenderContext) {
+        if (resultUVS.size() != basicUVSOwner.resultUVS.size()) {
+            throw new IllegalStateException("The amount of UV coordinates must be equal in order to cast one UV's onto another!");
+        }
+        if (!resultUVS.equals(basicUVSOwner.resultUVS)) {
+            if (!basicUVSOwner.wasTriangulated) {
+                throw new IllegalStateException("The given texture must be triangulated!");
+            }
+            if (!this.wasTriangulated) {
+                resultTexture = new TriangulatedTexture((Texture2D) resultTexture, resultUVS, blenderContext);
+                resultUVS = ((TriangulatedTexture) resultTexture).getResultUVS();
+                resultTexture = ((TriangulatedTexture) resultTexture).getResultTexture();
+            }
+            // casting algorithm
+            TextureHelper textureHelper = blenderContext.getHelper(TextureHelper.class);
+            ImageLoader imageLoader = new ImageLoader();
+            List<TriangleTextureElement> faceTextures = new ArrayList<TriangleTextureElement>();
+            List<Vector2f> basicUVS = basicUVSOwner.getResultUVS();
+            int[] imageRectangle = new int[4];// minX, minY, maxX, maxY
+            int[] sourceSize = new int[2], targetSize = new int[2];// width,
+                                                                   // height
+            Vector2f[] destinationUVS = new Vector2f[3];
+            Vector2f[] sourceUVS = new Vector2f[3];
+            List<Vector2f> partImageUVS = Arrays.asList(new Vector2f(), new Vector2f(), new Vector2f());
+            int faceIndex = 0;
+
+            for (int i = 0; i < basicUVS.size(); i += 3) {
+                // destination size nad UVS
+                destinationUVS[0] = basicUVS.get(i);
+                destinationUVS[1] = basicUVS.get(i + 1);
+                destinationUVS[2] = basicUVS.get(i + 2);
+                this.computeImageRectangle(destinationUVS, imageRectangle, basicUVSOwner.resultTexture.getImage().getWidth(), basicUVSOwner.resultTexture.getImage().getHeight(), blenderContext);
+                targetSize[0] = imageRectangle[2] - imageRectangle[0];
+                targetSize[1] = imageRectangle[3] - imageRectangle[1];
+                for (int j = 0; j < 3; ++j) {
+                    partImageUVS.get(j).set((basicUVSOwner.resultTexture.getImage().getWidth() * destinationUVS[j].x - imageRectangle[0]) / targetSize[0], (basicUVSOwner.resultTexture.getImage().getHeight() * destinationUVS[j].y - imageRectangle[1]) / targetSize[1]);
+                }
+
+                // source size and UVS (translate UVS to (0,0) and stretch it to
+                // the borders of the image)
+                sourceUVS[0] = resultUVS.get(i);
+                sourceUVS[1] = resultUVS.get(i + 1);
+                sourceUVS[2] = resultUVS.get(i + 2);
+                this.computeImageRectangle(sourceUVS, imageRectangle, resultTexture.getImage().getWidth(), resultTexture.getImage().getHeight(), blenderContext);
+                sourceSize[0] = imageRectangle[2] - imageRectangle[0];
+                sourceSize[1] = imageRectangle[3] - imageRectangle[1];
+                float xTranslateFactor = imageRectangle[0] / (float) resultTexture.getImage().getWidth();
+                float xStreachFactor = resultTexture.getImage().getWidth() / (float) sourceSize[0];
+                float yTranslateFactor = imageRectangle[1] / (float) resultTexture.getImage().getHeight();
+                float yStreachFactor = resultTexture.getImage().getHeight() / (float) sourceSize[1];
+                for (int j = 0; j < 3; ++j) {
+                    sourceUVS[j].x = (sourceUVS[j].x - xTranslateFactor) * xStreachFactor;
+                    sourceUVS[j].y = (sourceUVS[j].y - yTranslateFactor) * yStreachFactor;
+                }
+
+                AffineTransform affineTransform = textureHelper.createAffineTransform(sourceUVS, partImageUVS.toArray(new Vector2f[3]), sourceSize, targetSize);
+
+                Image image = textureHelper.getSubimage(resultTexture.getImage(), imageRectangle[0], imageRectangle[1], imageRectangle[2], imageRectangle[3]);
+
+                // compute the result texture
+                BufferedImage sourceImage = ImageToAwt.convert(image, false, true, 0);
+
+                BufferedImage targetImage = new BufferedImage(targetSize[0], targetSize[1], sourceImage.getType());
+                Graphics2D g = targetImage.createGraphics();
+                g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
+                g.drawImage(sourceImage, affineTransform, null);
+                g.dispose();
+
+                Image output = imageLoader.load(targetImage, false);
+                faceTextures.add(new TriangleTextureElement(faceIndex++, output, partImageUVS, false, blenderContext));
+            }
+            TriangulatedTexture triangulatedTexture = new TriangulatedTexture(faceTextures, blenderContext);
+            triangulatedTexture.setKeepIdenticalTextures(false);
+            resultTexture = triangulatedTexture.getResultTexture();
+            resultUVS = basicUVS;
+        }
+    }
+
+    /**
+     * This method computes the rectangle of an image constrained by the
+     * triangle UV coordinates.
+     * 
+     * @param triangleVertices
+     *            the triangle UV coordinates
+     * @param result
+     *            the array where the result is stored
+     * @param totalImageWidth
+     *            the total image width
+     * @param totalImageHeight
+     *            the total image height
+     * @param blenderContext
+     *            the blender context
+     */
+    private void computeImageRectangle(Vector2f[] triangleVertices, int[] result, int totalImageWidth, int totalImageHeight, BlenderContext blenderContext) {
+        TextureHelper textureHelper = blenderContext.getHelper(TextureHelper.class);
+
+        float minX = Math.min(triangleVertices[0].x, triangleVertices[1].x);
+        minX = Math.min(minX, triangleVertices[2].x);
+
+        float maxX = Math.max(triangleVertices[0].x, triangleVertices[1].x);
+        maxX = Math.max(maxX, triangleVertices[2].x);
+
+        float minY = Math.min(triangleVertices[0].y, triangleVertices[1].y);
+        minY = Math.min(minY, triangleVertices[2].y);
+
+        float maxY = Math.max(triangleVertices[0].y, triangleVertices[1].y);
+        maxY = Math.max(maxY, triangleVertices[2].y);
+
+        result[0] = textureHelper.getPixelPosition(minX, totalImageWidth);
+        result[1] = textureHelper.getPixelPosition(minY, totalImageHeight);
+        result[2] = textureHelper.getPixelPosition(maxX, totalImageWidth);
+        result[3] = textureHelper.getPixelPosition(maxY, totalImageHeight);
+    }
+
+    /**
+     * @return the result texture
+     */
+    public Texture getResultTexture() {
+        return resultTexture;
+    }
+
+    /**
+     * @return the result UV coordinates
+     */
+    public List<Vector2f> getResultUVS() {
+        return resultUVS;
+    }
+
+    /**
+     * @return the amount of added textures
+     */
+    public int getTexturesCount() {
+        return textureDatas.size();
+    }
+
+    /**
+     * @return <b>true</b> if the texture has at least one generated texture component and <b>false</b> otherwise
+     */
+    public boolean hasGeneratedTextures() {
+        if (textureDatas != null) {
+            for (TextureData textureData : textureDatas) {
+                if (textureData.texture instanceof GeneratedTexture) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * This method merges two given textures. The result is stored in the
+     * 'target' texture.
+     * 
+     * @param target
+     *            the target texture
+     * @param source
+     *            the source texture
+     */
+    private void merge(Texture2D target, Texture2D source) {
+        if (target.getImage().getDepth() != source.getImage().getDepth()) {
+            throw new IllegalArgumentException("Cannot merge images with different depths!");
+        }
+        Image sourceImage = source.getImage();
+        Image targetImage = target.getImage();
+        PixelInputOutput sourceIO = PixelIOFactory.getPixelIO(sourceImage.getFormat());
+        PixelInputOutput targetIO = PixelIOFactory.getPixelIO(targetImage.getFormat());
+        TexturePixel sourcePixel = new TexturePixel();
+        TexturePixel targetPixel = new TexturePixel();
+        int depth = target.getImage().getDepth() == 0 ? 1 : target.getImage().getDepth();
+
+        for (int layerIndex = 0; layerIndex < depth; ++layerIndex) {
+            for (int x = 0; x < sourceImage.getWidth(); ++x) {
+                for (int y = 0; y < sourceImage.getHeight(); ++y) {
+                    sourceIO.read(sourceImage, layerIndex, sourcePixel, x, y);
+                    targetIO.read(targetImage, layerIndex, targetPixel, x, y);
+                    targetPixel.merge(sourcePixel);
+                    targetIO.write(targetImage, layerIndex, targetPixel, x, y);
+                }
+            }
+        }
+    }
+
+    /**
+     * This method determines if the given texture has no alpha channel.
+     * 
+     * @param texture
+     *            the texture to check for alpha channel
+     * @return <b>true</b> if the texture has no alpha channel and <b>false</b>
+     *         otherwise
+     */
+    private boolean isWithoutAlpha(TextureData textureData, BlenderContext blenderContext) {
+        ColorBand colorBand = new ColorBand(textureData.textureStructure, blenderContext);
+        if (!colorBand.hasTransparencies()) {
+            int type = ((Number) textureData.textureStructure.getFieldValue("type")).intValue();
+            if (type == TextureHelper.TEX_MAGIC) {
+                return true;
+            }
+            if (type == TextureHelper.TEX_VORONOI) {
+                int voronoiColorType = ((Number) textureData.textureStructure.getFieldValue("vn_coltype")).intValue();
+                return voronoiColorType != 0;// voronoiColorType == 0:
+                                             // intensity, voronoiColorType
+                                             // != 0: col1, col2 or col3
+            }
+            if (type == TextureHelper.TEX_CLOUDS) {
+                int sType = ((Number) textureData.textureStructure.getFieldValue("stype")).intValue();
+                return sType == 1;// sType==0: without colors, sType==1: with
+                                  // colors
+            }
+
+            // checking the flat textures for alpha values presence
+            if (type == TextureHelper.TEX_IMAGE) {
+                Image image = textureData.texture.getImage();
+                switch (image.getFormat()) {
+                    case BGR8:
+                    case DXT1:
+                    case Luminance16:
+                    case Luminance16F:
+                    case Luminance32F:
+                    case Luminance8:
+                    case RGB10:
+                    case RGB111110F:
+                    case RGB16:
+                    case RGB16F:
+                    case RGB32F:
+                    case RGB565:
+                    case RGB8:
+                        return true;// these types have no alpha by definition
+                    case ABGR8:
+                    case DXT3:
+                    case DXT5:
+                    case Luminance16Alpha16:
+                    case Luminance16FAlpha16F:
+                    case Luminance8Alpha8:
+                    case RGBA16:
+                    case RGBA16F:
+                    case RGBA32F:
+                    case RGBA8:// with these types it is better to make sure if the texture is or is not transparent
+                        PixelInputOutput pixelInputOutput = PixelIOFactory.getPixelIO(image.getFormat());
+                        TexturePixel pixel = new TexturePixel();
+                        int depth = image.getDepth() == 0 ? 1 : image.getDepth();
+                        for (int layerIndex = 0; layerIndex < depth; ++layerIndex) {
+                            for (int x = 0; x < image.getWidth(); ++x) {
+                                for (int y = 0; y < image.getHeight(); ++y) {
+                                    pixelInputOutput.read(image, layerIndex, pixel, x, y);
+                                    if (pixel.alpha < 1.0f) {
+                                        return false;
+                                    }
+                                }
+                            }
+                        }
+                        return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * This method scales the given texture to the given size.
+     * 
+     * @param texture
+     *            the texture to be scaled
+     * @param width
+     *            new width of the texture
+     * @param height
+     *            new height of the texture
+     */
+    private void scale(Texture2D texture, int width, int height) {
+        // first determine if scaling is required
+        boolean scaleRequired = texture.getImage().getWidth() != width || texture.getImage().getHeight() != height;
+
+        if (scaleRequired) {
+            Image image = texture.getImage();
+            BufferedImage sourceImage = ImageToAwt.convert(image, false, true, 0);
+
+            int sourceWidth = sourceImage.getWidth();
+            int sourceHeight = sourceImage.getHeight();
+
+            BufferedImage targetImage = new BufferedImage(width, height, sourceImage.getType());
+
+            Graphics2D g = targetImage.createGraphics();
+            g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
+            g.drawImage(sourceImage, 0, 0, width, height, 0, 0, sourceWidth, sourceHeight, null);
+            g.dispose();
+
+            Image output = new ImageLoader().load(targetImage, false);
+            image.setWidth(width);
+            image.setHeight(height);
+            image.setData(output.getData(0));
+            image.setFormat(output.getFormat());
+        }
+    }
+
+    /**
+     * A simple class to aggregate the texture data (improves code quality).
+     * 
+     * @author Marcin Roguski (Kaelthas)
+     */
+    private static class TextureData {
+        /** The texture. */
+        public Texture           texture;
+        /** The texture blender (to mix the texture with its material color). */
+        public TextureBlender    textureBlender;
+        /** The type of UV coordinates. */
+        public UVCoordinatesType uvCoordinatesType;
+        /** The type of UV coordinates projection (for flat textures). */
+        public UVProjectionType  projectionType;
+        /** The texture sructure. */
+        public Structure         textureStructure;
+    }
 }

+ 134 - 134
engine/src/blender/com/jme3/scene/plugins/blender/textures/DDSTexelData.java

@@ -9,149 +9,149 @@ import com.jme3.texture.Image.Format;
  * @author Marcin Roguski (Kaelthas)
  */
 /* package */class DDSTexelData {
-	/** The colors of the texes. */
-	private TexturePixel[][]	colors;
-	/** The indexes of the texels. */
-	private long[]				indexes;
-	/** The alphas of the texels (might be null). */
-	private float[][]			alphas;
-	/** The indexels of texels alpha values (might be null). */
-	private long[]				alphaIndexes;
-	/** The counter of texel x column. */
-	private int					xCounter;
-	/** The counter of texel y row. */
-	private int					yCounter;
-	/** The width of the image in pixels. */
-	private int					widthInPixels;
-	/** The height of the image in pixels. */
-	private int					heightInPixels;
-	/** The total texel count. */
-	private int					xTexelCount;
+    /** The colors of the texes. */
+    private TexturePixel[][] colors;
+    /** The indexes of the texels. */
+    private long[]           indexes;
+    /** The alphas of the texels (might be null). */
+    private float[][]        alphas;
+    /** The indexels of texels alpha values (might be null). */
+    private long[]           alphaIndexes;
+    /** The counter of texel x column. */
+    private int              xCounter;
+    /** The counter of texel y row. */
+    private int              yCounter;
+    /** The width of the image in pixels. */
+    private int              widthInPixels;
+    /** The height of the image in pixels. */
+    private int              heightInPixels;
+    /** The total texel count. */
+    private int              xTexelCount;
 
-	/**
-	 * Constructor. Allocates memory for data structures.
-	 * 
-	 * @param compressedSize
-	 *            the size of compressed image (or its mipmap)
-	 * @param widthToHeightRatio
-	 *            width/height ratio for the image
-	 * @param format
-	 *            the format of the image
-	 */
-	public DDSTexelData(int compressedSize, float widthToHeightRatio, Format format) {
-		int texelsCount = compressedSize * 8 / format.getBitsPerPixel() / 16;
-		this.colors = new TexturePixel[texelsCount][];
-		this.indexes = new long[texelsCount];
-		this.widthInPixels = (int) (0.5f * (float) Math.sqrt(this.getSizeInBytes() / widthToHeightRatio));
-		this.heightInPixels = (int) (this.widthInPixels / widthToHeightRatio);
-		this.xTexelCount = widthInPixels >> 2;
-		this.yCounter = (heightInPixels >> 2) - 1;// xCounter is 0 for now
-		if (format == Format.DXT3 || format == Format.DXT5) {
-			this.alphas = new float[texelsCount][];
-			this.alphaIndexes = new long[texelsCount];
-		}
-	}
+    /**
+     * Constructor. Allocates memory for data structures.
+     * 
+     * @param compressedSize
+     *            the size of compressed image (or its mipmap)
+     * @param widthToHeightRatio
+     *            width/height ratio for the image
+     * @param format
+     *            the format of the image
+     */
+    public DDSTexelData(int compressedSize, float widthToHeightRatio, Format format) {
+        int texelsCount = compressedSize * 8 / format.getBitsPerPixel() / 16;
+        this.colors = new TexturePixel[texelsCount][];
+        this.indexes = new long[texelsCount];
+        this.widthInPixels = (int) (0.5f * (float) Math.sqrt(this.getSizeInBytes() / widthToHeightRatio));
+        this.heightInPixels = (int) (this.widthInPixels / widthToHeightRatio);
+        this.xTexelCount = widthInPixels >> 2;
+        this.yCounter = (heightInPixels >> 2) - 1;// xCounter is 0 for now
+        if (format == Format.DXT3 || format == Format.DXT5) {
+            this.alphas = new float[texelsCount][];
+            this.alphaIndexes = new long[texelsCount];
+        }
+    }
 
-	/**
-	 * This method adds a color and indexes for a texel.
-	 * 
-	 * @param colors
-	 *            the colors of the texel
-	 * @param indexes
-	 *            the indexes of the texel
-	 */
-	public void add(TexturePixel[] colors, int indexes) {
-		this.add(colors, indexes, null, 0);
-	}
+    /**
+     * This method adds a color and indexes for a texel.
+     * 
+     * @param colors
+     *            the colors of the texel
+     * @param indexes
+     *            the indexes of the texel
+     */
+    public void add(TexturePixel[] colors, int indexes) {
+        this.add(colors, indexes, null, 0);
+    }
 
-	/**
-	 * This method adds a color, color indexes and alha values (with their
-	 * indexes) for a texel.
-	 * 
-	 * @param colors
-	 *            the colors of the texel
-	 * @param indexes
-	 *            the indexes of the texel
-	 * @param alphas
-	 *            the alpha values
-	 * @param alphaIndexes
-	 *            the indexes of the given alpha values
-	 */
-	public void add(TexturePixel[] colors, int indexes, float[] alphas, long alphaIndexes) {
-		int index = yCounter * xTexelCount + xCounter;
-		this.colors[index] = colors;
-		this.indexes[index] = indexes;
-		if (alphas != null) {
-			this.alphas[index] = alphas;
-			this.alphaIndexes[index] = alphaIndexes;
-		}
-		++this.xCounter;
-		if (this.xCounter >= this.xTexelCount) {
-			this.xCounter = 0;
-			--this.yCounter;
-		}
-	}
+    /**
+     * This method adds a color, color indexes and alha values (with their
+     * indexes) for a texel.
+     * 
+     * @param colors
+     *            the colors of the texel
+     * @param indexes
+     *            the indexes of the texel
+     * @param alphas
+     *            the alpha values
+     * @param alphaIndexes
+     *            the indexes of the given alpha values
+     */
+    public void add(TexturePixel[] colors, int indexes, float[] alphas, long alphaIndexes) {
+        int index = yCounter * xTexelCount + xCounter;
+        this.colors[index] = colors;
+        this.indexes[index] = indexes;
+        if (alphas != null) {
+            this.alphas[index] = alphas;
+            this.alphaIndexes[index] = alphaIndexes;
+        }
+        ++this.xCounter;
+        if (this.xCounter >= this.xTexelCount) {
+            this.xCounter = 0;
+            --this.yCounter;
+        }
+    }
 
-	/**
-	 * This method returns the values of the pixel located on the given
-	 * coordinates on the result image.
-	 * 
-	 * @param x
-	 *            the x coordinate of the pixel
-	 * @param y
-	 *            the y coordinate of the pixel
-	 * @param result
-	 *            the table where the result is stored
-	 * @return <b>true</b> if the pixel was correctly read and <b>false</b> if
-	 *         the position was outside the image sizes
-	 */
-	public boolean getRGBA8(int x, int y, byte[] result) {
-		int xTexetlIndex = x % widthInPixels / 4;
-		int yTexelIndex = y % heightInPixels / 4;
+    /**
+     * This method returns the values of the pixel located on the given
+     * coordinates on the result image.
+     * 
+     * @param x
+     *            the x coordinate of the pixel
+     * @param y
+     *            the y coordinate of the pixel
+     * @param result
+     *            the table where the result is stored
+     * @return <b>true</b> if the pixel was correctly read and <b>false</b> if
+     *         the position was outside the image sizes
+     */
+    public boolean getRGBA8(int x, int y, byte[] result) {
+        int xTexetlIndex = x % widthInPixels / 4;
+        int yTexelIndex = y % heightInPixels / 4;
 
-		int texelIndex = yTexelIndex * xTexelCount + xTexetlIndex;
-		if (texelIndex < colors.length) {
-			TexturePixel[] colors = this.colors[texelIndex];
+        int texelIndex = yTexelIndex * xTexelCount + xTexetlIndex;
+        if (texelIndex < colors.length) {
+            TexturePixel[] colors = this.colors[texelIndex];
 
-			// coordinates of the pixel in the selected texel
-			x = x - 4 * xTexetlIndex;// pixels are arranged from left to right
-			y = 3 - y - 4 * yTexelIndex;// pixels are arranged from bottom to top (that is why '3 - ...' is at the start)
+            // coordinates of the pixel in the selected texel
+            x = x - 4 * xTexetlIndex;// pixels are arranged from left to right
+            y = 3 - y - 4 * yTexelIndex;// pixels are arranged from bottom to top (that is why '3 - ...' is at the start)
 
-			int pixelIndexInTexel = (y * 4 + x) * (int) FastMath.log(colors.length, 2);
-			int alphaIndexInTexel = alphas != null ? (y * 4 + x) * (int) FastMath.log(alphas.length, 2) : 0;
+            int pixelIndexInTexel = (y * 4 + x) * (int) FastMath.log(colors.length, 2);
+            int alphaIndexInTexel = alphas != null ? (y * 4 + x) * (int) FastMath.log(alphas.length, 2) : 0;
 
-			// getting the pixel
-			int indexMask = colors.length - 1;
-			int colorIndex = (int) (this.indexes[texelIndex] >> pixelIndexInTexel & indexMask);
-			float alpha = this.alphas != null ? this.alphas[texelIndex][(int) (this.alphaIndexes[texelIndex] >> alphaIndexInTexel & 0x07)] : colors[colorIndex].alpha;
-			result[0] = (byte) (colors[colorIndex].red * 255.0f);
-			result[1] = (byte) (colors[colorIndex].green * 255.0f);
-			result[2] = (byte) (colors[colorIndex].blue * 255.0f);
-			result[3] = (byte) (alpha * 255.0f);
-			return true;
-		}
-		return false;
-	}
+            // getting the pixel
+            int indexMask = colors.length - 1;
+            int colorIndex = (int) (this.indexes[texelIndex] >> pixelIndexInTexel & indexMask);
+            float alpha = this.alphas != null ? this.alphas[texelIndex][(int) (this.alphaIndexes[texelIndex] >> alphaIndexInTexel & 0x07)] : colors[colorIndex].alpha;
+            result[0] = (byte) (colors[colorIndex].red * 255.0f);
+            result[1] = (byte) (colors[colorIndex].green * 255.0f);
+            result[2] = (byte) (colors[colorIndex].blue * 255.0f);
+            result[3] = (byte) (alpha * 255.0f);
+            return true;
+        }
+        return false;
+    }
 
-	/**
-	 * @return the size of the decompressed texel (in bytes)
-	 */
-	public int getSizeInBytes() {
-		// indexes.length == count of texels
-		return indexes.length * 16 * 4;
-	}
+    /**
+     * @return the size of the decompressed texel (in bytes)
+     */
+    public int getSizeInBytes() {
+        // indexes.length == count of texels
+        return indexes.length * 16 * 4;
+    }
 
-	/**
-	 * @return image (mipmap) width
-	 */
-	public int getPixelWidth() {
-		return widthInPixels;
-	}
+    /**
+     * @return image (mipmap) width
+     */
+    public int getPixelWidth() {
+        return widthInPixels;
+    }
 
-	/**
-	 * @return image (mipmap) height
-	 */
-	public int getPixelHeight() {
-		return heightInPixels;
-	}
+    /**
+     * @return image (mipmap) height
+     */
+    public int getPixelHeight() {
+        return heightInPixels;
+    }
 }

+ 112 - 112
engine/src/blender/com/jme3/scene/plugins/blender/textures/GeneratedTexture.java

@@ -25,127 +25,127 @@ import java.util.TreeSet;
  * @author Marcin Roguski (Kaelthas)
  */
 /* package */class GeneratedTexture extends Texture {
-	// flag values
-	public static final int			TEX_COLORBAND		= 1;
-	public static final int			TEX_FLIPBLEND		= 2;
-	public static final int			TEX_NEGALPHA		= 4;
-	public static final int			TEX_CHECKER_ODD		= 8;
-	public static final int			TEX_CHECKER_EVEN	= 16;
-	public static final int			TEX_PRV_ALPHA		= 32;
-	public static final int			TEX_PRV_NOR			= 64;
-	public static final int			TEX_REPEAT_XMIR		= 128;
-	public static final int			TEX_REPEAT_YMIR		= 256;
-	public 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;
+    // flag values
+    public static final int        TEX_COLORBAND    = 1;
+    public static final int        TEX_FLIPBLEND    = 2;
+    public static final int        TEX_NEGALPHA     = 4;
+    public static final int        TEX_CHECKER_ODD  = 8;
+    public static final int        TEX_CHECKER_EVEN = 16;
+    public static final int        TEX_PRV_ALPHA    = 32;
+    public static final int        TEX_PRV_NOR      = 64;
+    public static final int        TEX_REPEAT_XMIR  = 128;
+    public static final int        TEX_REPEAT_YMIR  = 256;
+    public 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;
 
-	/** Material-texture link structure. */
-	private final Structure			mTex;
-	/** Texture generateo for the specified texture type. */
-	private final TextureGenerator	textureGenerator;
+    /** Material-texture link structure. */
+    private final Structure        mTex;
+    /** Texture generateo for the specified texture type. */
+    private final TextureGenerator textureGenerator;
 
-	/**
-	 * Constructor. Reads the required data from the 'tex' structure.
-	 * 
-	 * @param tex
-	 *            the texture structure
-	 * @param mTex
-	 *            the material-texture link data structure
-	 * @param textureGenerator
-	 *            the generator for the required texture type
-	 * @param blenderContext
-	 *            the blender context
-	 */
-	public GeneratedTexture(Structure tex, Structure mTex, TextureGenerator textureGenerator, BlenderContext blenderContext) {
-		this.mTex = mTex;
-		this.textureGenerator = textureGenerator;
-		this.textureGenerator.readData(tex, blenderContext);
-		super.setImage(new GeneratedTextureImage(textureGenerator.getImageFormat()));
-	}
+    /**
+     * Constructor. Reads the required data from the 'tex' structure.
+     * 
+     * @param tex
+     *            the texture structure
+     * @param mTex
+     *            the material-texture link data structure
+     * @param textureGenerator
+     *            the generator for the required texture type
+     * @param blenderContext
+     *            the blender context
+     */
+    public GeneratedTexture(Structure tex, Structure mTex, TextureGenerator textureGenerator, BlenderContext blenderContext) {
+        this.mTex = mTex;
+        this.textureGenerator = textureGenerator;
+        this.textureGenerator.readData(tex, blenderContext);
+        super.setImage(new GeneratedTextureImage(textureGenerator.getImageFormat()));
+    }
 
-	/**
-	 * This method computes the textyre color/intensity at the specified (u, v,
-	 * s) position in 3D space.
-	 * 
-	 * @param pixel
-	 *            the pixel where the result is stored
-	 * @param u
-	 *            the U factor
-	 * @param v
-	 *            the V factor
-	 * @param s
-	 *            the S factor
-	 */
-	public void getPixel(TexturePixel pixel, float u, float v, float s) {
-		textureGenerator.getPixel(pixel, u, v, s);
-	}
+    /**
+     * This method computes the textyre color/intensity at the specified (u, v,
+     * s) position in 3D space.
+     * 
+     * @param pixel
+     *            the pixel where the result is stored
+     * @param u
+     *            the U factor
+     * @param v
+     *            the V factor
+     * @param s
+     *            the S factor
+     */
+    public void getPixel(TexturePixel pixel, float u, float v, float s) {
+        textureGenerator.getPixel(pixel, u, v, s);
+    }
 
-	/**
-	 * This method triangulates the texture. In the result we get a set of small
-	 * flat textures for each face of the given mesh. This can be later merged
-	 * into one flat texture.
-	 * 
-	 * @param mesh
-	 *            the mesh we create the texture for
-	 * @param geometriesOMA
-	 *            the old memory address of the geometries group that the given
-	 *            mesh belongs to (required for bounding box calculations)
-	 * @param coordinatesType
-	 *            the types of UV coordinates
-	 * @param blenderContext
-	 *            the blender context
-	 * @return triangulated texture
-	 */
-	@SuppressWarnings("unchecked")
-	public TriangulatedTexture triangulate(Mesh mesh, Long geometriesOMA, UVCoordinatesType coordinatesType, BlenderContext blenderContext) {
-		List<Geometry> geometries = (List<Geometry>) blenderContext.getLoadedFeature(geometriesOMA, LoadedFeatureDataType.LOADED_FEATURE);
+    /**
+     * This method triangulates the texture. In the result we get a set of small
+     * flat textures for each face of the given mesh. This can be later merged
+     * into one flat texture.
+     * 
+     * @param mesh
+     *            the mesh we create the texture for
+     * @param geometriesOMA
+     *            the old memory address of the geometries group that the given
+     *            mesh belongs to (required for bounding box calculations)
+     * @param coordinatesType
+     *            the types of UV coordinates
+     * @param blenderContext
+     *            the blender context
+     * @return triangulated texture
+     */
+    @SuppressWarnings("unchecked")
+    public TriangulatedTexture triangulate(Mesh mesh, Long geometriesOMA, UVCoordinatesType coordinatesType, BlenderContext blenderContext) {
+        List<Geometry> geometries = (List<Geometry>) blenderContext.getLoadedFeature(geometriesOMA, LoadedFeatureDataType.LOADED_FEATURE);
 
-		int[] coordinatesSwappingIndexes = new int[] { ((Number) mTex.getFieldValue("projx")).intValue(), ((Number) mTex.getFieldValue("projy")).intValue(), ((Number) mTex.getFieldValue("projz")).intValue() };
-		List<Vector3f> uvs = UVCoordinatesGenerator.generateUVCoordinatesFor3DTexture(mesh, coordinatesType, coordinatesSwappingIndexes, geometries);
-		Vector3f[] uvsArray = uvs.toArray(new Vector3f[uvs.size()]);
-		BoundingBox boundingBox = UVCoordinatesGenerator.getBoundingBox(geometries);
-		Set<TriangleTextureElement> triangleTextureElements = new TreeSet<TriangleTextureElement>(new Comparator<TriangleTextureElement>() {
-			public int compare(TriangleTextureElement o1, TriangleTextureElement o2) {
-				return o1.faceIndex - o2.faceIndex;
-			}
-		});
-		int[] indices = new int[3];
-		for (int i = 0; i < mesh.getTriangleCount(); ++i) {
-			mesh.getTriangle(i, indices);
-			triangleTextureElements.add(new TriangleTextureElement(i, boundingBox, this, uvsArray, indices, blenderContext));
-		}
-		return new TriangulatedTexture(triangleTextureElements, blenderContext);
-	}
+        int[] coordinatesSwappingIndexes = new int[] { ((Number) mTex.getFieldValue("projx")).intValue(), ((Number) mTex.getFieldValue("projy")).intValue(), ((Number) mTex.getFieldValue("projz")).intValue() };
+        List<Vector3f> uvs = UVCoordinatesGenerator.generateUVCoordinatesFor3DTexture(mesh, coordinatesType, coordinatesSwappingIndexes, geometries);
+        Vector3f[] uvsArray = uvs.toArray(new Vector3f[uvs.size()]);
+        BoundingBox boundingBox = UVCoordinatesGenerator.getBoundingBox(geometries);
+        Set<TriangleTextureElement> triangleTextureElements = new TreeSet<TriangleTextureElement>(new Comparator<TriangleTextureElement>() {
+            public int compare(TriangleTextureElement o1, TriangleTextureElement o2) {
+                return o1.faceIndex - o2.faceIndex;
+            }
+        });
+        int[] indices = new int[3];
+        for (int i = 0; i < mesh.getTriangleCount(); ++i) {
+            mesh.getTriangle(i, indices);
+            triangleTextureElements.add(new TriangleTextureElement(i, boundingBox, this, uvsArray, indices, blenderContext));
+        }
+        return new TriangulatedTexture(triangleTextureElements, blenderContext);
+    }
 
-	@Override
-	public void setWrap(WrapAxis axis, WrapMode mode) {
-	}
+    @Override
+    public void setWrap(WrapAxis axis, WrapMode mode) {
+    }
 
-	@Override
-	public void setWrap(WrapMode mode) {
-	}
+    @Override
+    public void setWrap(WrapMode mode) {
+    }
 
-	@Override
-	public WrapMode getWrap(WrapAxis axis) {
-		return null;
-	}
+    @Override
+    public WrapMode getWrap(WrapAxis axis) {
+        return null;
+    }
 
-	@Override
-	public Type getType() {
-		return Type.ThreeDimensional;
-	}
+    @Override
+    public Type getType() {
+        return Type.ThreeDimensional;
+    }
 
-	@Override
-	public Texture createSimpleClone() {
-		return null;
-	}
+    @Override
+    public Texture createSimpleClone() {
+        return null;
+    }
 
-	/**
-	 * Private class to give the format of the 'virtual' 3D texture image.
-	 * 
-	 * @author Marcin Roguski (Kaelthas)
-	 */
-	private static class GeneratedTextureImage extends Image {
-		public GeneratedTextureImage(Format imageFormat) {
-			super.format = imageFormat;
-		}
-	}
+    /**
+     * Private class to give the format of the 'virtual' 3D texture image.
+     * 
+     * @author Marcin Roguski (Kaelthas)
+     */
+    private static class GeneratedTextureImage extends Image {
+        public GeneratedTextureImage(Format imageFormat) {
+            super.format = imageFormat;
+        }
+    }
 }

+ 82 - 82
engine/src/blender/com/jme3/scene/plugins/blender/textures/ImageLoader.java

@@ -45,91 +45,91 @@ import java.util.logging.Logger;
  * 
  * @author Marcin Roguski (Kaelthas)
  */
-/*package*/ class ImageLoader extends AWTLoader {
-	private static final Logger	LOGGER		= Logger.getLogger(ImageLoader.class.getName());
+/* package */class ImageLoader extends AWTLoader {
+    private static final Logger LOGGER    = Logger.getLogger(ImageLoader.class.getName());
 
-	protected DDSLoader			ddsLoader	= new DDSLoader();									// DirectX image loader
+    protected DDSLoader         ddsLoader = new DDSLoader();                              // DirectX image loader
 
-	/**
-	 * This method loads the image from the blender file itself. It tries each loader to load the image.
-	 * 
-	 * @param inputStream
-	 *        blender input stream
-	 * @param startPosition
-	 *        position in the stream where the image data starts
-	 * @param flipY
-	 *        if the image should be flipped (does not work with DirectX image)
-	 * @return loaded image or null if it could not be loaded
-	 */
-	public Image loadImage(BlenderInputStream inputStream, int startPosition, boolean flipY) {
-		// loading using AWT loader
-		inputStream.setPosition(startPosition);
-		Image result = this.loadImage(inputStream, ImageType.AWT, flipY);
-		// loading using TGA loader
-		if (result == null) {
-			inputStream.setPosition(startPosition);
-			result = this.loadImage(inputStream, ImageType.TGA, flipY);
-		}
-		// loading using DDS loader
-		if (result == null) {
-			inputStream.setPosition(startPosition);
-			result = this.loadImage(inputStream, ImageType.DDS, flipY);
-		}
+    /**
+     * This method loads the image from the blender file itself. It tries each loader to load the image.
+     * 
+     * @param inputStream
+     *            blender input stream
+     * @param startPosition
+     *            position in the stream where the image data starts
+     * @param flipY
+     *            if the image should be flipped (does not work with DirectX image)
+     * @return loaded image or null if it could not be loaded
+     */
+    public Image loadImage(BlenderInputStream inputStream, int startPosition, boolean flipY) {
+        // loading using AWT loader
+        inputStream.setPosition(startPosition);
+        Image result = this.loadImage(inputStream, ImageType.AWT, flipY);
+        // loading using TGA loader
+        if (result == null) {
+            inputStream.setPosition(startPosition);
+            result = this.loadImage(inputStream, ImageType.TGA, flipY);
+        }
+        // loading using DDS loader
+        if (result == null) {
+            inputStream.setPosition(startPosition);
+            result = this.loadImage(inputStream, ImageType.DDS, flipY);
+        }
 
-		if (result == null) {
-			LOGGER.warning("Image could not be loaded by none of available loaders!");
-		}
+        if (result == null) {
+            LOGGER.warning("Image could not be loaded by none of available loaders!");
+        }
 
-		return result;
-	}
+        return result;
+    }
 
-	/**
-	 * This method loads an image of a specified type from the given input stream.
-	 * 
-	 * @param inputStream
-	 *        the input stream we read the image from
-	 * @param imageType
-	 *        the type of the image {@link ImageType}
-	 * @param flipY
-	 *        if the image should be flipped (does not work with DirectX image)
-	 * @return loaded image or null if it could not be loaded
-	 */
-	public Image loadImage(InputStream inputStream, ImageType imageType, boolean flipY) {
-		Image result = null;
-		switch (imageType) {
-			case AWT:
-				try {
-					result = this.load(inputStream, flipY);
-				} catch (Exception e) {
-					LOGGER.warning("Unable to load image using AWT loader!");
-				}
-				break;
-			case DDS:
-				try {
-					result = ddsLoader.load(inputStream);
-				} catch (Exception e) {
-					LOGGER.warning("Unable to load image using DDS loader!");
-				}
-				break;
-			case TGA:
-				try {
-					result = TGALoader.load(inputStream, flipY);
-				} catch (Exception e) {
-					LOGGER.warning("Unable to load image using TGA loader!");
-				}
-				break;
-			default:
-				throw new IllegalStateException("Unknown image type: " + imageType);
-		}
-		return result;
-	}
-	
-	/**
-	 * Image types that can be loaded. AWT: png, jpg, jped or bmp TGA: tga DDS: DirectX image files
-	 * 
-	 * @author Marcin Roguski (Kaelthas)
-	 */
-	private static enum ImageType {
-		AWT, TGA, DDS;
-	}
+    /**
+     * This method loads an image of a specified type from the given input stream.
+     * 
+     * @param inputStream
+     *            the input stream we read the image from
+     * @param imageType
+     *            the type of the image {@link ImageType}
+     * @param flipY
+     *            if the image should be flipped (does not work with DirectX image)
+     * @return loaded image or null if it could not be loaded
+     */
+    public Image loadImage(InputStream inputStream, ImageType imageType, boolean flipY) {
+        Image result = null;
+        switch (imageType) {
+            case AWT:
+                try {
+                    result = this.load(inputStream, flipY);
+                } catch (Exception e) {
+                    LOGGER.warning("Unable to load image using AWT loader!");
+                }
+                break;
+            case DDS:
+                try {
+                    result = ddsLoader.load(inputStream);
+                } catch (Exception e) {
+                    LOGGER.warning("Unable to load image using DDS loader!");
+                }
+                break;
+            case TGA:
+                try {
+                    result = TGALoader.load(inputStream, flipY);
+                } catch (Exception e) {
+                    LOGGER.warning("Unable to load image using TGA loader!");
+                }
+                break;
+            default:
+                throw new IllegalStateException("Unknown image type: " + imageType);
+        }
+        return result;
+    }
+
+    /**
+     * Image types that can be loaded. AWT: png, jpg, jped or bmp TGA: tga DDS: DirectX image files
+     * 
+     * @author Marcin Roguski (Kaelthas)
+     */
+    private static enum ImageType {
+        AWT, TGA, DDS;
+    }
 }

Dosya farkı çok büyük olduğundan ihmal edildi
+ 604 - 606
engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureHelper.java


+ 353 - 353
engine/src/blender/com/jme3/scene/plugins/blender/textures/TexturePixel.java

@@ -9,357 +9,357 @@ import com.jme3.math.FastMath;
  * @author Marcin Roguski (Kaelthas)
  */
 public class TexturePixel implements Cloneable {
-	/** The pixel data. */
-	public float				intensity, red, green, blue, alpha;
-
-	/**
-	 * Copies the values from the given pixel.
-	 * 
-	 * @param pixel
-	 *            the pixel that we read from
-	 */
-	public void fromPixel(TexturePixel pixel) {
-		this.intensity = pixel.intensity;
-		this.red = pixel.red;
-		this.green = pixel.green;
-		this.blue = pixel.blue;
-		this.alpha = pixel.alpha;
-	}
-
-	/**
-	 * Copies the values from the given color.
-	 * 
-	 * @param colorRGBA
-	 *            the color that we read from
-	 */
-	public void fromColor(ColorRGBA colorRGBA) {
-		this.red = colorRGBA.r;
-		this.green = colorRGBA.g;
-		this.blue = colorRGBA.b;
-		this.alpha = colorRGBA.a;
-	}
-
-	/**
-	 * Copies the values from the given values.
-	 * 
-	 * @param a
-	 *            the alpha value
-	 * @param r
-	 *            the red value
-	 * @param g
-	 *            the green value
-	 * @param b
-	 *            the blue value
-	 */
-	public void fromARGB(float a, float r, float g, float b) {
-		this.alpha = a;
-		this.red = r;
-		this.green = g;
-		this.blue = b;
-	}
-
-	/**
-	 * Copies the values from the given values.
-	 * 
-	 * @param a
-	 *            the alpha value
-	 * @param r
-	 *            the red value
-	 * @param g
-	 *            the green value
-	 * @param b
-	 *            the blue value
-	 */
-	public void fromARGB8(byte a, byte r, byte g, byte b) {
-		this.alpha = a >= 0 ? a / 255.0f : 1.0f - ~a / 255.0f;
-		this.red = r >= 0 ? r / 255.0f : 1.0f - ~r / 255.0f;
-		this.green = g >= 0 ? g / 255.0f : 1.0f - ~g / 255.0f;
-		this.blue = b >= 0 ? b / 255.0f : 1.0f - ~b / 255.0f;
-	}
-	
-	/**
-	 * Copies the values from the given values.
-	 * 
-	 * @param a
-	 *            the alpha value
-	 * @param r
-	 *            the red value
-	 * @param g
-	 *            the green value
-	 * @param b
-	 *            the blue value
-	 */
-	public void fromARGB16(short a, short r, short g, short b) {
-		this.alpha = a >= 0 ? a / 65535.0f : 1.0f - ~a / 65535.0f;
-		this.red = r >= 0 ? r / 65535.0f : 1.0f - ~r / 65535.0f;
-		this.green = g >= 0 ? g / 65535.0f : 1.0f - ~g / 65535.0f;
-		this.blue = b >= 0 ? b / 65535.0f : 1.0f - ~b / 65535.0f;
-	}
-	
-	/**
-	 * Copies the intensity from the given value.
-	 * 
-	 * @param intensity
-	 *            the intensity value
-	 */
-	public void fromIntensity(byte intensity) {
-		this.intensity = intensity >= 0 ? intensity / 255.0f : 1.0f - ~intensity / 255.0f;
-	}
-	
-	/**
-	 * Copies the intensity from the given value.
-	 * 
-	 * @param intensity
-	 *            the intensity value
-	 */
-	public void fromIntensity(short intensity) {
-		this.intensity = intensity >= 0 ? intensity / 65535.0f : 1.0f - ~intensity / 65535.0f;
-	}
-	
-	/**
-	 * This method sets the alpha value (converts it to float number from range
-	 * <0, 1>).
-	 * 
-	 * @param alpha
-	 *            the alpha value
-	 */
-	public void setAlpha(byte alpha) {
-		this.alpha = alpha >= 0 ? alpha / 255.0f : 1.0f - ~alpha / 255.0f;
-	}
-
-	/**
-	 * This method sets the alpha value (converts it to float number from range
-	 * <0, 1>).
-	 * 
-	 * @param alpha
-	 *            the alpha value
-	 */
-	public void setAlpha(short alpha) {
-		this.alpha = alpha >= 0 ? alpha / 65535.0f : 1.0f - ~alpha / 65535.0f;
-	}
-	
-	/**
-	 * Copies the values from the given integer that stores the ARGB8 data.
-	 * 
-	 * @param argb8
-	 *            the data stored in an integer
-	 */
-	public void fromARGB8(int argb8) {
-		byte pixelValue = (byte) ((argb8 & 0xFF000000) >> 24);
-		this.alpha = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - ~pixelValue / 255.0f;
-		pixelValue = (byte) ((argb8 & 0xFF0000) >> 16);
-		this.red = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - ~pixelValue / 255.0f;
-		pixelValue = (byte) ((argb8 & 0xFF00) >> 8);
-		this.green = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - ~pixelValue / 255.0f;
-		pixelValue = (byte) (argb8 & 0xFF);
-		this.blue = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - ~pixelValue / 255.0f;
-	}
-	
-	/**
-	 * Stores RGBA values in the given array.
-	 * 
-	 * @param result
-	 *            the array to store values
-	 */
-	public void toRGBA(float[] result) {
-		result[0] = this.red;
-		result[1] = this.green;
-		result[2] = this.blue;
-		result[3] = this.alpha;
-	}
-
-	/**
-	 * Stores the data in the given table.
-	 * 
-	 * @param result
-	 *            the result table
-	 */
-	public void toRGBA8(byte[] result) {
-		result[0] = (byte) (this.red * 255.0f);
-		result[1] = (byte) (this.green * 255.0f);
-		result[2] = (byte) (this.blue * 255.0f);
-		result[3] = (byte) (this.alpha * 255.0f);
-	}
-
-	/**
-	 * Stores the pixel values in the integer.
-	 * 
-	 * @return the integer that stores the pixel values
-	 */
-	public int toARGB8() {
-		int result = 0;
-		int b = (int) (this.alpha * 255.0f);
-		result |= b << 24;
-		b = (int) (this.red * 255.0f);
-		result |= b << 16;
-		b = (int) (this.green * 255.0f);
-		result |= b << 8;
-		b = (int) (this.blue * 255.0f);
-		result |= b;
-		return result;
-	}
-
-	/**
-	 * @return the intensity of the pixel
-	 */
-	public byte getInt() {
-		return (byte) (this.intensity * 255.0f);
-	}
-	
-	/**
-	 * @return the alpha value of the pixel
-	 */
-	public byte getA8() {
-		return (byte) (this.alpha * 255.0f);
-	}
-	
-	/**
-	 * @return the alpha red of the pixel
-	 */
-	public byte getR8() {
-		return (byte) (this.red * 255.0f);
-	}
-	
-	/**
-	 * @return the green value of the pixel
-	 */
-	public byte getG8() {
-		return (byte) (this.green * 255.0f);
-	}
-	
-	/**
-	 * @return the blue value of the pixel
-	 */
-	public byte getB8() {
-		return (byte) (this.blue * 255.0f);
-	}
-	
-	/**
-	 * @return the alpha value of the pixel
-	 */
-	public short getA16() {
-		return (byte) (this.alpha * 65535.0f);
-	}
-	
-	/**
-	 * @return the alpha red of the pixel
-	 */
-	public short getR16() {
-		return (byte) (this.red * 65535.0f);
-	}
-	
-	/**
-	 * @return the green value of the pixel
-	 */
-	public short getG16() {
-		return (byte) (this.green * 65535.0f);
-	}
-	
-	/**
-	 * @return the blue value of the pixel
-	 */
-	public short getB16() {
-		return (byte) (this.blue * 65535.0f);
-	}
-	
-	/**
-	 * Merges two pixels (adds the values of each color).
-	 * 
-	 * @param pixel
-	 *            the pixel we merge with
-	 */
-	public void merge(TexturePixel pixel) {
-		float oneMinusAlpha = 1 - pixel.alpha;
-		this.red = oneMinusAlpha * this.red + pixel.alpha * pixel.red;
-		this.green = oneMinusAlpha * this.green + pixel.alpha * pixel.green;
-		this.blue = oneMinusAlpha * this.blue + pixel.alpha * pixel.blue;
-		this.alpha = (this.alpha + pixel.alpha) * 0.5f;
-	}
-
-	/**
-	 * This method negates the colors.
-	 */
-	public void negate() {
-		this.red = 1.0f - this.red;
-		this.green = 1.0f - this.green;
-		this.blue = 1.0f - this.blue;
-		this.alpha = 1.0f - this.alpha;
-	}
-
-	/**
-	 * This method clears the pixel values.
-	 */
-	public void clear() {
-		this.intensity = this.blue = this.red = this.green = this.alpha = 0.0f;
-	}
-
-	/**
-	 * This method adds the calues of the given pixel to the current pixel.
-	 * 
-	 * @param pixel
-	 *            the pixel we add
-	 */
-	public void add(TexturePixel pixel) {
-		this.red += pixel.red;
-		this.green += pixel.green;
-		this.blue += pixel.blue;
-		this.alpha += pixel.alpha;
-		this.intensity += pixel.intensity;
-	}
-
-	/**
-	 * This method multiplies the values of the given pixel by the given value.
-	 * 
-	 * @param value
-	 *            multiplication factor
-	 */
-	public void mult(float value) {
-		this.red *= value;
-		this.green *= value;
-		this.blue *= value;
-		this.alpha *= value;
-		this.intensity *= value;
-	}
-
-	/**
-	 * This method divides the values of the given pixel by the given value.
-	 * ATTENTION! Beware of the zero value. This will cause you NaN's in the
-	 * pixel values.
-	 * 
-	 * @param value
-	 *            division factor
-	 */
-	public void divide(float value) {
-		this.red /= value;
-		this.green /= value;
-		this.blue /= value;
-		this.alpha /= value;
-		this.intensity /= value;
-	}
-
-	/**
-	 * This method clamps the pixel values to the given borders.
-	 * 
-	 * @param min
-	 *            the minimum value
-	 * @param max
-	 *            the maximum value
-	 */
-	public void clamp(float min, float max) {
-		this.red = FastMath.clamp(this.red, min, max);
-		this.green = FastMath.clamp(this.green, min, max);
-		this.blue = FastMath.clamp(this.blue, min, max);
-		this.alpha = FastMath.clamp(this.alpha, min, max);
-		this.intensity = FastMath.clamp(this.intensity, min, max);
-	}
-
-	@Override
-	public Object clone() throws CloneNotSupportedException {
-		return super.clone();
-	}
-
-	@Override
-	public String toString() {
-		return "[" + red + ", " + green + ", " + blue + ", " + alpha + " {" + intensity + "}]";
-	}
+    /** The pixel data. */
+    public float intensity, red, green, blue, alpha;
+
+    /**
+     * Copies the values from the given pixel.
+     * 
+     * @param pixel
+     *            the pixel that we read from
+     */
+    public void fromPixel(TexturePixel pixel) {
+        this.intensity = pixel.intensity;
+        this.red = pixel.red;
+        this.green = pixel.green;
+        this.blue = pixel.blue;
+        this.alpha = pixel.alpha;
+    }
+
+    /**
+     * Copies the values from the given color.
+     * 
+     * @param colorRGBA
+     *            the color that we read from
+     */
+    public void fromColor(ColorRGBA colorRGBA) {
+        this.red = colorRGBA.r;
+        this.green = colorRGBA.g;
+        this.blue = colorRGBA.b;
+        this.alpha = colorRGBA.a;
+    }
+
+    /**
+     * Copies the values from the given values.
+     * 
+     * @param a
+     *            the alpha value
+     * @param r
+     *            the red value
+     * @param g
+     *            the green value
+     * @param b
+     *            the blue value
+     */
+    public void fromARGB(float a, float r, float g, float b) {
+        this.alpha = a;
+        this.red = r;
+        this.green = g;
+        this.blue = b;
+    }
+
+    /**
+     * Copies the values from the given values.
+     * 
+     * @param a
+     *            the alpha value
+     * @param r
+     *            the red value
+     * @param g
+     *            the green value
+     * @param b
+     *            the blue value
+     */
+    public void fromARGB8(byte a, byte r, byte g, byte b) {
+        this.alpha = a >= 0 ? a / 255.0f : 1.0f - ~a / 255.0f;
+        this.red = r >= 0 ? r / 255.0f : 1.0f - ~r / 255.0f;
+        this.green = g >= 0 ? g / 255.0f : 1.0f - ~g / 255.0f;
+        this.blue = b >= 0 ? b / 255.0f : 1.0f - ~b / 255.0f;
+    }
+
+    /**
+     * Copies the values from the given values.
+     * 
+     * @param a
+     *            the alpha value
+     * @param r
+     *            the red value
+     * @param g
+     *            the green value
+     * @param b
+     *            the blue value
+     */
+    public void fromARGB16(short a, short r, short g, short b) {
+        this.alpha = a >= 0 ? a / 65535.0f : 1.0f - ~a / 65535.0f;
+        this.red = r >= 0 ? r / 65535.0f : 1.0f - ~r / 65535.0f;
+        this.green = g >= 0 ? g / 65535.0f : 1.0f - ~g / 65535.0f;
+        this.blue = b >= 0 ? b / 65535.0f : 1.0f - ~b / 65535.0f;
+    }
+
+    /**
+     * Copies the intensity from the given value.
+     * 
+     * @param intensity
+     *            the intensity value
+     */
+    public void fromIntensity(byte intensity) {
+        this.intensity = intensity >= 0 ? intensity / 255.0f : 1.0f - ~intensity / 255.0f;
+    }
+
+    /**
+     * Copies the intensity from the given value.
+     * 
+     * @param intensity
+     *            the intensity value
+     */
+    public void fromIntensity(short intensity) {
+        this.intensity = intensity >= 0 ? intensity / 65535.0f : 1.0f - ~intensity / 65535.0f;
+    }
+
+    /**
+     * This method sets the alpha value (converts it to float number from range
+     * <0, 1>).
+     * 
+     * @param alpha
+     *            the alpha value
+     */
+    public void setAlpha(byte alpha) {
+        this.alpha = alpha >= 0 ? alpha / 255.0f : 1.0f - ~alpha / 255.0f;
+    }
+
+    /**
+     * This method sets the alpha value (converts it to float number from range
+     * <0, 1>).
+     * 
+     * @param alpha
+     *            the alpha value
+     */
+    public void setAlpha(short alpha) {
+        this.alpha = alpha >= 0 ? alpha / 65535.0f : 1.0f - ~alpha / 65535.0f;
+    }
+
+    /**
+     * Copies the values from the given integer that stores the ARGB8 data.
+     * 
+     * @param argb8
+     *            the data stored in an integer
+     */
+    public void fromARGB8(int argb8) {
+        byte pixelValue = (byte) ((argb8 & 0xFF000000) >> 24);
+        this.alpha = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - ~pixelValue / 255.0f;
+        pixelValue = (byte) ((argb8 & 0xFF0000) >> 16);
+        this.red = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - ~pixelValue / 255.0f;
+        pixelValue = (byte) ((argb8 & 0xFF00) >> 8);
+        this.green = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - ~pixelValue / 255.0f;
+        pixelValue = (byte) (argb8 & 0xFF);
+        this.blue = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - ~pixelValue / 255.0f;
+    }
+
+    /**
+     * Stores RGBA values in the given array.
+     * 
+     * @param result
+     *            the array to store values
+     */
+    public void toRGBA(float[] result) {
+        result[0] = this.red;
+        result[1] = this.green;
+        result[2] = this.blue;
+        result[3] = this.alpha;
+    }
+
+    /**
+     * Stores the data in the given table.
+     * 
+     * @param result
+     *            the result table
+     */
+    public void toRGBA8(byte[] result) {
+        result[0] = (byte) (this.red * 255.0f);
+        result[1] = (byte) (this.green * 255.0f);
+        result[2] = (byte) (this.blue * 255.0f);
+        result[3] = (byte) (this.alpha * 255.0f);
+    }
+
+    /**
+     * Stores the pixel values in the integer.
+     * 
+     * @return the integer that stores the pixel values
+     */
+    public int toARGB8() {
+        int result = 0;
+        int b = (int) (this.alpha * 255.0f);
+        result |= b << 24;
+        b = (int) (this.red * 255.0f);
+        result |= b << 16;
+        b = (int) (this.green * 255.0f);
+        result |= b << 8;
+        b = (int) (this.blue * 255.0f);
+        result |= b;
+        return result;
+    }
+
+    /**
+     * @return the intensity of the pixel
+     */
+    public byte getInt() {
+        return (byte) (this.intensity * 255.0f);
+    }
+
+    /**
+     * @return the alpha value of the pixel
+     */
+    public byte getA8() {
+        return (byte) (this.alpha * 255.0f);
+    }
+
+    /**
+     * @return the alpha red of the pixel
+     */
+    public byte getR8() {
+        return (byte) (this.red * 255.0f);
+    }
+
+    /**
+     * @return the green value of the pixel
+     */
+    public byte getG8() {
+        return (byte) (this.green * 255.0f);
+    }
+
+    /**
+     * @return the blue value of the pixel
+     */
+    public byte getB8() {
+        return (byte) (this.blue * 255.0f);
+    }
+
+    /**
+     * @return the alpha value of the pixel
+     */
+    public short getA16() {
+        return (byte) (this.alpha * 65535.0f);
+    }
+
+    /**
+     * @return the alpha red of the pixel
+     */
+    public short getR16() {
+        return (byte) (this.red * 65535.0f);
+    }
+
+    /**
+     * @return the green value of the pixel
+     */
+    public short getG16() {
+        return (byte) (this.green * 65535.0f);
+    }
+
+    /**
+     * @return the blue value of the pixel
+     */
+    public short getB16() {
+        return (byte) (this.blue * 65535.0f);
+    }
+
+    /**
+     * Merges two pixels (adds the values of each color).
+     * 
+     * @param pixel
+     *            the pixel we merge with
+     */
+    public void merge(TexturePixel pixel) {
+        float oneMinusAlpha = 1 - pixel.alpha;
+        this.red = oneMinusAlpha * this.red + pixel.alpha * pixel.red;
+        this.green = oneMinusAlpha * this.green + pixel.alpha * pixel.green;
+        this.blue = oneMinusAlpha * this.blue + pixel.alpha * pixel.blue;
+        this.alpha = (this.alpha + pixel.alpha) * 0.5f;
+    }
+
+    /**
+     * This method negates the colors.
+     */
+    public void negate() {
+        this.red = 1.0f - this.red;
+        this.green = 1.0f - this.green;
+        this.blue = 1.0f - this.blue;
+        this.alpha = 1.0f - this.alpha;
+    }
+
+    /**
+     * This method clears the pixel values.
+     */
+    public void clear() {
+        this.intensity = this.blue = this.red = this.green = this.alpha = 0.0f;
+    }
+
+    /**
+     * This method adds the calues of the given pixel to the current pixel.
+     * 
+     * @param pixel
+     *            the pixel we add
+     */
+    public void add(TexturePixel pixel) {
+        this.red += pixel.red;
+        this.green += pixel.green;
+        this.blue += pixel.blue;
+        this.alpha += pixel.alpha;
+        this.intensity += pixel.intensity;
+    }
+
+    /**
+     * This method multiplies the values of the given pixel by the given value.
+     * 
+     * @param value
+     *            multiplication factor
+     */
+    public void mult(float value) {
+        this.red *= value;
+        this.green *= value;
+        this.blue *= value;
+        this.alpha *= value;
+        this.intensity *= value;
+    }
+
+    /**
+     * This method divides the values of the given pixel by the given value.
+     * ATTENTION! Beware of the zero value. This will cause you NaN's in the
+     * pixel values.
+     * 
+     * @param value
+     *            division factor
+     */
+    public void divide(float value) {
+        this.red /= value;
+        this.green /= value;
+        this.blue /= value;
+        this.alpha /= value;
+        this.intensity /= value;
+    }
+
+    /**
+     * This method clamps the pixel values to the given borders.
+     * 
+     * @param min
+     *            the minimum value
+     * @param max
+     *            the maximum value
+     */
+    public void clamp(float min, float max) {
+        this.red = FastMath.clamp(this.red, min, max);
+        this.green = FastMath.clamp(this.green, min, max);
+        this.blue = FastMath.clamp(this.blue, min, max);
+        this.alpha = FastMath.clamp(this.alpha, min, max);
+        this.intensity = FastMath.clamp(this.intensity, min, max);
+    }
+
+    @Override
+    public Object clone() throws CloneNotSupportedException {
+        return super.clone();
+    }
+
+    @Override
+    public String toString() {
+        return "[" + red + ", " + green + ", " + blue + ", " + alpha + " {" + intensity + "}]";
+    }
 }

+ 642 - 644
engine/src/blender/com/jme3/scene/plugins/blender/textures/TriangulatedTexture.java

@@ -41,648 +41,646 @@ import com.jme3.util.BufferUtils;
  * @author Marcin Roguski (Kaelthas)
  */
 /* package */class TriangulatedTexture extends Texture2D {
-	/** The result image format. */
-	private Format								format;
-	/** The collection of images for each face. */
-	private Collection<TriangleTextureElement>	faceTextures;
-	/**
-	 * The maximum texture size (width/height). This is taken from the blender
-	 * key.
-	 */
-	private int									maxTextureSize;
-	/** A variable that can prevent removing identical textures. */
-	private boolean								keepIdenticalTextures = false;
-	/** The result texture. */
-	private Texture2D							resultTexture;
-	/** The result texture's UV coordinates. */
-	private List<Vector2f>						resultUVS;
-    
-	/**
-	 * This method triangulates the given flat texture. The given texture is not
-	 * changed.
-	 * 
-	 * @param texture2d
-	 *            the texture to be triangulated
-	 * @param uvs
-	 *            the UV coordinates for each face
-	 */
-	public TriangulatedTexture(Texture2D texture2d, List<Vector2f> uvs, BlenderContext blenderContext) {
-		maxTextureSize = blenderContext.getBlenderKey().getMaxTextureSize();
-		faceTextures = new TreeSet<TriangleTextureElement>(new Comparator<TriangleTextureElement>() {
-			public int compare(TriangleTextureElement o1, TriangleTextureElement o2) {
-				return o1.faceIndex - o2.faceIndex;
-			}
-		});
-		int facesCount = uvs.size() / 3;
-		for (int i = 0; i < facesCount; ++i) {
-			faceTextures.add(new TriangleTextureElement(i, texture2d.getImage(), uvs, true, blenderContext));
-		}
-		this.format = texture2d.getImage().getFormat();
-	}
-
-	/**
-	 * Constructor that simply stores precalculated images.
-	 * 
-	 * @param faceTextures
-	 *            a collection of images for the mesh's faces
-	 * @param blenderContext
-	 *            the blender context
-	 */
-	public TriangulatedTexture(Collection<TriangleTextureElement> faceTextures, BlenderContext blenderContext) {
-		maxTextureSize = blenderContext.getBlenderKey().getMaxTextureSize();
-		this.faceTextures = faceTextures;
-		for (TriangleTextureElement faceTextureElement : faceTextures) {
-			if (format == null) {
-				format = faceTextureElement.image.getFormat();
-			} else if (format != faceTextureElement.image.getFormat()) {
-				throw new IllegalArgumentException("Face texture element images MUST have the same image format!");
-			}
-		}
-	}
-
-	/**
-	 * This method blends the each image using the given blender and taking base
-	 * texture into consideration.
-	 * 
-	 * @param textureBlender
-	 *            the texture blender that holds the blending definition
-	 * @param baseTexture
-	 *            the texture that is 'below' the current texture (can be null)
-	 * @param blenderContext
-	 *            the blender context
-	 */
-	public void blend(TextureBlender textureBlender, TriangulatedTexture baseTexture, BlenderContext blenderContext) {
-		Format newFormat = null;
-		for (TriangleTextureElement triangleTextureElement : this.faceTextures) {
-			Image baseImage = baseTexture == null ? null : baseTexture.getFaceTextureElement(triangleTextureElement.faceIndex).image;
-			triangleTextureElement.image = textureBlender.blend(triangleTextureElement.image, baseImage, blenderContext);
-			if (newFormat == null) {
-				newFormat = triangleTextureElement.image.getFormat();
-			} else if (newFormat != triangleTextureElement.image.getFormat()) {
-				throw new IllegalArgumentException("Face texture element images MUST have the same image format!");
-			}
-		}
-		this.format = newFormat;
-	}
-
-	/**
-	 * This method alters the images to fit them into UV coordinates of the
-	 * given target texture.
-	 * 
-	 * @param targetTexture
-	 *            the texture to whose UV coordinates we fit current images
-	 * @param blenderContext
-	 *            the blender context
-	 */
-	public void castToUVS(TriangulatedTexture targetTexture, BlenderContext blenderContext) {
-		int[] sourceSize = new int[2], targetSize = new int[2];
-		ImageLoader imageLoader = new ImageLoader();
-		TextureHelper textureHelper = blenderContext.getHelper(TextureHelper.class);
-		for (TriangleTextureElement entry : faceTextures) {
-			TriangleTextureElement targetFaceTextureElement = targetTexture.getFaceTextureElement(entry.faceIndex);
-			Vector2f[] dest = targetFaceTextureElement.uv;
-
-			// get the sizes of the source and target images
-			sourceSize[0] = entry.image.getWidth();
-			sourceSize[1] = entry.image.getHeight();
-			targetSize[0] = targetFaceTextureElement.image.getWidth();
-			targetSize[1] = targetFaceTextureElement.image.getHeight();
-
-			// create triangle transformation
-			AffineTransform affineTransform = textureHelper.createAffineTransform(entry.uv, dest, sourceSize, targetSize);
-
-			// compute the result texture
-			BufferedImage sourceImage = ImageToAwt.convert(entry.image, false, true, 0);
-
-			BufferedImage targetImage = new BufferedImage(targetSize[0], targetSize[1], sourceImage.getType());
-			Graphics2D g = targetImage.createGraphics();
-			g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
-			g.drawImage(sourceImage, affineTransform, null);
-			g.dispose();
-
-			Image output = imageLoader.load(targetImage, false);
-			entry.image = output;
-			entry.uv[0].set(dest[0]);
-			entry.uv[1].set(dest[1]);
-			entry.uv[2].set(dest[2]);
-		}
-	}
-
-	/**
-	 * This method merges the current texture with the given one. The given
-	 * texture is not changed.
-	 * 
-	 * @param triangulatedTexture
-	 *            the texture we merge current texture with
-	 */
-	public void merge(TriangulatedTexture triangulatedTexture) {
-		TexturePixel sourcePixel = new TexturePixel();
-		TexturePixel targetPixel = new TexturePixel();
-		for (TriangleTextureElement triangleTextureElement : this.faceTextures) {
-			Image sourceImage = triangulatedTexture.getFaceTextureElement(triangleTextureElement.faceIndex).image;
-			Image targetImage = triangleTextureElement.image;
-			PixelInputOutput sourceIO = PixelIOFactory.getPixelIO(sourceImage.getFormat());
-			PixelInputOutput targetIO = PixelIOFactory.getPixelIO(targetImage.getFormat());
-
-			for (int x = 0; x < sourceImage.getWidth(); ++x) {
-				for (int y = 0; y < sourceImage.getHeight(); ++y) {
-					sourceIO.read(sourceImage, 0, sourcePixel, x, y);
-					targetIO.read(targetImage, 0, targetPixel, x, y);
-					targetPixel.merge(sourcePixel);
-					targetIO.write(targetImage, 0, targetPixel, x, y);
-				}
-			}
-		}
-	}
-
-	/**
-	 * This method returns the flat texture. It is calculated if required or if
-	 * it was not created before. Images that are identical are discarded to
-	 * reduce the texture size.
-	 * 
-	 * @param rebuild
-	 *            a variable that forces texture recomputation (even if it was
-	 *            computed vefore)
-	 * @return flat result texture (all images merged into one)
-	 */
-	public Texture2D getResultTexture(boolean rebuild) {
-		if (resultTexture == null || rebuild) {
-			// sorting the parts by their height (from highest to the lowest)
-			List<TriangleTextureElement> list = new ArrayList<TriangleTextureElement>(faceTextures);
-			Collections.sort(list, new Comparator<TriangleTextureElement>() {
-				public int compare(TriangleTextureElement o1, TriangleTextureElement o2) {
-					return o2.image.getHeight() - o1.image.getHeight();
-				}
-			});
-
-			// arraging the images on the resulting image (calculating the result image width and height)
-			Set<Integer> duplicatedFaceIndexes = new HashSet<Integer>();
-			int resultImageHeight = list.get(0).image.getHeight();
-			int resultImageWidth = 0;
-			int currentXPos = 0, currentYPos = 0;
-			Map<TriangleTextureElement, Integer[]> imageLayoutData = new HashMap<TriangleTextureElement, Integer[]>(list.size());
-			while (list.size() > 0) {
-				TriangleTextureElement currentElement = list.remove(0);
-				if (currentXPos + currentElement.image.getWidth() > maxTextureSize) {
-					currentXPos = 0;
-					currentYPos = resultImageHeight;
-					resultImageHeight += currentElement.image.getHeight();
-				}
-				Integer[] currentPositions = new Integer[] { currentXPos, currentYPos };
-				imageLayoutData.put(currentElement, currentPositions);
-
-				if(keepIdenticalTextures) {// removing identical images
-					for (int i = 0; i < list.size(); ++i) {
-						if (currentElement.image.equals(list.get(i).image)) {
-							duplicatedFaceIndexes.add(list.get(i).faceIndex);
-							imageLayoutData.put(list.remove(i--), currentPositions);
-						}
-					}
-				}
-
-				currentXPos += currentElement.image.getWidth();
-				resultImageWidth = Math.max(resultImageWidth, currentXPos);
-				// currentYPos += currentElement.image.getHeight();
-
-				// TODO: implement that to compact the result image
-				// try to add smaller images below the current one
-				// int remainingHeight = resultImageHeight -
-				// currentElement.image.getHeight();
-				// while(remainingHeight > 0) {
-				// for(int i=list.size() - 1;i>=0;--i) {
-				//
-				// }
-				// }
-			}
-
-			// computing the result UV coordinates
-			resultUVS = new ArrayList<Vector2f>(imageLayoutData.size() * 3);
-			for (int i = 0; i < imageLayoutData.size() * 3; ++i) {
-				resultUVS.add(null);
-			}
-			Vector2f[] uvs = new Vector2f[3];
-			for (Entry<TriangleTextureElement, Integer[]> entry : imageLayoutData.entrySet()) {
-				Integer[] position = entry.getValue();
-				entry.getKey().computeFinalUVCoordinates(resultImageWidth, resultImageHeight, position[0], position[1], uvs);
-				resultUVS.set(entry.getKey().faceIndex * 3, uvs[0]);
-				resultUVS.set(entry.getKey().faceIndex * 3 + 1, uvs[1]);
-				resultUVS.set(entry.getKey().faceIndex * 3 + 2, uvs[2]);
-			}
-
-			Image resultImage = new Image(format, resultImageWidth, resultImageHeight, BufferUtils.createByteBuffer(resultImageWidth * resultImageHeight * (format.getBitsPerPixel() >> 3)));
-			resultTexture = new Texture2D(resultImage);
-			for (Entry<TriangleTextureElement, Integer[]> entry : imageLayoutData.entrySet()) {
-				if (!duplicatedFaceIndexes.contains(entry.getKey().faceIndex)) {
-					this.draw(resultImage, entry.getKey().image, entry.getValue()[0], entry.getValue()[1]);
-				}
-			}
-			
-			// setting additional data
-			resultTexture.setWrap(WrapAxis.S, this.getWrap(WrapAxis.S));
-			resultTexture.setWrap(WrapAxis.T, this.getWrap(WrapAxis.T));
-			resultTexture.setMagFilter(this.getMagFilter());
-			resultTexture.setMinFilter(this.getMinFilter());
-		}
-		return resultTexture;
-	}
-
-	/**
-	 * @return the result flat texture
-	 */
-	public Texture2D getResultTexture() {
-		return this.getResultTexture(false);
-	}
-
-	/**
-	 * @return the result texture's UV coordinates
-	 */
-	public List<Vector2f> getResultUVS() {
-		this.getResultTexture();// this is called here to make sure that the result UVS are computed
-		return resultUVS;
-	}
-
-	/**
-	 * This method returns a single image element for the given face index.
-	 * 
-	 * @param faceIndex
-	 *            the face index
-	 * @return image element for the required face index
-	 * @throws IllegalStateException
-	 *             this exception is thrown if the current image set does not
-	 *             contain an image for the given face index
-	 */
-	public TriangleTextureElement getFaceTextureElement(int faceIndex) {
-		for (TriangleTextureElement textureElement : faceTextures) {
-			if (textureElement.faceIndex == faceIndex) {
-				return textureElement;
-			}
-		}
-		throw new IllegalStateException("No face texture element found for index: " + faceIndex);
-	}
-	
-	/**
-	 * @return the amount of texture faces
-	 */
-	public int getFaceTextureCount() {
-		return faceTextures.size();
-	}
-
-	/**
-	 * Tells the object wheather to keep or reduce identical face textures.
-	 * 
-	 * @param keepIdenticalTextures
-	 *            keeps or discards identical textures
-	 */
-	public void setKeepIdenticalTextures(boolean keepIdenticalTextures) {
-		this.keepIdenticalTextures = keepIdenticalTextures;
-	}
-
-	/**
-	 * This method draws the source image on the target image starting with the
-	 * specified positions.
-	 * 
-	 * @param target
-	 *            the target image
-	 * @param source
-	 *            the source image
-	 * @param targetXPos
-	 *            start X position on the target image
-	 * @param targetYPos
-	 *            start Y position on the target image
-	 */
-	private void draw(Image target, Image source, int targetXPos, int targetYPos) {
-		PixelInputOutput sourceIO = PixelIOFactory.getPixelIO(source.getFormat());
-		PixelInputOutput targetIO = PixelIOFactory.getPixelIO(target.getFormat());
-		TexturePixel pixel = new TexturePixel();
-
-		for (int x = 0; x < source.getWidth(); ++x) {
-			for (int y = 0; y < source.getHeight(); ++y) {
-				sourceIO.read(source, 0, pixel, x, y);
-				targetIO.write(target, 0, pixel, targetXPos + x, targetYPos + y);
-			}
-		}
-	}
-
-	/**
-	 * A class that represents an image for a single face of the mesh.
-	 * 
-	 * @author Marcin Roguski (Kaelthas)
-	 */
-	/* package */static class TriangleTextureElement {
-		/** The image for the face. */
-		public Image			image;
-		/** The UV coordinates for the image. */
-		public final Vector2f[]	uv;
-		/** The index of the face this image refers to. */
-		public final int		faceIndex;
-
-		/**
-		 * Constructor that creates the image element from the given texture and
-		 * UV coordinates (it cuts out the smallest rectasngle possible from the
-		 * given image that will hold the triangle defined by the given UV
-		 * coordinates). After the image is cut out the UV coordinates are
-		 * recalculated to be fit for the new image.
-		 * 
-		 * @param faceIndex
-		 *            the index of mesh's face this image refers to
-		 * @param sourceImage
-		 *            the source image
-		 * @param uvCoordinates
-		 *            the UV coordinates that define the image
-		 */
-		public TriangleTextureElement(int faceIndex, Image sourceImage, List<Vector2f> uvCoordinates, boolean wholeUVList, BlenderContext blenderContext) {
-			TextureHelper textureHelper = blenderContext.getHelper(TextureHelper.class);
-			this.faceIndex = faceIndex;
-
-			uv = wholeUVList ? 
-					new Vector2f[] { uvCoordinates.get(faceIndex * 3).clone(), uvCoordinates.get(faceIndex * 3 + 1).clone(), uvCoordinates.get(faceIndex * 3 + 2).clone() } :
-					new Vector2f[] { uvCoordinates.get(0).clone(), uvCoordinates.get(1).clone(), uvCoordinates.get(2).clone() };
-
-			// be careful here, floating point operations might cause the
-			// texture positions to be inapropriate
-			int[][] texturePosition = new int[3][2];
-			for (int i = 0; i < texturePosition.length; ++i) {
-				texturePosition[i][0] = textureHelper.getPixelPosition(uv[i].x, sourceImage.getWidth());
-				texturePosition[i][1] = textureHelper.getPixelPosition(uv[i].y, sourceImage.getHeight());
-			}
-
-			// calculating the extent of the texture
-			int minX = Integer.MAX_VALUE, minY = Integer.MAX_VALUE;
-			int maxX = Integer.MIN_VALUE, maxY = Integer.MIN_VALUE;
-			float minUVX = Float.MAX_VALUE, minUVY = Float.MAX_VALUE;
-			float maxUVX = Float.MIN_VALUE, maxUVY = Float.MIN_VALUE;
-
-			for (int i = 0; i < texturePosition.length; ++i) {
-				minX = Math.min(texturePosition[i][0], minX);
-				minY = Math.min(texturePosition[i][1], minY);
-
-				maxX = Math.max(texturePosition[i][0], maxX);
-				maxY = Math.max(texturePosition[i][1], maxY);
-
-				minUVX = Math.min(uv[i].x, minUVX);
-				minUVY = Math.min(uv[i].y, minUVY);
-				maxUVX = Math.max(uv[i].x, maxUVX);
-				maxUVY = Math.max(uv[i].y, maxUVY);
-			}
-			int width = maxX - minX;
-			int height = maxY - minY;
-
-			if (width == 0) {
-				width = 1;
-			}
-			if (height == 0) {
-				height = 1;
-			}
-
-			// copy the pixel from the texture to the result image
-			PixelInputOutput pixelReader = PixelIOFactory.getPixelIO(sourceImage.getFormat());
-			TexturePixel pixel = new TexturePixel();
-			ByteBuffer data = BufferUtils.createByteBuffer(width * height * 4);
-			for (int y = minY; y < maxY; ++y) {
-				for (int x = minX; x < maxX; ++x) {
-					int xPos = x >= sourceImage.getWidth() ? x - sourceImage.getWidth() : x;
-					int yPos = y >= sourceImage.getHeight() ? y - sourceImage.getHeight() : y;
-					pixelReader.read(sourceImage, 0, pixel, xPos, yPos);
-					data.put(pixel.getR8());
-					data.put(pixel.getG8());
-					data.put(pixel.getB8());
-					data.put(pixel.getA8());
-				}
-			}
-			image = new Image(Format.RGBA8, width, height, data);
-
-			// modify the UV values so that they fit the new image
-			float heightUV = maxUVY - minUVY;
-			float widthUV = maxUVX - minUVX;
-			for (int i = 0; i < uv.length; ++i) {
-				// first translate it to the image borders
-				uv[i].x -= minUVX;
-				uv[i].y -= minUVY;
-				// then scale so that it fills the whole area
-				uv[i].x /= widthUV;
-				uv[i].y /= heightUV;
-			}
-		}
-
-		/**
-		 * Constructor that creates an image element from the 3D texture
-		 * (generated texture). It computes a flat smallest rectangle that can
-		 * hold a (3D) triangle defined by the given UV coordinates. Then it
-		 * defines the image pixels for points in 3D space that define the
-		 * calculated rectangle.
-		 * 
-		 * @param faceIndex
-		 *            the face index this image refers to
-		 * @param boundingBox
-		 *            the bounding box of the mesh
-		 * @param texture
-		 *            the texture that allows to compute a pixel value in 3D
-		 *            space
-		 * @param uv
-		 *            the UV coordinates of the mesh
-		 * @param blenderContext
-		 *            the blender context
-		 */
-		public TriangleTextureElement(int faceIndex, BoundingBox boundingBox, GeneratedTexture texture, Vector3f[] uv, int[] uvIndices, BlenderContext blenderContext) {
-			this.faceIndex = faceIndex;
-
-			// compute the face vertices from the UV coordinates
-			float width = boundingBox.getXExtent() * 2;
-			float height = boundingBox.getYExtent() * 2;
-			float depth = boundingBox.getZExtent() * 2;
-
-			Vector3f min = boundingBox.getMin(null);
-			Vector3f v1 = min.add(uv[uvIndices[0]].x * width, uv[uvIndices[0]].y * height, uv[uvIndices[0]].z * depth);
-			Vector3f v2 = min.add(uv[uvIndices[1]].x * width, uv[uvIndices[1]].y * height, uv[uvIndices[1]].z * depth);
-			Vector3f v3 = min.add(uv[uvIndices[2]].x * width, uv[uvIndices[2]].y * height, uv[uvIndices[2]].z * depth);
-
-			// get the rectangle envelope for the triangle
-			RectangleEnvelope envelope = this.getTriangleEnvelope(v1, v2, v3);
-
-			// create the result image
-			Format imageFormat = texture.getImage().getFormat();
-			int imageWidth = (int) (envelope.width * blenderContext.getBlenderKey().getGeneratedTexturePPU());
-			if(imageWidth == 0) {
-				imageWidth = 1;
-			}
-			int imageHeight = (int) (envelope.height * blenderContext.getBlenderKey().getGeneratedTexturePPU());
-			if(imageHeight == 0) {
-				imageHeight = 1;
-			}
-			ByteBuffer data = BufferUtils.createByteBuffer(imageWidth * imageHeight * (imageFormat.getBitsPerPixel() >> 3));
-			image = new Image(texture.getImage().getFormat(), imageWidth, imageHeight, data);
-
-			// computing the pixels
-			PixelInputOutput pixelWriter = PixelIOFactory.getPixelIO(imageFormat);
-			TexturePixel pixel = new TexturePixel();
-			float[] uvs = new float[3];
-			Vector3f point = new Vector3f(envelope.min);
-			Vector3f vecY = new Vector3f();
-			Vector3f wDelta = new Vector3f(envelope.w).multLocal(1.0f / imageWidth);
-			Vector3f hDelta = new Vector3f(envelope.h).multLocal(1.0f / imageHeight);
-			for (int x = 0; x < imageWidth; ++x) {
-				for (int y = 0; y < imageHeight; ++y) {
-					this.toTextureUV(boundingBox, point, uvs);
-					texture.getPixel(pixel, uvs[0], uvs[1], uvs[2]);
-					pixelWriter.write(image, 0, pixel, x, y);
-					point.addLocal(hDelta);
-				}
-
-				vecY.addLocal(wDelta);
-				point.set(envelope.min).addLocal(vecY);
-			}
-
-			// preparing UV coordinates for the flatted texture
-			this.uv = new Vector2f[3];
-			this.uv[0] = new Vector2f(FastMath.clamp(v1.subtract(envelope.min).length(), 0, Float.MAX_VALUE) / envelope.height, 0);
-			Vector3f heightDropPoint = v2.subtract(envelope.w);// w is directed from the base to v2
-			this.uv[1] = new Vector2f(1, heightDropPoint.subtractLocal(envelope.min).length() / envelope.height);
-			this.uv[2] = new Vector2f(0, 1);
-		}
-
-		/**
-		 * This method computes the final UV coordinates for the image (after it
-		 * is combined with other images and drawed on the result image).
-		 * 
-		 * @param totalImageWidth
-		 *            the result image width
-		 * @param totalImageHeight
-		 *            the result image height
-		 * @param xPos
-		 *            the most left x coordinate of the image
-		 * @param yPos
-		 *            the most top y coordinate of the image
-		 * @param result
-		 *            a vector where the result is stored
-		 */
-		public void computeFinalUVCoordinates(int totalImageWidth, int totalImageHeight, int xPos, int yPos, Vector2f[] result) {
-			for (int i = 0; i < 3; ++i) {
-				result[i] = new Vector2f();
-				result[i].x = xPos / (float) totalImageWidth + this.uv[i].x * (this.image.getWidth() / (float) totalImageWidth);
-				result[i].y = yPos / (float) totalImageHeight + this.uv[i].y * (this.image.getHeight() / (float) totalImageHeight);
-			}
-		}
-
-		/**
-		 * This method converts the given point into 3D UV coordinates.
-		 * 
-		 * @param boundingBox
-		 *            the bounding box of the mesh
-		 * @param point
-		 *            the point to be transformed
-		 * @param uvs
-		 *            the result UV coordinates
-		 */
-		private void toTextureUV(BoundingBox boundingBox, Vector3f point, float[] uvs) {
-			uvs[0] = (point.x - boundingBox.getCenter().x)/(boundingBox.getXExtent() == 0 ? 1 : boundingBox.getXExtent());
-			uvs[1] = (point.y - boundingBox.getCenter().y)/(boundingBox.getYExtent() == 0 ? 1 : boundingBox.getYExtent());
-			uvs[2] = (point.z - boundingBox.getCenter().z)/(boundingBox.getZExtent() == 0 ? 1 : boundingBox.getZExtent());
-			//UVS cannot go outside <0, 1> range, but since we are generating texture for triangle envelope it might happen that
-			//some points of the envelope will exceet the bounding box of the mesh thus generating uvs outside the range
-			for (int i = 0; i < 3; ++i) {
-				uvs[i] = FastMath.clamp(uvs[i], 0, 1);
-			}
-		}
-
-		/**
-		 * This method returns an envelope of a minimal rectangle, that is set
-		 * in 3D space, and contains the given triangle.
-		 * 
-		 * @param triangle
-		 *            the triangle
-		 * @return a rectangle minimum and maximum point and height and width
-		 */
-		private RectangleEnvelope getTriangleEnvelope(Vector3f v1, Vector3f v2, Vector3f v3) {
-			Vector3f h = v3.subtract(v1);// the height of the resulting rectangle
-			Vector3f temp = v2.subtract(v1);
-
-			float field = 0.5f * h.cross(temp).length();// the field of the rectangle: Field = 0.5 * ||h x temp||
-			if (field <= 0.0f) {
-				return new RectangleEnvelope(v1);// return single point envelope
-			}
-
-			float cosAlpha = h.dot(temp) / (h.length() * temp.length());// the cosinus of angle betweenh and temp
-
-			float triangleHeight = 2 * field / h.length();// the base of the height is the h vector
-			// now calculate the distance between v1 vertex and the point where
-			// the above calculated height 'touches' the base line (it can be
-			// settled outside the h vector)
-			float x = Math.abs((float) Math.sqrt(FastMath.clamp(temp.lengthSquared() - triangleHeight * triangleHeight, 0, Float.MAX_VALUE))) * Math.signum(cosAlpha);
-			// now get the height base point
-			Vector3f xPoint = v1.add(h.normalize().multLocal(x));
-
-			// get the minimum point of the envelope
-			Vector3f min = x < 0 ? xPoint : v1;
-			if (x < 0) {
-				h = v3.subtract(min);
-			} else if (x > h.length()) {
-				h = xPoint.subtract(min);
-			}
-
-			Vector3f envelopeWidth = v2.subtract(xPoint);
-			return new RectangleEnvelope(min, envelopeWidth, h);
-		}
-	}
-
-	/**
-	 * A class that represents a flat rectangle in 3D space that is built on a
-	 * triangle in 3D space.
-	 * 
-	 * @author Marcin Roguski (Kaelthas)
-	 */
-	private static class RectangleEnvelope {
-		/** The minimum point of the rectangle. */
-		public final Vector3f	min;
-		/** The width vector. */
-		public final Vector3f	w;
-		/** The height vector. */
-		public final Vector3f	h;
-		/** The width of the rectangle. */
-		public final float		width;
-		/** The height of the rectangle. */
-		public final float		height;
-
-		/**
-		 * Constructs a rectangle that actually holds a point, not a triangle.
-		 * This is a special case that is sometimes used when generating a
-		 * texture where UV coordinates are defined by normals instead of
-		 * vertices.
-		 * 
-		 * @param pointPosition
-		 *            a position in 3D space
-		 */
-		public RectangleEnvelope(Vector3f pointPosition) {
-			this.min = pointPosition;
-			this.h = this.w = Vector3f.ZERO;
-			this.width = this.height = 1;
-		}
-
-		/**
-		 * Constructs a rectangle envelope.
-		 * 
-		 * @param min
-		 *            the minimum rectangle point
-		 * @param w
-		 *            the width vector
-		 * @param h
-		 *            the height vector
-		 */
-		public RectangleEnvelope(Vector3f min, Vector3f w, Vector3f h) {
-			this.min = min;
-			this.h = h;
-			this.w = w;
-			this.width = w.length();
-			this.height = h.length();
-		}
-
-		@Override
-		public String toString() {
-			return "Envelope[min = " + min + ", w = " + w + ", h = " + h + "]";
-		}
-	}
-
-	@Override
-	public Texture createSimpleClone() {
-		return null;
-	}
+    /** The result image format. */
+    private Format                             format;
+    /** The collection of images for each face. */
+    private Collection<TriangleTextureElement> faceTextures;
+    /**
+     * The maximum texture size (width/height). This is taken from the blender
+     * key.
+     */
+    private int                                maxTextureSize;
+    /** A variable that can prevent removing identical textures. */
+    private boolean                            keepIdenticalTextures = false;
+    /** The result texture. */
+    private Texture2D                          resultTexture;
+    /** The result texture's UV coordinates. */
+    private List<Vector2f>                     resultUVS;
+
+    /**
+     * This method triangulates the given flat texture. The given texture is not
+     * changed.
+     * 
+     * @param texture2d
+     *            the texture to be triangulated
+     * @param uvs
+     *            the UV coordinates for each face
+     */
+    public TriangulatedTexture(Texture2D texture2d, List<Vector2f> uvs, BlenderContext blenderContext) {
+        maxTextureSize = blenderContext.getBlenderKey().getMaxTextureSize();
+        faceTextures = new TreeSet<TriangleTextureElement>(new Comparator<TriangleTextureElement>() {
+            public int compare(TriangleTextureElement o1, TriangleTextureElement o2) {
+                return o1.faceIndex - o2.faceIndex;
+            }
+        });
+        int facesCount = uvs.size() / 3;
+        for (int i = 0; i < facesCount; ++i) {
+            faceTextures.add(new TriangleTextureElement(i, texture2d.getImage(), uvs, true, blenderContext));
+        }
+        this.format = texture2d.getImage().getFormat();
+    }
+
+    /**
+     * Constructor that simply stores precalculated images.
+     * 
+     * @param faceTextures
+     *            a collection of images for the mesh's faces
+     * @param blenderContext
+     *            the blender context
+     */
+    public TriangulatedTexture(Collection<TriangleTextureElement> faceTextures, BlenderContext blenderContext) {
+        maxTextureSize = blenderContext.getBlenderKey().getMaxTextureSize();
+        this.faceTextures = faceTextures;
+        for (TriangleTextureElement faceTextureElement : faceTextures) {
+            if (format == null) {
+                format = faceTextureElement.image.getFormat();
+            } else if (format != faceTextureElement.image.getFormat()) {
+                throw new IllegalArgumentException("Face texture element images MUST have the same image format!");
+            }
+        }
+    }
+
+    /**
+     * This method blends the each image using the given blender and taking base
+     * texture into consideration.
+     * 
+     * @param textureBlender
+     *            the texture blender that holds the blending definition
+     * @param baseTexture
+     *            the texture that is 'below' the current texture (can be null)
+     * @param blenderContext
+     *            the blender context
+     */
+    public void blend(TextureBlender textureBlender, TriangulatedTexture baseTexture, BlenderContext blenderContext) {
+        Format newFormat = null;
+        for (TriangleTextureElement triangleTextureElement : this.faceTextures) {
+            Image baseImage = baseTexture == null ? null : baseTexture.getFaceTextureElement(triangleTextureElement.faceIndex).image;
+            triangleTextureElement.image = textureBlender.blend(triangleTextureElement.image, baseImage, blenderContext);
+            if (newFormat == null) {
+                newFormat = triangleTextureElement.image.getFormat();
+            } else if (newFormat != triangleTextureElement.image.getFormat()) {
+                throw new IllegalArgumentException("Face texture element images MUST have the same image format!");
+            }
+        }
+        this.format = newFormat;
+    }
+
+    /**
+     * This method alters the images to fit them into UV coordinates of the
+     * given target texture.
+     * 
+     * @param targetTexture
+     *            the texture to whose UV coordinates we fit current images
+     * @param blenderContext
+     *            the blender context
+     */
+    public void castToUVS(TriangulatedTexture targetTexture, BlenderContext blenderContext) {
+        int[] sourceSize = new int[2], targetSize = new int[2];
+        ImageLoader imageLoader = new ImageLoader();
+        TextureHelper textureHelper = blenderContext.getHelper(TextureHelper.class);
+        for (TriangleTextureElement entry : faceTextures) {
+            TriangleTextureElement targetFaceTextureElement = targetTexture.getFaceTextureElement(entry.faceIndex);
+            Vector2f[] dest = targetFaceTextureElement.uv;
+
+            // get the sizes of the source and target images
+            sourceSize[0] = entry.image.getWidth();
+            sourceSize[1] = entry.image.getHeight();
+            targetSize[0] = targetFaceTextureElement.image.getWidth();
+            targetSize[1] = targetFaceTextureElement.image.getHeight();
+
+            // create triangle transformation
+            AffineTransform affineTransform = textureHelper.createAffineTransform(entry.uv, dest, sourceSize, targetSize);
+
+            // compute the result texture
+            BufferedImage sourceImage = ImageToAwt.convert(entry.image, false, true, 0);
+
+            BufferedImage targetImage = new BufferedImage(targetSize[0], targetSize[1], sourceImage.getType());
+            Graphics2D g = targetImage.createGraphics();
+            g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
+            g.drawImage(sourceImage, affineTransform, null);
+            g.dispose();
+
+            Image output = imageLoader.load(targetImage, false);
+            entry.image = output;
+            entry.uv[0].set(dest[0]);
+            entry.uv[1].set(dest[1]);
+            entry.uv[2].set(dest[2]);
+        }
+    }
+
+    /**
+     * This method merges the current texture with the given one. The given
+     * texture is not changed.
+     * 
+     * @param triangulatedTexture
+     *            the texture we merge current texture with
+     */
+    public void merge(TriangulatedTexture triangulatedTexture) {
+        TexturePixel sourcePixel = new TexturePixel();
+        TexturePixel targetPixel = new TexturePixel();
+        for (TriangleTextureElement triangleTextureElement : this.faceTextures) {
+            Image sourceImage = triangulatedTexture.getFaceTextureElement(triangleTextureElement.faceIndex).image;
+            Image targetImage = triangleTextureElement.image;
+            PixelInputOutput sourceIO = PixelIOFactory.getPixelIO(sourceImage.getFormat());
+            PixelInputOutput targetIO = PixelIOFactory.getPixelIO(targetImage.getFormat());
+
+            for (int x = 0; x < sourceImage.getWidth(); ++x) {
+                for (int y = 0; y < sourceImage.getHeight(); ++y) {
+                    sourceIO.read(sourceImage, 0, sourcePixel, x, y);
+                    targetIO.read(targetImage, 0, targetPixel, x, y);
+                    targetPixel.merge(sourcePixel);
+                    targetIO.write(targetImage, 0, targetPixel, x, y);
+                }
+            }
+        }
+    }
+
+    /**
+     * This method returns the flat texture. It is calculated if required or if
+     * it was not created before. Images that are identical are discarded to
+     * reduce the texture size.
+     * 
+     * @param rebuild
+     *            a variable that forces texture recomputation (even if it was
+     *            computed vefore)
+     * @return flat result texture (all images merged into one)
+     */
+    public Texture2D getResultTexture(boolean rebuild) {
+        if (resultTexture == null || rebuild) {
+            // sorting the parts by their height (from highest to the lowest)
+            List<TriangleTextureElement> list = new ArrayList<TriangleTextureElement>(faceTextures);
+            Collections.sort(list, new Comparator<TriangleTextureElement>() {
+                public int compare(TriangleTextureElement o1, TriangleTextureElement o2) {
+                    return o2.image.getHeight() - o1.image.getHeight();
+                }
+            });
+
+            // arraging the images on the resulting image (calculating the result image width and height)
+            Set<Integer> duplicatedFaceIndexes = new HashSet<Integer>();
+            int resultImageHeight = list.get(0).image.getHeight();
+            int resultImageWidth = 0;
+            int currentXPos = 0, currentYPos = 0;
+            Map<TriangleTextureElement, Integer[]> imageLayoutData = new HashMap<TriangleTextureElement, Integer[]>(list.size());
+            while (list.size() > 0) {
+                TriangleTextureElement currentElement = list.remove(0);
+                if (currentXPos + currentElement.image.getWidth() > maxTextureSize) {
+                    currentXPos = 0;
+                    currentYPos = resultImageHeight;
+                    resultImageHeight += currentElement.image.getHeight();
+                }
+                Integer[] currentPositions = new Integer[] { currentXPos, currentYPos };
+                imageLayoutData.put(currentElement, currentPositions);
+
+                if (keepIdenticalTextures) {// removing identical images
+                    for (int i = 0; i < list.size(); ++i) {
+                        if (currentElement.image.equals(list.get(i).image)) {
+                            duplicatedFaceIndexes.add(list.get(i).faceIndex);
+                            imageLayoutData.put(list.remove(i--), currentPositions);
+                        }
+                    }
+                }
+
+                currentXPos += currentElement.image.getWidth();
+                resultImageWidth = Math.max(resultImageWidth, currentXPos);
+                // currentYPos += currentElement.image.getHeight();
+
+                // TODO: implement that to compact the result image
+                // try to add smaller images below the current one
+                // int remainingHeight = resultImageHeight -
+                // currentElement.image.getHeight();
+                // while(remainingHeight > 0) {
+                // for(int i=list.size() - 1;i>=0;--i) {
+                //
+                // }
+                // }
+            }
+
+            // computing the result UV coordinates
+            resultUVS = new ArrayList<Vector2f>(imageLayoutData.size() * 3);
+            for (int i = 0; i < imageLayoutData.size() * 3; ++i) {
+                resultUVS.add(null);
+            }
+            Vector2f[] uvs = new Vector2f[3];
+            for (Entry<TriangleTextureElement, Integer[]> entry : imageLayoutData.entrySet()) {
+                Integer[] position = entry.getValue();
+                entry.getKey().computeFinalUVCoordinates(resultImageWidth, resultImageHeight, position[0], position[1], uvs);
+                resultUVS.set(entry.getKey().faceIndex * 3, uvs[0]);
+                resultUVS.set(entry.getKey().faceIndex * 3 + 1, uvs[1]);
+                resultUVS.set(entry.getKey().faceIndex * 3 + 2, uvs[2]);
+            }
+
+            Image resultImage = new Image(format, resultImageWidth, resultImageHeight, BufferUtils.createByteBuffer(resultImageWidth * resultImageHeight * (format.getBitsPerPixel() >> 3)));
+            resultTexture = new Texture2D(resultImage);
+            for (Entry<TriangleTextureElement, Integer[]> entry : imageLayoutData.entrySet()) {
+                if (!duplicatedFaceIndexes.contains(entry.getKey().faceIndex)) {
+                    this.draw(resultImage, entry.getKey().image, entry.getValue()[0], entry.getValue()[1]);
+                }
+            }
+
+            // setting additional data
+            resultTexture.setWrap(WrapAxis.S, this.getWrap(WrapAxis.S));
+            resultTexture.setWrap(WrapAxis.T, this.getWrap(WrapAxis.T));
+            resultTexture.setMagFilter(this.getMagFilter());
+            resultTexture.setMinFilter(this.getMinFilter());
+        }
+        return resultTexture;
+    }
+
+    /**
+     * @return the result flat texture
+     */
+    public Texture2D getResultTexture() {
+        return this.getResultTexture(false);
+    }
+
+    /**
+     * @return the result texture's UV coordinates
+     */
+    public List<Vector2f> getResultUVS() {
+        this.getResultTexture();// this is called here to make sure that the result UVS are computed
+        return resultUVS;
+    }
+
+    /**
+     * This method returns a single image element for the given face index.
+     * 
+     * @param faceIndex
+     *            the face index
+     * @return image element for the required face index
+     * @throws IllegalStateException
+     *             this exception is thrown if the current image set does not
+     *             contain an image for the given face index
+     */
+    public TriangleTextureElement getFaceTextureElement(int faceIndex) {
+        for (TriangleTextureElement textureElement : faceTextures) {
+            if (textureElement.faceIndex == faceIndex) {
+                return textureElement;
+            }
+        }
+        throw new IllegalStateException("No face texture element found for index: " + faceIndex);
+    }
+
+    /**
+     * @return the amount of texture faces
+     */
+    public int getFaceTextureCount() {
+        return faceTextures.size();
+    }
+
+    /**
+     * Tells the object wheather to keep or reduce identical face textures.
+     * 
+     * @param keepIdenticalTextures
+     *            keeps or discards identical textures
+     */
+    public void setKeepIdenticalTextures(boolean keepIdenticalTextures) {
+        this.keepIdenticalTextures = keepIdenticalTextures;
+    }
+
+    /**
+     * This method draws the source image on the target image starting with the
+     * specified positions.
+     * 
+     * @param target
+     *            the target image
+     * @param source
+     *            the source image
+     * @param targetXPos
+     *            start X position on the target image
+     * @param targetYPos
+     *            start Y position on the target image
+     */
+    private void draw(Image target, Image source, int targetXPos, int targetYPos) {
+        PixelInputOutput sourceIO = PixelIOFactory.getPixelIO(source.getFormat());
+        PixelInputOutput targetIO = PixelIOFactory.getPixelIO(target.getFormat());
+        TexturePixel pixel = new TexturePixel();
+
+        for (int x = 0; x < source.getWidth(); ++x) {
+            for (int y = 0; y < source.getHeight(); ++y) {
+                sourceIO.read(source, 0, pixel, x, y);
+                targetIO.write(target, 0, pixel, targetXPos + x, targetYPos + y);
+            }
+        }
+    }
+
+    /**
+     * A class that represents an image for a single face of the mesh.
+     * 
+     * @author Marcin Roguski (Kaelthas)
+     */
+    /* package */static class TriangleTextureElement {
+        /** The image for the face. */
+        public Image            image;
+        /** The UV coordinates for the image. */
+        public final Vector2f[] uv;
+        /** The index of the face this image refers to. */
+        public final int        faceIndex;
+
+        /**
+         * Constructor that creates the image element from the given texture and
+         * UV coordinates (it cuts out the smallest rectasngle possible from the
+         * given image that will hold the triangle defined by the given UV
+         * coordinates). After the image is cut out the UV coordinates are
+         * recalculated to be fit for the new image.
+         * 
+         * @param faceIndex
+         *            the index of mesh's face this image refers to
+         * @param sourceImage
+         *            the source image
+         * @param uvCoordinates
+         *            the UV coordinates that define the image
+         */
+        public TriangleTextureElement(int faceIndex, Image sourceImage, List<Vector2f> uvCoordinates, boolean wholeUVList, BlenderContext blenderContext) {
+            TextureHelper textureHelper = blenderContext.getHelper(TextureHelper.class);
+            this.faceIndex = faceIndex;
+
+            uv = wholeUVList ? new Vector2f[] { uvCoordinates.get(faceIndex * 3).clone(), uvCoordinates.get(faceIndex * 3 + 1).clone(), uvCoordinates.get(faceIndex * 3 + 2).clone() } : new Vector2f[] { uvCoordinates.get(0).clone(), uvCoordinates.get(1).clone(), uvCoordinates.get(2).clone() };
+
+            // be careful here, floating point operations might cause the
+            // texture positions to be inapropriate
+            int[][] texturePosition = new int[3][2];
+            for (int i = 0; i < texturePosition.length; ++i) {
+                texturePosition[i][0] = textureHelper.getPixelPosition(uv[i].x, sourceImage.getWidth());
+                texturePosition[i][1] = textureHelper.getPixelPosition(uv[i].y, sourceImage.getHeight());
+            }
+
+            // calculating the extent of the texture
+            int minX = Integer.MAX_VALUE, minY = Integer.MAX_VALUE;
+            int maxX = Integer.MIN_VALUE, maxY = Integer.MIN_VALUE;
+            float minUVX = Float.MAX_VALUE, minUVY = Float.MAX_VALUE;
+            float maxUVX = Float.MIN_VALUE, maxUVY = Float.MIN_VALUE;
+
+            for (int i = 0; i < texturePosition.length; ++i) {
+                minX = Math.min(texturePosition[i][0], minX);
+                minY = Math.min(texturePosition[i][1], minY);
+
+                maxX = Math.max(texturePosition[i][0], maxX);
+                maxY = Math.max(texturePosition[i][1], maxY);
+
+                minUVX = Math.min(uv[i].x, minUVX);
+                minUVY = Math.min(uv[i].y, minUVY);
+                maxUVX = Math.max(uv[i].x, maxUVX);
+                maxUVY = Math.max(uv[i].y, maxUVY);
+            }
+            int width = maxX - minX;
+            int height = maxY - minY;
+
+            if (width == 0) {
+                width = 1;
+            }
+            if (height == 0) {
+                height = 1;
+            }
+
+            // copy the pixel from the texture to the result image
+            PixelInputOutput pixelReader = PixelIOFactory.getPixelIO(sourceImage.getFormat());
+            TexturePixel pixel = new TexturePixel();
+            ByteBuffer data = BufferUtils.createByteBuffer(width * height * 4);
+            for (int y = minY; y < maxY; ++y) {
+                for (int x = minX; x < maxX; ++x) {
+                    int xPos = x >= sourceImage.getWidth() ? x - sourceImage.getWidth() : x;
+                    int yPos = y >= sourceImage.getHeight() ? y - sourceImage.getHeight() : y;
+                    pixelReader.read(sourceImage, 0, pixel, xPos, yPos);
+                    data.put(pixel.getR8());
+                    data.put(pixel.getG8());
+                    data.put(pixel.getB8());
+                    data.put(pixel.getA8());
+                }
+            }
+            image = new Image(Format.RGBA8, width, height, data);
+
+            // modify the UV values so that they fit the new image
+            float heightUV = maxUVY - minUVY;
+            float widthUV = maxUVX - minUVX;
+            for (int i = 0; i < uv.length; ++i) {
+                // first translate it to the image borders
+                uv[i].x -= minUVX;
+                uv[i].y -= minUVY;
+                // then scale so that it fills the whole area
+                uv[i].x /= widthUV;
+                uv[i].y /= heightUV;
+            }
+        }
+
+        /**
+         * Constructor that creates an image element from the 3D texture
+         * (generated texture). It computes a flat smallest rectangle that can
+         * hold a (3D) triangle defined by the given UV coordinates. Then it
+         * defines the image pixels for points in 3D space that define the
+         * calculated rectangle.
+         * 
+         * @param faceIndex
+         *            the face index this image refers to
+         * @param boundingBox
+         *            the bounding box of the mesh
+         * @param texture
+         *            the texture that allows to compute a pixel value in 3D
+         *            space
+         * @param uv
+         *            the UV coordinates of the mesh
+         * @param blenderContext
+         *            the blender context
+         */
+        public TriangleTextureElement(int faceIndex, BoundingBox boundingBox, GeneratedTexture texture, Vector3f[] uv, int[] uvIndices, BlenderContext blenderContext) {
+            this.faceIndex = faceIndex;
+
+            // compute the face vertices from the UV coordinates
+            float width = boundingBox.getXExtent() * 2;
+            float height = boundingBox.getYExtent() * 2;
+            float depth = boundingBox.getZExtent() * 2;
+
+            Vector3f min = boundingBox.getMin(null);
+            Vector3f v1 = min.add(uv[uvIndices[0]].x * width, uv[uvIndices[0]].y * height, uv[uvIndices[0]].z * depth);
+            Vector3f v2 = min.add(uv[uvIndices[1]].x * width, uv[uvIndices[1]].y * height, uv[uvIndices[1]].z * depth);
+            Vector3f v3 = min.add(uv[uvIndices[2]].x * width, uv[uvIndices[2]].y * height, uv[uvIndices[2]].z * depth);
+
+            // get the rectangle envelope for the triangle
+            RectangleEnvelope envelope = this.getTriangleEnvelope(v1, v2, v3);
+
+            // create the result image
+            Format imageFormat = texture.getImage().getFormat();
+            int imageWidth = (int) (envelope.width * blenderContext.getBlenderKey().getGeneratedTexturePPU());
+            if (imageWidth == 0) {
+                imageWidth = 1;
+            }
+            int imageHeight = (int) (envelope.height * blenderContext.getBlenderKey().getGeneratedTexturePPU());
+            if (imageHeight == 0) {
+                imageHeight = 1;
+            }
+            ByteBuffer data = BufferUtils.createByteBuffer(imageWidth * imageHeight * (imageFormat.getBitsPerPixel() >> 3));
+            image = new Image(texture.getImage().getFormat(), imageWidth, imageHeight, data);
+
+            // computing the pixels
+            PixelInputOutput pixelWriter = PixelIOFactory.getPixelIO(imageFormat);
+            TexturePixel pixel = new TexturePixel();
+            float[] uvs = new float[3];
+            Vector3f point = new Vector3f(envelope.min);
+            Vector3f vecY = new Vector3f();
+            Vector3f wDelta = new Vector3f(envelope.w).multLocal(1.0f / imageWidth);
+            Vector3f hDelta = new Vector3f(envelope.h).multLocal(1.0f / imageHeight);
+            for (int x = 0; x < imageWidth; ++x) {
+                for (int y = 0; y < imageHeight; ++y) {
+                    this.toTextureUV(boundingBox, point, uvs);
+                    texture.getPixel(pixel, uvs[0], uvs[1], uvs[2]);
+                    pixelWriter.write(image, 0, pixel, x, y);
+                    point.addLocal(hDelta);
+                }
+
+                vecY.addLocal(wDelta);
+                point.set(envelope.min).addLocal(vecY);
+            }
+
+            // preparing UV coordinates for the flatted texture
+            this.uv = new Vector2f[3];
+            this.uv[0] = new Vector2f(FastMath.clamp(v1.subtract(envelope.min).length(), 0, Float.MAX_VALUE) / envelope.height, 0);
+            Vector3f heightDropPoint = v2.subtract(envelope.w);// w is directed from the base to v2
+            this.uv[1] = new Vector2f(1, heightDropPoint.subtractLocal(envelope.min).length() / envelope.height);
+            this.uv[2] = new Vector2f(0, 1);
+        }
+
+        /**
+         * This method computes the final UV coordinates for the image (after it
+         * is combined with other images and drawed on the result image).
+         * 
+         * @param totalImageWidth
+         *            the result image width
+         * @param totalImageHeight
+         *            the result image height
+         * @param xPos
+         *            the most left x coordinate of the image
+         * @param yPos
+         *            the most top y coordinate of the image
+         * @param result
+         *            a vector where the result is stored
+         */
+        public void computeFinalUVCoordinates(int totalImageWidth, int totalImageHeight, int xPos, int yPos, Vector2f[] result) {
+            for (int i = 0; i < 3; ++i) {
+                result[i] = new Vector2f();
+                result[i].x = xPos / (float) totalImageWidth + this.uv[i].x * (this.image.getWidth() / (float) totalImageWidth);
+                result[i].y = yPos / (float) totalImageHeight + this.uv[i].y * (this.image.getHeight() / (float) totalImageHeight);
+            }
+        }
+
+        /**
+         * This method converts the given point into 3D UV coordinates.
+         * 
+         * @param boundingBox
+         *            the bounding box of the mesh
+         * @param point
+         *            the point to be transformed
+         * @param uvs
+         *            the result UV coordinates
+         */
+        private void toTextureUV(BoundingBox boundingBox, Vector3f point, float[] uvs) {
+            uvs[0] = (point.x - boundingBox.getCenter().x) / (boundingBox.getXExtent() == 0 ? 1 : boundingBox.getXExtent());
+            uvs[1] = (point.y - boundingBox.getCenter().y) / (boundingBox.getYExtent() == 0 ? 1 : boundingBox.getYExtent());
+            uvs[2] = (point.z - boundingBox.getCenter().z) / (boundingBox.getZExtent() == 0 ? 1 : boundingBox.getZExtent());
+            // UVS cannot go outside <0, 1> range, but since we are generating texture for triangle envelope it might happen that
+            // some points of the envelope will exceet the bounding box of the mesh thus generating uvs outside the range
+            for (int i = 0; i < 3; ++i) {
+                uvs[i] = FastMath.clamp(uvs[i], 0, 1);
+            }
+        }
+
+        /**
+         * This method returns an envelope of a minimal rectangle, that is set
+         * in 3D space, and contains the given triangle.
+         * 
+         * @param triangle
+         *            the triangle
+         * @return a rectangle minimum and maximum point and height and width
+         */
+        private RectangleEnvelope getTriangleEnvelope(Vector3f v1, Vector3f v2, Vector3f v3) {
+            Vector3f h = v3.subtract(v1);// the height of the resulting rectangle
+            Vector3f temp = v2.subtract(v1);
+
+            float field = 0.5f * h.cross(temp).length();// the field of the rectangle: Field = 0.5 * ||h x temp||
+            if (field <= 0.0f) {
+                return new RectangleEnvelope(v1);// return single point envelope
+            }
+
+            float cosAlpha = h.dot(temp) / (h.length() * temp.length());// the cosinus of angle betweenh and temp
+
+            float triangleHeight = 2 * field / h.length();// the base of the height is the h vector
+            // now calculate the distance between v1 vertex and the point where
+            // the above calculated height 'touches' the base line (it can be
+            // settled outside the h vector)
+            float x = Math.abs((float) Math.sqrt(FastMath.clamp(temp.lengthSquared() - triangleHeight * triangleHeight, 0, Float.MAX_VALUE))) * Math.signum(cosAlpha);
+            // now get the height base point
+            Vector3f xPoint = v1.add(h.normalize().multLocal(x));
+
+            // get the minimum point of the envelope
+            Vector3f min = x < 0 ? xPoint : v1;
+            if (x < 0) {
+                h = v3.subtract(min);
+            } else if (x > h.length()) {
+                h = xPoint.subtract(min);
+            }
+
+            Vector3f envelopeWidth = v2.subtract(xPoint);
+            return new RectangleEnvelope(min, envelopeWidth, h);
+        }
+    }
+
+    /**
+     * A class that represents a flat rectangle in 3D space that is built on a
+     * triangle in 3D space.
+     * 
+     * @author Marcin Roguski (Kaelthas)
+     */
+    private static class RectangleEnvelope {
+        /** The minimum point of the rectangle. */
+        public final Vector3f min;
+        /** The width vector. */
+        public final Vector3f w;
+        /** The height vector. */
+        public final Vector3f h;
+        /** The width of the rectangle. */
+        public final float    width;
+        /** The height of the rectangle. */
+        public final float    height;
+
+        /**
+         * Constructs a rectangle that actually holds a point, not a triangle.
+         * This is a special case that is sometimes used when generating a
+         * texture where UV coordinates are defined by normals instead of
+         * vertices.
+         * 
+         * @param pointPosition
+         *            a position in 3D space
+         */
+        public RectangleEnvelope(Vector3f pointPosition) {
+            this.min = pointPosition;
+            this.h = this.w = Vector3f.ZERO;
+            this.width = this.height = 1;
+        }
+
+        /**
+         * Constructs a rectangle envelope.
+         * 
+         * @param min
+         *            the minimum rectangle point
+         * @param w
+         *            the width vector
+         * @param h
+         *            the height vector
+         */
+        public RectangleEnvelope(Vector3f min, Vector3f w, Vector3f h) {
+            this.min = min;
+            this.h = h;
+            this.w = w;
+            this.width = w.length();
+            this.height = h.length();
+        }
+
+        @Override
+        public String toString() {
+            return "Envelope[min = " + min + ", w = " + w + ", h = " + h + "]";
+        }
+    }
+
+    @Override
+    public Texture createSimpleClone() {
+        return null;
+    }
 }

+ 399 - 413
engine/src/blender/com/jme3/scene/plugins/blender/textures/UVCoordinatesGenerator.java

@@ -52,442 +52,428 @@ import java.util.logging.Logger;
  * @author Marcin Roguski (Kaelthas)
  */
 public class UVCoordinatesGenerator {
-	private static final Logger	LOGGER	= Logger.getLogger(UVCoordinatesGenerator.class.getName());
+    private static final Logger LOGGER = Logger.getLogger(UVCoordinatesGenerator.class.getName());
 
-	public static enum UVCoordinatesType {
-		TEXCO_ORCO(1), 
-		TEXCO_REFL(2), 
-		TEXCO_NORM(4), 
-		TEXCO_GLOB(8), 
-		TEXCO_UV(16), 
-		TEXCO_OBJECT(32), 
-		TEXCO_LAVECTOR(64), 
-		TEXCO_VIEW(128), 
-		TEXCO_STICKY(256), 
-		TEXCO_OSA(512), 
-		TEXCO_WINDOW(1024), 
-		NEED_UV(2048), 
-		TEXCO_TANGENT(4096),
-		// still stored in vertex->accum, 1 D
-		TEXCO_PARTICLE_OR_STRAND(8192), 
-		TEXCO_STRESS(16384), 
-		TEXCO_SPEED(32768);
+    public static enum UVCoordinatesType {
+        TEXCO_ORCO(1), TEXCO_REFL(2), TEXCO_NORM(4), TEXCO_GLOB(8), TEXCO_UV(16), TEXCO_OBJECT(32), TEXCO_LAVECTOR(64), TEXCO_VIEW(128), TEXCO_STICKY(256), TEXCO_OSA(512), TEXCO_WINDOW(1024), NEED_UV(2048), TEXCO_TANGENT(4096),
+        // still stored in vertex->accum, 1 D
+        TEXCO_PARTICLE_OR_STRAND(8192), TEXCO_STRESS(16384), TEXCO_SPEED(32768);
 
-		public final int	blenderValue;
+        public final int blenderValue;
 
-		private UVCoordinatesType(int blenderValue) {
-			this.blenderValue = blenderValue;
-		}
+        private UVCoordinatesType(int blenderValue) {
+            this.blenderValue = blenderValue;
+        }
 
-		public static UVCoordinatesType valueOf(int blenderValue) {
-			for (UVCoordinatesType coordinatesType : UVCoordinatesType.values()) {
-				if (coordinatesType.blenderValue == blenderValue) {
-					return coordinatesType;
-				}
-			}
-			return null;
-		}
-	}
+        public static UVCoordinatesType valueOf(int blenderValue) {
+            for (UVCoordinatesType coordinatesType : UVCoordinatesType.values()) {
+                if (coordinatesType.blenderValue == blenderValue) {
+                    return coordinatesType;
+                }
+            }
+            return null;
+        }
+    }
 
-	/**
-	 * Generates a UV coordinates for 2D texture.
-	 * 
-	 * @param mesh
-	 *            the mesh we generate UV's for
-	 * @param texco
-	 *            UV coordinates type
-	 * @param projection
-	 *            projection type
-	 * @param geometries
-	 *            the geometris the given mesh belongs to (required to compute
-	 *            bounding box)
-	 * @return UV coordinates for the given mesh
-	 */
-	public static List<Vector2f> generateUVCoordinatesFor2DTexture(Mesh mesh, UVCoordinatesType texco, UVProjectionType projection, List<Geometry> geometries) {
-		List<Vector2f> result = new ArrayList<Vector2f>();
-		BoundingBox bb = UVCoordinatesGenerator.getBoundingBox(geometries);
-		float[] inputData = null;// positions, normals, reflection vectors, etc.
+    /**
+     * Generates a UV coordinates for 2D texture.
+     * 
+     * @param mesh
+     *            the mesh we generate UV's for
+     * @param texco
+     *            UV coordinates type
+     * @param projection
+     *            projection type
+     * @param geometries
+     *            the geometris the given mesh belongs to (required to compute
+     *            bounding box)
+     * @return UV coordinates for the given mesh
+     */
+    public static List<Vector2f> generateUVCoordinatesFor2DTexture(Mesh mesh, UVCoordinatesType texco, UVProjectionType projection, List<Geometry> geometries) {
+        List<Vector2f> result = new ArrayList<Vector2f>();
+        BoundingBox bb = UVCoordinatesGenerator.getBoundingBox(geometries);
+        float[] inputData = null;// positions, normals, reflection vectors, etc.
 
-		switch (texco) {
-			case TEXCO_ORCO:
-				inputData = BufferUtils.getFloatArray(mesh.getFloatBuffer(VertexBuffer.Type.Position));
-				break;
-			case TEXCO_UV:// this should be used if not defined by user explicitly
-				Vector2f[] data = new Vector2f[] { new Vector2f(0, 1), new Vector2f(0, 0), new Vector2f(1, 0) };
-				for (int i = 0; i < mesh.getVertexCount(); ++i) {
-					result.add(data[i % 3]);
-				}
-				break;
-			case TEXCO_NORM:
-				inputData = BufferUtils.getFloatArray(mesh.getFloatBuffer(VertexBuffer.Type.Normal));
-				break;
-			case TEXCO_REFL:
-			case TEXCO_GLOB:
-			case TEXCO_TANGENT:
-			case TEXCO_STRESS:
-			case TEXCO_LAVECTOR:
-			case TEXCO_OBJECT:
-			case TEXCO_OSA:
-			case TEXCO_PARTICLE_OR_STRAND:
-			case TEXCO_SPEED:
-			case TEXCO_STICKY:
-			case TEXCO_VIEW:
-			case TEXCO_WINDOW:
-				LOGGER.warning("Texture coordinates type not currently supported: " + texco);
-				break;
-			default:
-				throw new IllegalStateException("Unknown texture coordinates value: " + texco);
-		}
+        switch (texco) {
+            case TEXCO_ORCO:
+                inputData = BufferUtils.getFloatArray(mesh.getFloatBuffer(VertexBuffer.Type.Position));
+                break;
+            case TEXCO_UV:// this should be used if not defined by user explicitly
+                Vector2f[] data = new Vector2f[] { new Vector2f(0, 1), new Vector2f(0, 0), new Vector2f(1, 0) };
+                for (int i = 0; i < mesh.getVertexCount(); ++i) {
+                    result.add(data[i % 3]);
+                }
+                break;
+            case TEXCO_NORM:
+                inputData = BufferUtils.getFloatArray(mesh.getFloatBuffer(VertexBuffer.Type.Normal));
+                break;
+            case TEXCO_REFL:
+            case TEXCO_GLOB:
+            case TEXCO_TANGENT:
+            case TEXCO_STRESS:
+            case TEXCO_LAVECTOR:
+            case TEXCO_OBJECT:
+            case TEXCO_OSA:
+            case TEXCO_PARTICLE_OR_STRAND:
+            case TEXCO_SPEED:
+            case TEXCO_STICKY:
+            case TEXCO_VIEW:
+            case TEXCO_WINDOW:
+                LOGGER.warning("Texture coordinates type not currently supported: " + texco);
+                break;
+            default:
+                throw new IllegalStateException("Unknown texture coordinates value: " + texco);
+        }
 
-		if (inputData != null) {// make projection calculations
-			switch (projection) {
-				case PROJECTION_FLAT:
-					inputData = UVProjectionGenerator.flatProjection(inputData, bb);
-					break;
-				case PROJECTION_CUBE:
-					inputData = UVProjectionGenerator.cubeProjection(inputData, bb);
-					break;
-				case PROJECTION_TUBE:
-					BoundingTube bt = UVCoordinatesGenerator.getBoundingTube(geometries);
-					inputData = UVProjectionGenerator.tubeProjection(inputData, bt);
-					break;
-				case PROJECTION_SPHERE:
-					BoundingSphere bs = UVCoordinatesGenerator.getBoundingSphere(geometries);
-					inputData = UVProjectionGenerator.sphereProjection(inputData, bs);
-					break;
-				default:
-					throw new IllegalStateException("Unknown projection type: " + projection);
-			}
-			for (int i = 0; i < inputData.length; i += 2) {
-				result.add(new Vector2f(inputData[i], inputData[i + 1]));
-			}
-		}
-		return result;
-	}
+        if (inputData != null) {// make projection calculations
+            switch (projection) {
+                case PROJECTION_FLAT:
+                    inputData = UVProjectionGenerator.flatProjection(inputData, bb);
+                    break;
+                case PROJECTION_CUBE:
+                    inputData = UVProjectionGenerator.cubeProjection(inputData, bb);
+                    break;
+                case PROJECTION_TUBE:
+                    BoundingTube bt = UVCoordinatesGenerator.getBoundingTube(geometries);
+                    inputData = UVProjectionGenerator.tubeProjection(inputData, bt);
+                    break;
+                case PROJECTION_SPHERE:
+                    BoundingSphere bs = UVCoordinatesGenerator.getBoundingSphere(geometries);
+                    inputData = UVProjectionGenerator.sphereProjection(inputData, bs);
+                    break;
+                default:
+                    throw new IllegalStateException("Unknown projection type: " + projection);
+            }
+            for (int i = 0; i < inputData.length; i += 2) {
+                result.add(new Vector2f(inputData[i], inputData[i + 1]));
+            }
+        }
+        return result;
+    }
 
-	/**
-	 * Generates a UV coordinates for 3D texture.
-	 * 
-	 * @param mesh
-	 *            the mesh we generate UV's for
-	 * @param texco
-	 *            UV coordinates type
-	 * @param coordinatesSwappingIndexes
-	 *            coordinates swapping indexes
-	 * @param geometries
-	 *            the geometris the given mesh belongs to (required to compute
-	 *            bounding box)
-	 * @return UV coordinates for the given mesh
-	 */
-	public static List<Vector3f> generateUVCoordinatesFor3DTexture(Mesh mesh, UVCoordinatesType texco, int[] coordinatesSwappingIndexes, List<Geometry> geometries) {
-		List<Vector3f> result = new ArrayList<Vector3f>();
-		BoundingBox bb = UVCoordinatesGenerator.getBoundingBox(geometries);
-		float[] inputData = null;// positions, normals, reflection vectors, etc.
+    /**
+     * Generates a UV coordinates for 3D texture.
+     * 
+     * @param mesh
+     *            the mesh we generate UV's for
+     * @param texco
+     *            UV coordinates type
+     * @param coordinatesSwappingIndexes
+     *            coordinates swapping indexes
+     * @param geometries
+     *            the geometris the given mesh belongs to (required to compute
+     *            bounding box)
+     * @return UV coordinates for the given mesh
+     */
+    public static List<Vector3f> generateUVCoordinatesFor3DTexture(Mesh mesh, UVCoordinatesType texco, int[] coordinatesSwappingIndexes, List<Geometry> geometries) {
+        List<Vector3f> result = new ArrayList<Vector3f>();
+        BoundingBox bb = UVCoordinatesGenerator.getBoundingBox(geometries);
+        float[] inputData = null;// positions, normals, reflection vectors, etc.
 
-		switch (texco) {
-			case TEXCO_ORCO:
-				inputData = BufferUtils.getFloatArray(mesh.getFloatBuffer(VertexBuffer.Type.Position));
-				break;
-			case TEXCO_UV:
-				Vector2f[] data = new Vector2f[] { new Vector2f(0, 1), new Vector2f(0, 0), new Vector2f(1, 0) };
-				for (int i = 0; i < mesh.getVertexCount(); ++i) {
-					Vector2f uv = data[i % 3];
-					result.add(new Vector3f(uv.x, uv.y, 0));
-				}
-				break;
-			case TEXCO_NORM:
-				inputData = BufferUtils.getFloatArray(mesh.getFloatBuffer(VertexBuffer.Type.Normal));
-				break;
-			case TEXCO_REFL:
-			case TEXCO_GLOB:
-			case TEXCO_TANGENT:
-			case TEXCO_STRESS:
-			case TEXCO_LAVECTOR:
-			case TEXCO_OBJECT:
-			case TEXCO_OSA:
-			case TEXCO_PARTICLE_OR_STRAND:
-			case TEXCO_SPEED:
-			case TEXCO_STICKY:
-			case TEXCO_VIEW:
-			case TEXCO_WINDOW:
-				LOGGER.warning("Texture coordinates type not currently supported: " + texco);
-				break;
-			default:
-				throw new IllegalStateException("Unknown texture coordinates value: " + texco);
-		}
+        switch (texco) {
+            case TEXCO_ORCO:
+                inputData = BufferUtils.getFloatArray(mesh.getFloatBuffer(VertexBuffer.Type.Position));
+                break;
+            case TEXCO_UV:
+                Vector2f[] data = new Vector2f[] { new Vector2f(0, 1), new Vector2f(0, 0), new Vector2f(1, 0) };
+                for (int i = 0; i < mesh.getVertexCount(); ++i) {
+                    Vector2f uv = data[i % 3];
+                    result.add(new Vector3f(uv.x, uv.y, 0));
+                }
+                break;
+            case TEXCO_NORM:
+                inputData = BufferUtils.getFloatArray(mesh.getFloatBuffer(VertexBuffer.Type.Normal));
+                break;
+            case TEXCO_REFL:
+            case TEXCO_GLOB:
+            case TEXCO_TANGENT:
+            case TEXCO_STRESS:
+            case TEXCO_LAVECTOR:
+            case TEXCO_OBJECT:
+            case TEXCO_OSA:
+            case TEXCO_PARTICLE_OR_STRAND:
+            case TEXCO_SPEED:
+            case TEXCO_STICKY:
+            case TEXCO_VIEW:
+            case TEXCO_WINDOW:
+                LOGGER.warning("Texture coordinates type not currently supported: " + texco);
+                break;
+            default:
+                throw new IllegalStateException("Unknown texture coordinates value: " + texco);
+        }
 
-		if (inputData != null) {// make calculations
-			Vector3f min = bb.getMin(null);
-			float[] uvCoordsResults = new float[4];// used for coordinates swapping
-			float[] ext = new float[] { bb.getXExtent() * 2, bb.getYExtent() * 2, bb.getZExtent() * 2 };
-			for (int i = 0; i < ext.length; ++i) {
-				if (ext[i] == 0) {
-					ext[i] = 1;
-				}
-			}
-			// now transform the coordinates so that they are in the range of
-			// <0; 1>
-			for (int i = 0; i < inputData.length; i += 3) {
-				uvCoordsResults[1] = (inputData[i] - min.x) / ext[0];
-				uvCoordsResults[2] = (inputData[i + 1] - min.y) / ext[1];
-				uvCoordsResults[3] = (inputData[i + 2] - min.z) / ext[2];
-				result.add(new Vector3f(uvCoordsResults[coordinatesSwappingIndexes[0]], uvCoordsResults[coordinatesSwappingIndexes[1]], uvCoordsResults[coordinatesSwappingIndexes[2]]));
-			}
-		}
-		return result;
-	}
-	
-	/**
-	 * This method should be used to determine if the texture will ever be
-	 * computed. If the texture coordinates are not supported then the try of
-	 * flattening the texture might result in runtime exceptions occurence.
-	 * 
-	 * @param texco
-	 *            the texture coordinates type
-	 * @return <b>true</b> if the type is supported and false otherwise
-	 */
-	public static boolean isTextureCoordinateTypeSupported(UVCoordinatesType texco) {
-		switch (texco) {
-			case TEXCO_ORCO:
-			case TEXCO_UV:
-			case TEXCO_NORM:
-				return true;
-			case TEXCO_REFL:
-			case TEXCO_GLOB:
-			case TEXCO_TANGENT:
-			case TEXCO_STRESS:
-			case TEXCO_LAVECTOR:
-			case TEXCO_OBJECT:
-			case TEXCO_OSA:
-			case TEXCO_PARTICLE_OR_STRAND:
-			case TEXCO_SPEED:
-			case TEXCO_STICKY:
-			case TEXCO_VIEW:
-			case TEXCO_WINDOW:
-				return false;
-			default:
-				throw new IllegalStateException("Unknown texture coordinates value: " + texco);
-		}
-	}
+        if (inputData != null) {// make calculations
+            Vector3f min = bb.getMin(null);
+            float[] uvCoordsResults = new float[4];// used for coordinates swapping
+            float[] ext = new float[] { bb.getXExtent() * 2, bb.getYExtent() * 2, bb.getZExtent() * 2 };
+            for (int i = 0; i < ext.length; ++i) {
+                if (ext[i] == 0) {
+                    ext[i] = 1;
+                }
+            }
+            // now transform the coordinates so that they are in the range of
+            // <0; 1>
+            for (int i = 0; i < inputData.length; i += 3) {
+                uvCoordsResults[1] = (inputData[i] - min.x) / ext[0];
+                uvCoordsResults[2] = (inputData[i + 1] - min.y) / ext[1];
+                uvCoordsResults[3] = (inputData[i + 2] - min.z) / ext[2];
+                result.add(new Vector3f(uvCoordsResults[coordinatesSwappingIndexes[0]], uvCoordsResults[coordinatesSwappingIndexes[1]], uvCoordsResults[coordinatesSwappingIndexes[2]]));
+            }
+        }
+        return result;
+    }
 
-	/**
-	 * This method returns the bounding box of the given geometries.
-	 * 
-	 * @param geometries
-	 *            the list of geometries
-	 * @return bounding box of the given geometries
-	 */
-	public static BoundingBox getBoundingBox(List<Geometry> geometries) {
-		BoundingBox result = null;
-		for (Geometry geometry : geometries) {
-			BoundingBox bb = UVCoordinatesGenerator.getBoundingBox(geometry.getMesh());
-			if (result == null) {
-				result = bb;
-			} else {
-				result.merge(bb);
-			}
-		}
-		return result;
-	}
+    /**
+     * This method should be used to determine if the texture will ever be
+     * computed. If the texture coordinates are not supported then the try of
+     * flattening the texture might result in runtime exceptions occurence.
+     * 
+     * @param texco
+     *            the texture coordinates type
+     * @return <b>true</b> if the type is supported and false otherwise
+     */
+    public static boolean isTextureCoordinateTypeSupported(UVCoordinatesType texco) {
+        switch (texco) {
+            case TEXCO_ORCO:
+            case TEXCO_UV:
+            case TEXCO_NORM:
+                return true;
+            case TEXCO_REFL:
+            case TEXCO_GLOB:
+            case TEXCO_TANGENT:
+            case TEXCO_STRESS:
+            case TEXCO_LAVECTOR:
+            case TEXCO_OBJECT:
+            case TEXCO_OSA:
+            case TEXCO_PARTICLE_OR_STRAND:
+            case TEXCO_SPEED:
+            case TEXCO_STICKY:
+            case TEXCO_VIEW:
+            case TEXCO_WINDOW:
+                return false;
+            default:
+                throw new IllegalStateException("Unknown texture coordinates value: " + texco);
+        }
+    }
 
-	/**
-	 * This method returns the bounding box of the given mesh.
-	 * 
-	 * @param mesh
-	 *            the mesh
-	 * @return bounding box of the given mesh
-	 */
-	/* package */static BoundingBox getBoundingBox(Mesh mesh) {
-		mesh.updateBound();
-		BoundingVolume bv = mesh.getBound();
-		if (bv instanceof BoundingBox) {
-			return (BoundingBox) bv;
-		} else if (bv instanceof BoundingSphere) {
-			BoundingSphere bs = (BoundingSphere) bv;
-			float r = bs.getRadius();
-			return new BoundingBox(bs.getCenter(), r, r, r);
-		} else {
-			throw new IllegalStateException("Unknown bounding volume type: " + bv.getClass().getName());
-		}
-	}
+    /**
+     * This method returns the bounding box of the given geometries.
+     * 
+     * @param geometries
+     *            the list of geometries
+     * @return bounding box of the given geometries
+     */
+    public static BoundingBox getBoundingBox(List<Geometry> geometries) {
+        BoundingBox result = null;
+        for (Geometry geometry : geometries) {
+            BoundingBox bb = UVCoordinatesGenerator.getBoundingBox(geometry.getMesh());
+            if (result == null) {
+                result = bb;
+            } else {
+                result.merge(bb);
+            }
+        }
+        return result;
+    }
 
-	/**
-	 * This method returns the bounding sphere of the given geometries.
-	 * 
-	 * @param geometries
-	 *            the list of geometries
-	 * @return bounding sphere of the given geometries
-	 */
-	/* package */static BoundingSphere getBoundingSphere(List<Geometry> geometries) {
-		BoundingSphere result = null;
-		for (Geometry geometry : geometries) {
-			BoundingSphere bs = UVCoordinatesGenerator.getBoundingSphere(geometry.getMesh());
-			if (result == null) {
-				result = bs;
-			} else {
-				result.merge(bs);
-			}
-		}
-		return result;
-	}
+    /**
+     * This method returns the bounding box of the given mesh.
+     * 
+     * @param mesh
+     *            the mesh
+     * @return bounding box of the given mesh
+     */
+    /* package */static BoundingBox getBoundingBox(Mesh mesh) {
+        mesh.updateBound();
+        BoundingVolume bv = mesh.getBound();
+        if (bv instanceof BoundingBox) {
+            return (BoundingBox) bv;
+        } else if (bv instanceof BoundingSphere) {
+            BoundingSphere bs = (BoundingSphere) bv;
+            float r = bs.getRadius();
+            return new BoundingBox(bs.getCenter(), r, r, r);
+        } else {
+            throw new IllegalStateException("Unknown bounding volume type: " + bv.getClass().getName());
+        }
+    }
 
-	/**
-	 * This method returns the bounding sphere of the given mesh.
-	 * 
-	 * @param mesh
-	 *            the mesh
-	 * @return bounding sphere of the given mesh
-	 */
-	/* package */static BoundingSphere getBoundingSphere(Mesh mesh) {
-		mesh.updateBound();
-		BoundingVolume bv = mesh.getBound();
-		if (bv instanceof BoundingBox) {
-			BoundingBox bb = (BoundingBox) bv;
-			float r = Math.max(bb.getXExtent(), bb.getYExtent());
-			r = Math.max(r, bb.getZExtent());
-			return new BoundingSphere(r, bb.getCenter());
-		} else if (bv instanceof BoundingSphere) {
-			return (BoundingSphere) bv;
-		} else {
-			throw new IllegalStateException("Unknown bounding volume type: " + bv.getClass().getName());
-		}
-	}
+    /**
+     * This method returns the bounding sphere of the given geometries.
+     * 
+     * @param geometries
+     *            the list of geometries
+     * @return bounding sphere of the given geometries
+     */
+    /* package */static BoundingSphere getBoundingSphere(List<Geometry> geometries) {
+        BoundingSphere result = null;
+        for (Geometry geometry : geometries) {
+            BoundingSphere bs = UVCoordinatesGenerator.getBoundingSphere(geometry.getMesh());
+            if (result == null) {
+                result = bs;
+            } else {
+                result.merge(bs);
+            }
+        }
+        return result;
+    }
 
-	/**
-	 * This method returns the bounding tube of the given mesh.
-	 * 
-	 * @param mesh
-	 *            the mesh
-	 * @return bounding tube of the given mesh
-	 */
-	/* package */static BoundingTube getBoundingTube(Mesh mesh) {
-		Vector3f center = new Vector3f();
-		float maxx = -Float.MAX_VALUE, minx = Float.MAX_VALUE;
-		float maxy = -Float.MAX_VALUE, miny = Float.MAX_VALUE;
-		float maxz = -Float.MAX_VALUE, minz = Float.MAX_VALUE;
+    /**
+     * This method returns the bounding sphere of the given mesh.
+     * 
+     * @param mesh
+     *            the mesh
+     * @return bounding sphere of the given mesh
+     */
+    /* package */static BoundingSphere getBoundingSphere(Mesh mesh) {
+        mesh.updateBound();
+        BoundingVolume bv = mesh.getBound();
+        if (bv instanceof BoundingBox) {
+            BoundingBox bb = (BoundingBox) bv;
+            float r = Math.max(bb.getXExtent(), bb.getYExtent());
+            r = Math.max(r, bb.getZExtent());
+            return new BoundingSphere(r, bb.getCenter());
+        } else if (bv instanceof BoundingSphere) {
+            return (BoundingSphere) bv;
+        } else {
+            throw new IllegalStateException("Unknown bounding volume type: " + bv.getClass().getName());
+        }
+    }
 
-		FloatBuffer positions = mesh.getFloatBuffer(VertexBuffer.Type.Position);
-		int limit = positions.limit();
-		for (int i = 0; i < limit; i += 3) {
-			float x = positions.get(i);
-			float y = positions.get(i + 1);
-			float z = positions.get(i + 2);
-			center.addLocal(x, y, z);
-			maxx = x > maxx ? x : maxx;
-			minx = x < minx ? x : minx;
-			maxy = y > maxy ? y : maxy;
-			miny = y < miny ? y : miny;
-			maxz = z > maxz ? z : maxz;
-			minz = z < minz ? z : minz;
-		}
-		center.divideLocal(limit / 3);
+    /**
+     * This method returns the bounding tube of the given mesh.
+     * 
+     * @param mesh
+     *            the mesh
+     * @return bounding tube of the given mesh
+     */
+    /* package */static BoundingTube getBoundingTube(Mesh mesh) {
+        Vector3f center = new Vector3f();
+        float maxx = -Float.MAX_VALUE, minx = Float.MAX_VALUE;
+        float maxy = -Float.MAX_VALUE, miny = Float.MAX_VALUE;
+        float maxz = -Float.MAX_VALUE, minz = Float.MAX_VALUE;
 
-		float radius = Math.max(maxx - minx, maxy - miny) * 0.5f;
-		return new BoundingTube(radius, maxz - minz, center);
-	}
+        FloatBuffer positions = mesh.getFloatBuffer(VertexBuffer.Type.Position);
+        int limit = positions.limit();
+        for (int i = 0; i < limit; i += 3) {
+            float x = positions.get(i);
+            float y = positions.get(i + 1);
+            float z = positions.get(i + 2);
+            center.addLocal(x, y, z);
+            maxx = x > maxx ? x : maxx;
+            minx = x < minx ? x : minx;
+            maxy = y > maxy ? y : maxy;
+            miny = y < miny ? y : miny;
+            maxz = z > maxz ? z : maxz;
+            minz = z < minz ? z : minz;
+        }
+        center.divideLocal(limit / 3);
 
-	/**
-	 * This method returns the bounding tube of the given geometries.
-	 * 
-	 * @param geometries
-	 *            the list of geometries
-	 * @return bounding tube of the given geometries
-	 */
-	/* package */static BoundingTube getBoundingTube(List<Geometry> geometries) {
-		BoundingTube result = null;
-		for (Geometry geometry : geometries) {
-			BoundingTube bt = UVCoordinatesGenerator.getBoundingTube(geometry.getMesh());
-			if (result == null) {
-				result = bt;
-			} else {
-				result.merge(bt);
-			}
-		}
-		return result;
-	}
+        float radius = Math.max(maxx - minx, maxy - miny) * 0.5f;
+        return new BoundingTube(radius, maxz - minz, center);
+    }
 
-	/**
-	 * A very simple bounding tube. Id holds only the basic data bout the
-	 * bounding tube and does not provide full functionality of a
-	 * BoundingVolume. Should be replaced with a bounding tube that extends the
-	 * BoundingVolume if it is ever created.
-	 * 
-	 * @author Marcin Roguski (Kaelthas)
-	 */
-	/* package */static class BoundingTube {
-		private float		radius;
-		private float		height;
-		private Vector3f	center;
+    /**
+     * This method returns the bounding tube of the given geometries.
+     * 
+     * @param geometries
+     *            the list of geometries
+     * @return bounding tube of the given geometries
+     */
+    /* package */static BoundingTube getBoundingTube(List<Geometry> geometries) {
+        BoundingTube result = null;
+        for (Geometry geometry : geometries) {
+            BoundingTube bt = UVCoordinatesGenerator.getBoundingTube(geometry.getMesh());
+            if (result == null) {
+                result = bt;
+            } else {
+                result.merge(bt);
+            }
+        }
+        return result;
+    }
 
-		/**
-		 * Constructor creates the tube with the given params.
-		 * 
-		 * @param radius
-		 *            the radius of the tube
-		 * @param height
-		 *            the height of the tube
-		 * @param center
-		 *            the center of the tube
-		 */
-		public BoundingTube(float radius, float height, Vector3f center) {
-			this.radius = radius;
-			this.height = height;
-			this.center = center;
-		}
+    /**
+     * A very simple bounding tube. Id holds only the basic data bout the
+     * bounding tube and does not provide full functionality of a
+     * BoundingVolume. Should be replaced with a bounding tube that extends the
+     * BoundingVolume if it is ever created.
+     * 
+     * @author Marcin Roguski (Kaelthas)
+     */
+    /* package */static class BoundingTube {
+        private float    radius;
+        private float    height;
+        private Vector3f center;
 
-		/**
-		 * This method merges two bounding tubes.
-		 * 
-		 * @param boundingTube
-		 *            bounding tube to be merged woth the current one
-		 * @return new instance of bounding tube representing the tubes' merge
-		 */
-		public BoundingTube merge(BoundingTube boundingTube) {
-			// get tubes (tube1.radius >= tube2.radius)
-			BoundingTube tube1, tube2;
-			if (this.radius >= boundingTube.radius) {
-				tube1 = this;
-				tube2 = boundingTube;
-			} else {
-				tube1 = boundingTube;
-				tube2 = this;
-			}
-			float r1 = tube1.radius;
-			float r2 = tube2.radius;
+        /**
+         * Constructor creates the tube with the given params.
+         * 
+         * @param radius
+         *            the radius of the tube
+         * @param height
+         *            the height of the tube
+         * @param center
+         *            the center of the tube
+         */
+        public BoundingTube(float radius, float height, Vector3f center) {
+            this.radius = radius;
+            this.height = height;
+            this.center = center;
+        }
 
-			float minZ = Math.min(tube1.center.z - tube1.height * 0.5f, tube2.center.z - tube2.height * 0.5f);
-			float maxZ = Math.max(tube1.center.z + tube1.height * 0.5f, tube2.center.z + tube2.height * 0.5f);
-			float height = maxZ - minZ;
-			Vector3f distance = tube2.center.subtract(tube1.center);
-			Vector3f center = tube1.center.add(distance.mult(0.5f));
-			distance.z = 0;// projecting this vector on XY plane
-			float d = distance.length();
-			// d <= r1 - r2: tube2 is inside tube1 or touches tube1 from the
-			// inside
-			// d > r1 - r2: tube2 is outside or touches tube1 or crosses tube1
-			float radius = d <= r1 - r2 ? tube1.radius : (d + r1 + r2) * 0.5f;
-			return new BoundingTube(radius, height, center);
-		}
+        /**
+         * This method merges two bounding tubes.
+         * 
+         * @param boundingTube
+         *            bounding tube to be merged woth the current one
+         * @return new instance of bounding tube representing the tubes' merge
+         */
+        public BoundingTube merge(BoundingTube boundingTube) {
+            // get tubes (tube1.radius >= tube2.radius)
+            BoundingTube tube1, tube2;
+            if (this.radius >= boundingTube.radius) {
+                tube1 = this;
+                tube2 = boundingTube;
+            } else {
+                tube1 = boundingTube;
+                tube2 = this;
+            }
+            float r1 = tube1.radius;
+            float r2 = tube2.radius;
 
-		/**
-		 * @return the radius of the tube
-		 */
-		public float getRadius() {
-			return radius;
-		}
+            float minZ = Math.min(tube1.center.z - tube1.height * 0.5f, tube2.center.z - tube2.height * 0.5f);
+            float maxZ = Math.max(tube1.center.z + tube1.height * 0.5f, tube2.center.z + tube2.height * 0.5f);
+            float height = maxZ - minZ;
+            Vector3f distance = tube2.center.subtract(tube1.center);
+            Vector3f center = tube1.center.add(distance.mult(0.5f));
+            distance.z = 0;// projecting this vector on XY plane
+            float d = distance.length();
+            // d <= r1 - r2: tube2 is inside tube1 or touches tube1 from the
+            // inside
+            // d > r1 - r2: tube2 is outside or touches tube1 or crosses tube1
+            float radius = d <= r1 - r2 ? tube1.radius : (d + r1 + r2) * 0.5f;
+            return new BoundingTube(radius, height, center);
+        }
 
-		/**
-		 * @return the height of the tube
-		 */
-		public float getHeight() {
-			return height;
-		}
+        /**
+         * @return the radius of the tube
+         */
+        public float getRadius() {
+            return radius;
+        }
 
-		/**
-		 * @return the center of the tube
-		 */
-		public Vector3f getCenter() {
-			return center;
-		}
-	}
+        /**
+         * @return the height of the tube
+         */
+        public float getHeight() {
+            return height;
+        }
+
+        /**
+         * @return the center of the tube
+         */
+        public Vector3f getCenter() {
+            return center;
+        }
+    }
 }

+ 224 - 231
engine/src/blender/com/jme3/scene/plugins/blender/textures/UVProjectionGenerator.java

@@ -13,235 +13,228 @@ import com.jme3.scene.plugins.blender.textures.UVCoordinatesGenerator.BoundingTu
  * @author Marcin Roguski (Kaelthas)
  */
 /* package */class UVProjectionGenerator {
-	/**
-	 * 2D texture mapping (projection)
-	 * @author Marcin Roguski (Kaelthas)
-	 */
-	public static enum UVProjectionType {
-		PROJECTION_FLAT(0),
-		PROJECTION_CUBE(1),
-		PROJECTION_TUBE(2),
-		PROJECTION_SPHERE(3);
-		
-		public final int blenderValue;
-		
-		private UVProjectionType(int blenderValue) {
-			this.blenderValue = blenderValue;
-		}
-		
-		public static UVProjectionType valueOf(int blenderValue) {
-			for(UVProjectionType projectionType : UVProjectionType.values()) {
-				if(projectionType.blenderValue == blenderValue) {
-					return projectionType;
-				}
-			}
-			return null;
-		}
-	}
-	
-	/**
-	 * Flat projection for 2D textures.
-	 * 
-	 * @param mesh
-	 *            mesh that is to be projected
-	 * @param bb
-	 *            the bounding box for projecting
-	 * @return UV coordinates after the projection
-	 */
-	public static float[] flatProjection(float[] positions, BoundingBox bb) {
-		Vector3f min = bb.getMin(null);
-		float[] ext = new float[] { bb.getXExtent() * 2.0f, bb.getZExtent() * 2.0f };
-		float[] uvCoordinates = new float[positions.length / 3 * 2];
-		for (int i = 0, j = 0; i < positions.length; i += 3, j += 2) {
-			uvCoordinates[j] = (positions[i] - min.x) / ext[0];
-			// skip the Y-coordinate
-			uvCoordinates[j + 1] = (positions[i + 2] - min.z) / ext[1];
-		}
-		return uvCoordinates;
-	}
-
-	/**
-	 * Cube projection for 2D textures.
-	 * 
-	 * @param positions
-	 *            points to be projected
-	 * @param bb
-	 *            the bounding box for projecting
-	 * @return UV coordinates after the projection
-	 */
-	public static float[] cubeProjection(float[] positions, BoundingBox bb) {
-		Triangle triangle = new Triangle();
-		Vector3f x = new Vector3f(1, 0, 0);
-		Vector3f y = new Vector3f(0, 1, 0);
-		Vector3f z = new Vector3f(0, 0, 1);
-		Vector3f min = bb.getMin(null);
-		float[] ext = new float[] { bb.getXExtent() * 2.0f, bb.getYExtent() * 2.0f, bb.getZExtent() * 2.0f };
-
-		float[] uvCoordinates = new float[positions.length/3*2];
-		float borderAngle = (float) Math.sqrt(2.0f) / 2.0f;
-		for (int i = 0, pointIndex = 0; i < positions.length; i+=9) {
-			triangle.set(0, positions[i], positions[i + 1], positions[i + 2]);
-			triangle.set(1, positions[i + 3], positions[i + 4], positions[i + 5]);
-			triangle.set(2, positions[i + 6], positions[i + 7], positions[i + 8]);
-			Vector3f n = triangle.getNormal();
-			float dotNX = Math.abs(n.dot(x));
-			float dorNY = Math.abs(n.dot(y));
-			float dotNZ = Math.abs(n.dot(z));
-			if (dotNX > borderAngle) {
-				if (dotNZ < borderAngle) {// discard X-coordinate
-					uvCoordinates[pointIndex++] = (triangle.get1().y - min.y) / ext[1];
-					uvCoordinates[pointIndex++] = (triangle.get1().z - min.z) / ext[2];
-					uvCoordinates[pointIndex++] = (triangle.get2().y - min.y) / ext[1];
-					uvCoordinates[pointIndex++] = (triangle.get2().z - min.z) / ext[2];
-					uvCoordinates[pointIndex++] = (triangle.get3().y - min.y) / ext[1];
-					uvCoordinates[pointIndex++] = (triangle.get3().z - min.z) / ext[2];
-				} else {// discard Z-coordinate
-					uvCoordinates[pointIndex++] = (triangle.get1().x - min.x) / ext[0];
-					uvCoordinates[pointIndex++] = (triangle.get1().y - min.y) / ext[1];
-					uvCoordinates[pointIndex++] = (triangle.get2().x - min.x) / ext[0];
-					uvCoordinates[pointIndex++] = (triangle.get2().y - min.y) / ext[1];
-					uvCoordinates[pointIndex++] = (triangle.get3().x - min.x) / ext[0];
-					uvCoordinates[pointIndex++] = (triangle.get3().y - min.y) / ext[1];
-				}
-			} else {
-				if (dorNY > borderAngle) {// discard Y-coordinate
-					uvCoordinates[pointIndex++] = (triangle.get1().x - min.x) / ext[0];
-					uvCoordinates[pointIndex++] = (triangle.get1().z - min.z) / ext[2];
-					uvCoordinates[pointIndex++] = (triangle.get2().x - min.x) / ext[0];
-					uvCoordinates[pointIndex++] = (triangle.get2().z - min.z) / ext[2];
-					uvCoordinates[pointIndex++] = (triangle.get3().x - min.x) / ext[0];
-					uvCoordinates[pointIndex++] = (triangle.get3().z - min.z) / ext[2];
-				} else {// discard Z-coordinate
-					uvCoordinates[pointIndex++] = (triangle.get1().x - min.x) / ext[0];
-					uvCoordinates[pointIndex++] = (triangle.get1().y - min.y) / ext[1];
-					uvCoordinates[pointIndex++] = (triangle.get2().x - min.x) / ext[0];
-					uvCoordinates[pointIndex++] = (triangle.get2().y - min.y) / ext[1];
-					uvCoordinates[pointIndex++] = (triangle.get3().x - min.x) / ext[0];
-					uvCoordinates[pointIndex++] = (triangle.get3().y - min.y) / ext[1];
-				}
-			}
-			triangle.setNormal(null);// clear the previous normal vector
-		}
-		return uvCoordinates;
-	}
-
-	/**
-	 * Tube projection for 2D textures.
-	 * 
-	 * @param positions
-	 *            points to be projected
-	 * @param bt
-	 *            the bounding tube for projecting
-	 * @return UV coordinates after the projection
-	 */
-	public static float[] tubeProjection(float[] positions, BoundingTube bt) {
-		float[] uvCoordinates = new float[positions.length / 3 * 2];
-		Vector3f v = new Vector3f();
-		float cx = bt.getCenter().x, cz = bt.getCenter().z;
-		Vector3f uBase = new Vector3f(0, 0, -1);
-		
-		float vBase = bt.getCenter().y - bt.getHeight() * 0.5f;
-		for (int i = 0, j = 0; i < positions.length; i += 3, j += 2) {
-			// calculating U
-			v.set(positions[i]-cx, 0, positions[i + 2]-cz);
-			v.normalizeLocal();
-			float angle = v.angleBetween(uBase);// result between [0; PI]
-			if (v.x < 0) {// the angle should be greater than PI, we're on the other part of the image then
-				angle = FastMath.TWO_PI - angle;
-			}
-			uvCoordinates[j] = angle / FastMath.TWO_PI;
-
-			// calculating V
-			float y = positions[i + 1];
-			uvCoordinates[j + 1] = (y - vBase) / bt.getHeight();
-		}
-		
-		//looking for splitted triangles
-		Triangle triangle = new Triangle();
-		for (int i = 0; i < positions.length; i+=9) {
-			triangle.set(0, positions[i], positions[i + 1], positions[i + 2]);
-			triangle.set(1, positions[i + 3], positions[i + 4], positions[i + 5]);
-			triangle.set(2, positions[i + 6], positions[i + 7], positions[i + 8]);
-			float sgn1 = Math.signum(triangle.get1().x-cx);
-			float sgn2 = Math.signum(triangle.get2().x-cx);
-			float sgn3 = Math.signum(triangle.get3().x-cx);
-			float xSideFactor = sgn1 + sgn2 + sgn3;
-			float ySideFactor = Math.signum(triangle.get1().z-cz)+
-					   Math.signum(triangle.get2().z-cz)+
-					   Math.signum(triangle.get3().z-cz);
-			if((xSideFactor>-3 || xSideFactor<3) && ySideFactor<0) {//the triangle is on the splitting plane
-				if(sgn1==1.0f) {
-					uvCoordinates[i/3*2] += 1.0f;
-				}
-				if(sgn2==1.0f) {
-					uvCoordinates[(i/3+1)*2] += 1.0f;
-				}
-				if(sgn3==1.0f) {
-					uvCoordinates[(i/3+2)*2] += 1.0f;
-				}
-			}
-		}
-		return uvCoordinates;
-	}
-
-	/**
-	 * Sphere projection for 2D textures.
-	 * 
-	 * @param positions
-	 *            points to be projected
-	 * @param bb
-	 *            the bounding box for projecting
-	 * @return UV coordinates after the projection
-	 */
-	public static float[] sphereProjection(float[] positions, BoundingSphere bs) {//TODO: rotate it to be vertical
-		float[] uvCoordinates = new float[positions.length / 3 * 2];
-		Vector3f v = new Vector3f();
-		float cx = bs.getCenter().x, cy = bs.getCenter().y, cz = bs.getCenter().z;
-		Vector3f uBase = new Vector3f(0, -1, 0);
-		Vector3f vBase = new Vector3f(0, 0, -1);
-
-		for (int i = 0, j = 0; i < positions.length; i += 3, j += 2) {
-			// calculating U
-			v.set(positions[i]-cx, positions[i + 1] - cy, 0);
-			v.normalizeLocal();
-			float angle = v.angleBetween(uBase);// result between [0; PI]
-			if (v.x < 0) {// the angle should be greater than PI, we're on the other part of the image then
-				angle = FastMath.TWO_PI - angle;
-			}
-			uvCoordinates[j] = angle / FastMath.TWO_PI;
-
-			// calculating V
-			v.set(positions[i]-cx, positions[i + 1]-cy, positions[i + 2]-cz);
-			v.normalizeLocal();
-			angle = v.angleBetween(vBase);// result between [0; PI]
-			uvCoordinates[j+1] = angle / FastMath.PI;
-		}
-		
-		//looking for splitted triangles
-		Triangle triangle = new Triangle();
-		for (int i = 0; i < positions.length; i+=9) {
-			triangle.set(0, positions[i], positions[i + 1], positions[i + 2]);
-			triangle.set(1, positions[i + 3], positions[i + 4], positions[i + 5]);
-			triangle.set(2, positions[i + 6], positions[i + 7], positions[i + 8]);
-			float sgn1 = Math.signum(triangle.get1().x-cx);
-			float sgn2 = Math.signum(triangle.get2().x-cx);
-			float sgn3 = Math.signum(triangle.get3().x-cx);
-			float xSideFactor = sgn1 + sgn2 + sgn3;
-			float ySideFactor = Math.signum(triangle.get1().y-cy)+
-					   Math.signum(triangle.get2().y-cy)+
-					   Math.signum(triangle.get3().y-cy);
-			if((xSideFactor>-3 || xSideFactor<3) && ySideFactor<0) {//the triangle is on the splitting plane
-				if(sgn1==1.0f) {
-					uvCoordinates[i/3*2] += 1.0f;
-				}
-				if(sgn2==1.0f) {
-					uvCoordinates[(i/3+1)*2] += 1.0f;
-				}
-				if(sgn3==1.0f) {
-					uvCoordinates[(i/3+2)*2] += 1.0f;
-				}
-			}
-		}
-		return uvCoordinates;
-	}
+    /**
+     * 2D texture mapping (projection)
+     * @author Marcin Roguski (Kaelthas)
+     */
+    public static enum UVProjectionType {
+        PROJECTION_FLAT(0), PROJECTION_CUBE(1), PROJECTION_TUBE(2), PROJECTION_SPHERE(3);
+
+        public final int blenderValue;
+
+        private UVProjectionType(int blenderValue) {
+            this.blenderValue = blenderValue;
+        }
+
+        public static UVProjectionType valueOf(int blenderValue) {
+            for (UVProjectionType projectionType : UVProjectionType.values()) {
+                if (projectionType.blenderValue == blenderValue) {
+                    return projectionType;
+                }
+            }
+            return null;
+        }
+    }
+
+    /**
+     * Flat projection for 2D textures.
+     * 
+     * @param mesh
+     *            mesh that is to be projected
+     * @param bb
+     *            the bounding box for projecting
+     * @return UV coordinates after the projection
+     */
+    public static float[] flatProjection(float[] positions, BoundingBox bb) {
+        Vector3f min = bb.getMin(null);
+        float[] ext = new float[] { bb.getXExtent() * 2.0f, bb.getZExtent() * 2.0f };
+        float[] uvCoordinates = new float[positions.length / 3 * 2];
+        for (int i = 0, j = 0; i < positions.length; i += 3, j += 2) {
+            uvCoordinates[j] = (positions[i] - min.x) / ext[0];
+            // skip the Y-coordinate
+            uvCoordinates[j + 1] = (positions[i + 2] - min.z) / ext[1];
+        }
+        return uvCoordinates;
+    }
+
+    /**
+     * Cube projection for 2D textures.
+     * 
+     * @param positions
+     *            points to be projected
+     * @param bb
+     *            the bounding box for projecting
+     * @return UV coordinates after the projection
+     */
+    public static float[] cubeProjection(float[] positions, BoundingBox bb) {
+        Triangle triangle = new Triangle();
+        Vector3f x = new Vector3f(1, 0, 0);
+        Vector3f y = new Vector3f(0, 1, 0);
+        Vector3f z = new Vector3f(0, 0, 1);
+        Vector3f min = bb.getMin(null);
+        float[] ext = new float[] { bb.getXExtent() * 2.0f, bb.getYExtent() * 2.0f, bb.getZExtent() * 2.0f };
+
+        float[] uvCoordinates = new float[positions.length / 3 * 2];
+        float borderAngle = (float) Math.sqrt(2.0f) / 2.0f;
+        for (int i = 0, pointIndex = 0; i < positions.length; i += 9) {
+            triangle.set(0, positions[i], positions[i + 1], positions[i + 2]);
+            triangle.set(1, positions[i + 3], positions[i + 4], positions[i + 5]);
+            triangle.set(2, positions[i + 6], positions[i + 7], positions[i + 8]);
+            Vector3f n = triangle.getNormal();
+            float dotNX = Math.abs(n.dot(x));
+            float dorNY = Math.abs(n.dot(y));
+            float dotNZ = Math.abs(n.dot(z));
+            if (dotNX > borderAngle) {
+                if (dotNZ < borderAngle) {// discard X-coordinate
+                    uvCoordinates[pointIndex++] = (triangle.get1().y - min.y) / ext[1];
+                    uvCoordinates[pointIndex++] = (triangle.get1().z - min.z) / ext[2];
+                    uvCoordinates[pointIndex++] = (triangle.get2().y - min.y) / ext[1];
+                    uvCoordinates[pointIndex++] = (triangle.get2().z - min.z) / ext[2];
+                    uvCoordinates[pointIndex++] = (triangle.get3().y - min.y) / ext[1];
+                    uvCoordinates[pointIndex++] = (triangle.get3().z - min.z) / ext[2];
+                } else {// discard Z-coordinate
+                    uvCoordinates[pointIndex++] = (triangle.get1().x - min.x) / ext[0];
+                    uvCoordinates[pointIndex++] = (triangle.get1().y - min.y) / ext[1];
+                    uvCoordinates[pointIndex++] = (triangle.get2().x - min.x) / ext[0];
+                    uvCoordinates[pointIndex++] = (triangle.get2().y - min.y) / ext[1];
+                    uvCoordinates[pointIndex++] = (triangle.get3().x - min.x) / ext[0];
+                    uvCoordinates[pointIndex++] = (triangle.get3().y - min.y) / ext[1];
+                }
+            } else {
+                if (dorNY > borderAngle) {// discard Y-coordinate
+                    uvCoordinates[pointIndex++] = (triangle.get1().x - min.x) / ext[0];
+                    uvCoordinates[pointIndex++] = (triangle.get1().z - min.z) / ext[2];
+                    uvCoordinates[pointIndex++] = (triangle.get2().x - min.x) / ext[0];
+                    uvCoordinates[pointIndex++] = (triangle.get2().z - min.z) / ext[2];
+                    uvCoordinates[pointIndex++] = (triangle.get3().x - min.x) / ext[0];
+                    uvCoordinates[pointIndex++] = (triangle.get3().z - min.z) / ext[2];
+                } else {// discard Z-coordinate
+                    uvCoordinates[pointIndex++] = (triangle.get1().x - min.x) / ext[0];
+                    uvCoordinates[pointIndex++] = (triangle.get1().y - min.y) / ext[1];
+                    uvCoordinates[pointIndex++] = (triangle.get2().x - min.x) / ext[0];
+                    uvCoordinates[pointIndex++] = (triangle.get2().y - min.y) / ext[1];
+                    uvCoordinates[pointIndex++] = (triangle.get3().x - min.x) / ext[0];
+                    uvCoordinates[pointIndex++] = (triangle.get3().y - min.y) / ext[1];
+                }
+            }
+            triangle.setNormal(null);// clear the previous normal vector
+        }
+        return uvCoordinates;
+    }
+
+    /**
+     * Tube projection for 2D textures.
+     * 
+     * @param positions
+     *            points to be projected
+     * @param bt
+     *            the bounding tube for projecting
+     * @return UV coordinates after the projection
+     */
+    public static float[] tubeProjection(float[] positions, BoundingTube bt) {
+        float[] uvCoordinates = new float[positions.length / 3 * 2];
+        Vector3f v = new Vector3f();
+        float cx = bt.getCenter().x, cz = bt.getCenter().z;
+        Vector3f uBase = new Vector3f(0, 0, -1);
+
+        float vBase = bt.getCenter().y - bt.getHeight() * 0.5f;
+        for (int i = 0, j = 0; i < positions.length; i += 3, j += 2) {
+            // calculating U
+            v.set(positions[i] - cx, 0, positions[i + 2] - cz);
+            v.normalizeLocal();
+            float angle = v.angleBetween(uBase);// result between [0; PI]
+            if (v.x < 0) {// the angle should be greater than PI, we're on the other part of the image then
+                angle = FastMath.TWO_PI - angle;
+            }
+            uvCoordinates[j] = angle / FastMath.TWO_PI;
+
+            // calculating V
+            float y = positions[i + 1];
+            uvCoordinates[j + 1] = (y - vBase) / bt.getHeight();
+        }
+
+        // looking for splitted triangles
+        Triangle triangle = new Triangle();
+        for (int i = 0; i < positions.length; i += 9) {
+            triangle.set(0, positions[i], positions[i + 1], positions[i + 2]);
+            triangle.set(1, positions[i + 3], positions[i + 4], positions[i + 5]);
+            triangle.set(2, positions[i + 6], positions[i + 7], positions[i + 8]);
+            float sgn1 = Math.signum(triangle.get1().x - cx);
+            float sgn2 = Math.signum(triangle.get2().x - cx);
+            float sgn3 = Math.signum(triangle.get3().x - cx);
+            float xSideFactor = sgn1 + sgn2 + sgn3;
+            float ySideFactor = Math.signum(triangle.get1().z - cz) + Math.signum(triangle.get2().z - cz) + Math.signum(triangle.get3().z - cz);
+            if ((xSideFactor > -3 || xSideFactor < 3) && ySideFactor < 0) {// the triangle is on the splitting plane
+                if (sgn1 == 1.0f) {
+                    uvCoordinates[i / 3 * 2] += 1.0f;
+                }
+                if (sgn2 == 1.0f) {
+                    uvCoordinates[(i / 3 + 1) * 2] += 1.0f;
+                }
+                if (sgn3 == 1.0f) {
+                    uvCoordinates[(i / 3 + 2) * 2] += 1.0f;
+                }
+            }
+        }
+        return uvCoordinates;
+    }
+
+    /**
+     * Sphere projection for 2D textures.
+     * 
+     * @param positions
+     *            points to be projected
+     * @param bb
+     *            the bounding box for projecting
+     * @return UV coordinates after the projection
+     */
+    public static float[] sphereProjection(float[] positions, BoundingSphere bs) {// TODO: rotate it to be vertical
+        float[] uvCoordinates = new float[positions.length / 3 * 2];
+        Vector3f v = new Vector3f();
+        float cx = bs.getCenter().x, cy = bs.getCenter().y, cz = bs.getCenter().z;
+        Vector3f uBase = new Vector3f(0, -1, 0);
+        Vector3f vBase = new Vector3f(0, 0, -1);
+
+        for (int i = 0, j = 0; i < positions.length; i += 3, j += 2) {
+            // calculating U
+            v.set(positions[i] - cx, positions[i + 1] - cy, 0);
+            v.normalizeLocal();
+            float angle = v.angleBetween(uBase);// result between [0; PI]
+            if (v.x < 0) {// the angle should be greater than PI, we're on the other part of the image then
+                angle = FastMath.TWO_PI - angle;
+            }
+            uvCoordinates[j] = angle / FastMath.TWO_PI;
+
+            // calculating V
+            v.set(positions[i] - cx, positions[i + 1] - cy, positions[i + 2] - cz);
+            v.normalizeLocal();
+            angle = v.angleBetween(vBase);// result between [0; PI]
+            uvCoordinates[j + 1] = angle / FastMath.PI;
+        }
+
+        // looking for splitted triangles
+        Triangle triangle = new Triangle();
+        for (int i = 0; i < positions.length; i += 9) {
+            triangle.set(0, positions[i], positions[i + 1], positions[i + 2]);
+            triangle.set(1, positions[i + 3], positions[i + 4], positions[i + 5]);
+            triangle.set(2, positions[i + 6], positions[i + 7], positions[i + 8]);
+            float sgn1 = Math.signum(triangle.get1().x - cx);
+            float sgn2 = Math.signum(triangle.get2().x - cx);
+            float sgn3 = Math.signum(triangle.get3().x - cx);
+            float xSideFactor = sgn1 + sgn2 + sgn3;
+            float ySideFactor = Math.signum(triangle.get1().y - cy) + Math.signum(triangle.get2().y - cy) + Math.signum(triangle.get3().y - cy);
+            if ((xSideFactor > -3 || xSideFactor < 3) && ySideFactor < 0) {// the triangle is on the splitting plane
+                if (sgn1 == 1.0f) {
+                    uvCoordinates[i / 3 * 2] += 1.0f;
+                }
+                if (sgn2 == 1.0f) {
+                    uvCoordinates[(i / 3 + 1) * 2] += 1.0f;
+                }
+                if (sgn3 == 1.0f) {
+                    uvCoordinates[(i / 3 + 2) * 2] += 1.0f;
+                }
+            }
+        }
+        return uvCoordinates;
+    }
 }

+ 118 - 118
engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/AbstractTextureBlender.java

@@ -13,124 +13,124 @@ import jme3tools.converters.MipMapGenerator;
  * @author Marcin Roguski (Kaelthas)
  */
 /* package */abstract class AbstractTextureBlender implements TextureBlender {
-	private static final Logger	LOGGER	= Logger.getLogger(AbstractTextureBlender.class.getName());
-	
-	protected int flag;
-	protected boolean negateTexture;
-	protected int blendType;
-	protected float[] materialColor;
-	protected float[] color;
-	protected float blendFactor;
-	
-	public AbstractTextureBlender(int flag, boolean negateTexture, int blendType, float[] materialColor, float[] color, float blendFactor) {
-		this.flag = flag;
-		this.negateTexture = negateTexture;
-		this.blendType = blendType;
-		this.materialColor = materialColor;
-		this.color = color;
-		this.blendFactor = blendFactor;
-	}
+    private static final Logger LOGGER = Logger.getLogger(AbstractTextureBlender.class.getName());
 
-	/**
-	 * The method that performs the ramp blending.
-	 * 
-	 * @param type
-	 *            the blend type
-	 * @param materialRGB
-	 *            the rgb value of the material, here the result is stored too
-	 * @param fac
-	 *            color affection factor
-	 * @param pixelColor
-	 *            the texture color
-	 * @param blenderContext
-	 *            the blender context
-	 */
-	protected void blendHSV(int type, float[] materialRGB, float fac, float[] pixelColor, BlenderContext blenderContext) {
-		float oneMinusFactor = 1.0f - fac;
-		MaterialHelper materialHelper = blenderContext.getHelper(MaterialHelper.class);
+    protected int               flag;
+    protected boolean           negateTexture;
+    protected int               blendType;
+    protected float[]           materialColor;
+    protected float[]           color;
+    protected float             blendFactor;
 
-		switch (type) {
-			case MTEX_BLEND_HUE: {// FIXME: not working well for image textures (works fine for generated textures)
-				float[] colorTransformResult = new float[3];
-				materialHelper.rgbToHsv(pixelColor[0], pixelColor[1], pixelColor[2], colorTransformResult);
-				if (colorTransformResult[0] != 0.0f) {
-					float colH = colorTransformResult[0];
-					materialHelper.rgbToHsv(materialRGB[0], materialRGB[1], materialRGB[2], colorTransformResult);
-					materialHelper.hsvToRgb(colH, colorTransformResult[1], colorTransformResult[2], colorTransformResult);
-					materialRGB[0] = oneMinusFactor * materialRGB[0] + fac * colorTransformResult[0];
-					materialRGB[1] = oneMinusFactor * materialRGB[1] + fac * colorTransformResult[1];
-					materialRGB[2] = oneMinusFactor * materialRGB[2] + fac * colorTransformResult[2];
-				}
-				break;
-			}
-			case MTEX_BLEND_SAT: {
-				float[] colorTransformResult = new float[3];
-				materialHelper.rgbToHsv(materialRGB[0], materialRGB[1], materialRGB[2], colorTransformResult);
-				float h = colorTransformResult[0];
-				float s = colorTransformResult[1];
-				float v = colorTransformResult[2];
-				if (s != 0.0f) {
-					materialHelper.rgbToHsv(pixelColor[0], pixelColor[1], pixelColor[2], colorTransformResult);
-					materialHelper.hsvToRgb(h, oneMinusFactor * s + fac * colorTransformResult[1], v, materialRGB);
-				}
-				break;
-			}
-			case MTEX_BLEND_VAL: {
-				float[] rgbToHsv = new float[3];
-				float[] colToHsv = new float[3];
-				materialHelper.rgbToHsv(materialRGB[0], materialRGB[1], materialRGB[2], rgbToHsv);
-				materialHelper.rgbToHsv(pixelColor[0], pixelColor[1], pixelColor[2], colToHsv);
-				materialHelper.hsvToRgb(rgbToHsv[0], rgbToHsv[1], oneMinusFactor * rgbToHsv[2] + fac * colToHsv[2], materialRGB);
-				break;
-			}
-			case MTEX_BLEND_COLOR: {// FIXME: not working well for image textures (works fine for generated textures)
-				float[] rgbToHsv = new float[3];
-				float[] colToHsv = new float[3];
-				materialHelper.rgbToHsv(pixelColor[0], pixelColor[1], pixelColor[2], colToHsv);
-				if (colToHsv[2] != 0) {
-					materialHelper.rgbToHsv(materialRGB[0], materialRGB[1], materialRGB[2], rgbToHsv);
-					materialHelper.hsvToRgb(colToHsv[0], colToHsv[1], rgbToHsv[2], rgbToHsv);
-					materialRGB[0] = oneMinusFactor * materialRGB[0] + fac * rgbToHsv[0];
-					materialRGB[1] = oneMinusFactor * materialRGB[1] + fac * rgbToHsv[1];
-					materialRGB[2] = oneMinusFactor * materialRGB[2] + fac * rgbToHsv[2];
-				}
-				break;
-			}
-			default:
-				throw new IllegalStateException("Unknown ramp type: " + type);
-		}
-	}
-	
-	public void copyBlendingData(TextureBlender textureBlender) {
-		if(textureBlender instanceof AbstractTextureBlender) {
-			this.flag = ((AbstractTextureBlender) textureBlender).flag;
-			this.negateTexture = ((AbstractTextureBlender) textureBlender).negateTexture;
-			this.blendType = ((AbstractTextureBlender) textureBlender).blendType;
-			this.materialColor = ((AbstractTextureBlender) textureBlender).materialColor.clone();
-			this.color = ((AbstractTextureBlender) textureBlender).color.clone();
-			this.blendFactor = ((AbstractTextureBlender) textureBlender).blendFactor;
-		} else {
-			LOGGER.warning("Cannot copy blending data from other types than " + this.getClass());
-		}
-	}
-	
-	/**
-	 * The method prepares images for blending. It generates mipmaps if one of
-	 * the images has them defined and the other one has not.
-	 * 
-	 * @param target
-	 *            the image where the blending result is stored
-	 * @param source
-	 *            the image that is being read only
-	 */
-	protected void prepareImagesForBlending(Image target, Image source) {
-		LOGGER.fine("Generating mipmaps if needed!");
-		boolean targetHasMipmaps = target == null ? false : target.getMipMapSizes() != null && target.getMipMapSizes().length > 0;
-		boolean sourceHasMipmaps = source == null ? false : source.getMipMapSizes() != null && source.getMipMapSizes().length > 0;
-		if (target != null && !targetHasMipmaps && sourceHasMipmaps) {
-			MipMapGenerator.generateMipMaps(target);
-		} else if (source != null && !sourceHasMipmaps && targetHasMipmaps) {
-			MipMapGenerator.generateMipMaps(source);
-		}
-	}
+    public AbstractTextureBlender(int flag, boolean negateTexture, int blendType, float[] materialColor, float[] color, float blendFactor) {
+        this.flag = flag;
+        this.negateTexture = negateTexture;
+        this.blendType = blendType;
+        this.materialColor = materialColor;
+        this.color = color;
+        this.blendFactor = blendFactor;
+    }
+
+    /**
+     * The method that performs the ramp blending.
+     * 
+     * @param type
+     *            the blend type
+     * @param materialRGB
+     *            the rgb value of the material, here the result is stored too
+     * @param fac
+     *            color affection factor
+     * @param pixelColor
+     *            the texture color
+     * @param blenderContext
+     *            the blender context
+     */
+    protected void blendHSV(int type, float[] materialRGB, float fac, float[] pixelColor, BlenderContext blenderContext) {
+        float oneMinusFactor = 1.0f - fac;
+        MaterialHelper materialHelper = blenderContext.getHelper(MaterialHelper.class);
+
+        switch (type) {
+            case MTEX_BLEND_HUE: {// FIXME: not working well for image textures (works fine for generated textures)
+                float[] colorTransformResult = new float[3];
+                materialHelper.rgbToHsv(pixelColor[0], pixelColor[1], pixelColor[2], colorTransformResult);
+                if (colorTransformResult[0] != 0.0f) {
+                    float colH = colorTransformResult[0];
+                    materialHelper.rgbToHsv(materialRGB[0], materialRGB[1], materialRGB[2], colorTransformResult);
+                    materialHelper.hsvToRgb(colH, colorTransformResult[1], colorTransformResult[2], colorTransformResult);
+                    materialRGB[0] = oneMinusFactor * materialRGB[0] + fac * colorTransformResult[0];
+                    materialRGB[1] = oneMinusFactor * materialRGB[1] + fac * colorTransformResult[1];
+                    materialRGB[2] = oneMinusFactor * materialRGB[2] + fac * colorTransformResult[2];
+                }
+                break;
+            }
+            case MTEX_BLEND_SAT: {
+                float[] colorTransformResult = new float[3];
+                materialHelper.rgbToHsv(materialRGB[0], materialRGB[1], materialRGB[2], colorTransformResult);
+                float h = colorTransformResult[0];
+                float s = colorTransformResult[1];
+                float v = colorTransformResult[2];
+                if (s != 0.0f) {
+                    materialHelper.rgbToHsv(pixelColor[0], pixelColor[1], pixelColor[2], colorTransformResult);
+                    materialHelper.hsvToRgb(h, oneMinusFactor * s + fac * colorTransformResult[1], v, materialRGB);
+                }
+                break;
+            }
+            case MTEX_BLEND_VAL: {
+                float[] rgbToHsv = new float[3];
+                float[] colToHsv = new float[3];
+                materialHelper.rgbToHsv(materialRGB[0], materialRGB[1], materialRGB[2], rgbToHsv);
+                materialHelper.rgbToHsv(pixelColor[0], pixelColor[1], pixelColor[2], colToHsv);
+                materialHelper.hsvToRgb(rgbToHsv[0], rgbToHsv[1], oneMinusFactor * rgbToHsv[2] + fac * colToHsv[2], materialRGB);
+                break;
+            }
+            case MTEX_BLEND_COLOR: {// FIXME: not working well for image textures (works fine for generated textures)
+                float[] rgbToHsv = new float[3];
+                float[] colToHsv = new float[3];
+                materialHelper.rgbToHsv(pixelColor[0], pixelColor[1], pixelColor[2], colToHsv);
+                if (colToHsv[2] != 0) {
+                    materialHelper.rgbToHsv(materialRGB[0], materialRGB[1], materialRGB[2], rgbToHsv);
+                    materialHelper.hsvToRgb(colToHsv[0], colToHsv[1], rgbToHsv[2], rgbToHsv);
+                    materialRGB[0] = oneMinusFactor * materialRGB[0] + fac * rgbToHsv[0];
+                    materialRGB[1] = oneMinusFactor * materialRGB[1] + fac * rgbToHsv[1];
+                    materialRGB[2] = oneMinusFactor * materialRGB[2] + fac * rgbToHsv[2];
+                }
+                break;
+            }
+            default:
+                throw new IllegalStateException("Unknown ramp type: " + type);
+        }
+    }
+
+    public void copyBlendingData(TextureBlender textureBlender) {
+        if (textureBlender instanceof AbstractTextureBlender) {
+            this.flag = ((AbstractTextureBlender) textureBlender).flag;
+            this.negateTexture = ((AbstractTextureBlender) textureBlender).negateTexture;
+            this.blendType = ((AbstractTextureBlender) textureBlender).blendType;
+            this.materialColor = ((AbstractTextureBlender) textureBlender).materialColor.clone();
+            this.color = ((AbstractTextureBlender) textureBlender).color.clone();
+            this.blendFactor = ((AbstractTextureBlender) textureBlender).blendFactor;
+        } else {
+            LOGGER.warning("Cannot copy blending data from other types than " + this.getClass());
+        }
+    }
+
+    /**
+     * The method prepares images for blending. It generates mipmaps if one of
+     * the images has them defined and the other one has not.
+     * 
+     * @param target
+     *            the image where the blending result is stored
+     * @param source
+     *            the image that is being read only
+     */
+    protected void prepareImagesForBlending(Image target, Image source) {
+        LOGGER.fine("Generating mipmaps if needed!");
+        boolean targetHasMipmaps = target == null ? false : target.getMipMapSizes() != null && target.getMipMapSizes().length > 0;
+        boolean sourceHasMipmaps = source == null ? false : source.getMipMapSizes() != null && source.getMipMapSizes().length > 0;
+        if (target != null && !targetHasMipmaps && sourceHasMipmaps) {
+            MipMapGenerator.generateMipMaps(target);
+        } else if (source != null && !sourceHasMipmaps && targetHasMipmaps) {
+            MipMapGenerator.generateMipMaps(source);
+        }
+    }
 }

+ 38 - 38
engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlender.java

@@ -10,44 +10,44 @@ import com.jme3.texture.Image;
  * @author Marcin Roguski (Kaelthas)
  */
 public interface TextureBlender {
-	// types of blending
-	int	MTEX_BLEND			= 0;
-	int	MTEX_MUL			= 1;
-	int	MTEX_ADD			= 2;
-	int	MTEX_SUB			= 3;
-	int	MTEX_DIV			= 4;
-	int	MTEX_DARK			= 5;
-	int	MTEX_DIFF			= 6;
-	int	MTEX_LIGHT			= 7;
-	int	MTEX_SCREEN			= 8;
-	int	MTEX_OVERLAY		= 9;
-	int	MTEX_BLEND_HUE		= 10;
-	int	MTEX_BLEND_SAT		= 11;
-	int	MTEX_BLEND_VAL		= 12;
-	int	MTEX_BLEND_COLOR	= 13;
-	int	MTEX_NUM_BLENDTYPES	= 14;
+    // types of blending
+    int MTEX_BLEND          = 0;
+    int MTEX_MUL            = 1;
+    int MTEX_ADD            = 2;
+    int MTEX_SUB            = 3;
+    int MTEX_DIV            = 4;
+    int MTEX_DARK           = 5;
+    int MTEX_DIFF           = 6;
+    int MTEX_LIGHT          = 7;
+    int MTEX_SCREEN         = 8;
+    int MTEX_OVERLAY        = 9;
+    int MTEX_BLEND_HUE      = 10;
+    int MTEX_BLEND_SAT      = 11;
+    int MTEX_BLEND_VAL      = 12;
+    int MTEX_BLEND_COLOR    = 13;
+    int MTEX_NUM_BLENDTYPES = 14;
 
-	/**
-	 * This method blends the given texture with material color and the defined
-	 * color in 'map to' panel. As a result of this method a new texture is
-	 * created. The input texture is NOT.
-	 * 
-	 * @param image
-	 *            the image we use in blending
-	 * @param baseImage
-	 *            the texture that is underneath the current texture (its pixels
-	 *            will be used instead of material color)
-	 * @param blenderContext
-	 *            the blender context
-	 * @return new image that was created after the blending
-	 */
-	Image blend(Image image, Image baseImage, BlenderContext blenderContext);
+    /**
+     * This method blends the given texture with material color and the defined
+     * color in 'map to' panel. As a result of this method a new texture is
+     * created. The input texture is NOT.
+     * 
+     * @param image
+     *            the image we use in blending
+     * @param baseImage
+     *            the texture that is underneath the current texture (its pixels
+     *            will be used instead of material color)
+     * @param blenderContext
+     *            the blender context
+     * @return new image that was created after the blending
+     */
+    Image blend(Image image, Image baseImage, BlenderContext blenderContext);
 
-	/**
-	 * Copies blending data. Used for blending type format changing.
-	 * 
-	 * @param textureBlender
-	 *            the blend data that should be copied
-	 */
-	void copyBlendingData(TextureBlender textureBlender);
+    /**
+     * Copies blending data. Used for blending type format changing.
+     * 
+     * @param textureBlender
+     *            the blend data that should be copied
+     */
+    void copyBlendingData(TextureBlender textureBlender);
 }

+ 175 - 193
engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderAWT.java

@@ -43,202 +43,184 @@ import java.nio.ByteBuffer;
 import java.util.ArrayList;
 
 /**
- * The class that is responsible for blending the following texture types:
- * <li> RGBA8
- * <li> ABGR8
- * <li> BGR8
- * <li> RGB8
- * Not yet supported (but will be):
- * <li> ARGB4444:
- * <li> RGB10:
- * <li> RGB111110F:
- * <li> RGB16:
- * <li> RGB16F:
- * <li> RGB16F_to_RGB111110F:
- * <li> RGB16F_to_RGB9E5:
- * <li> RGB32F:
- * <li> RGB565:
- * <li> RGB5A1:
- * <li> RGB9E5:
- * <li> RGBA16:
- * <li> RGBA16F
+ * The class that is responsible for blending the following texture types: <li>RGBA8 <li>ABGR8 <li>BGR8 <li>RGB8 Not yet supported (but will be): <li>ARGB4444: <li>RGB10: <li>RGB111110F: <li>RGB16: <li>RGB16F: <li>RGB16F_to_RGB111110F: <li>RGB16F_to_RGB9E5: <li>RGB32F: <li>RGB565: <li>RGB5A1: <li>RGB9E5: <li>RGBA16: <li>RGBA16F
  * @author Marcin Roguski (Kaelthas)
  */
 public class TextureBlenderAWT extends AbstractTextureBlender {
-	public TextureBlenderAWT(int flag, boolean negateTexture, int blendType, float[] materialColor, float[] color, float blendFactor) {
-		super(flag, negateTexture, blendType, materialColor, color, blendFactor);
-	}
-	
-	public Image blend(Image image, Image baseImage, BlenderContext blenderContext) {
-		this.prepareImagesForBlending(image, baseImage);
-		
-		float[] pixelColor = new float[] { color[0], color[1], color[2], 1.0f };
-		Format format = image.getFormat();
-		
-		PixelInputOutput basePixelIO = null, pixelReader = PixelIOFactory.getPixelIO(format);
-		TexturePixel basePixel = null, pixel = new TexturePixel();
-		float[] materialColor = this.materialColor;
-		if(baseImage != null) {
-			basePixelIO = PixelIOFactory.getPixelIO(baseImage.getFormat());
-			materialColor = new float[this.materialColor.length];
-			basePixel = new TexturePixel();
-		}
-		
-		int width = image.getWidth();
-		int height = image.getHeight();
-		int depth = image.getDepth();
-		if (depth == 0) {
-			depth = 1;
-		}
-		ArrayList<ByteBuffer> dataArray = new ArrayList<ByteBuffer>(depth);
-		
-		float[] resultPixel = new float[4];
-		for (int dataLayerIndex = 0; dataLayerIndex < depth; ++dataLayerIndex) {
-			ByteBuffer data = image.getData(dataLayerIndex);
-			data.rewind();
-			ByteBuffer newData = BufferUtils.createByteBuffer(width * height * 4);
-			
-			int dataIndex = 0, x = 0, y = 0, index = 0;
-			while (index < data.limit()) {
-				//getting the proper material color if the base texture is applied
-				if(basePixelIO != null) {
-					basePixelIO.read(baseImage, dataLayerIndex, basePixel, x, y);
-					basePixel.toRGBA(materialColor);
-				}			
-				
-				//reading the current texture's pixel
-				pixelReader.read(image, dataLayerIndex, pixel, index);
-				index += image.getFormat().getBitsPerPixel() >> 3;
-				pixel.toRGBA(pixelColor);
-				if (negateTexture) {
-					pixel.negate();
-				}
-				
-				this.blendPixel(resultPixel, materialColor, pixelColor, blenderContext);
-				newData.put(dataIndex++, (byte) (resultPixel[0] * 255.0f));
-				newData.put(dataIndex++, (byte) (resultPixel[1] * 255.0f));
-				newData.put(dataIndex++, (byte) (resultPixel[2] * 255.0f));
-				newData.put(dataIndex++, (byte) (pixelColor[3] * 255.0f));
-				
-				++x;
-				if(x >= width) {
-					x = 0;
-					++y;
-				}
-			}
-			dataArray.add(newData);
-		}
-		
-		Image result = depth > 1 ? new Image(Format.RGBA8, width, height, depth, dataArray) : new Image(Format.RGBA8, width, height, dataArray.get(0));
-		if(image.getMipMapSizes() != null) {
-			result.setMipMapSizes(image.getMipMapSizes().clone());
-		}
-		return result;
-	}
+    public TextureBlenderAWT(int flag, boolean negateTexture, int blendType, float[] materialColor, float[] color, float blendFactor) {
+        super(flag, negateTexture, blendType, materialColor, color, blendFactor);
+    }
 
-	/**
-	 * This method blends the single pixel depending on the blending type.
-	 * 
-	 * @param result
-	 *            the result pixel
-	 * @param materialColor
-	 *            the material color
-	 * @param pixelColor
-	 *            the pixel color
-	 * @param blenderContext
-	 *            the blender context
-	 */
-	protected void blendPixel(float[] result, float[] materialColor, float[] pixelColor, BlenderContext blenderContext) {
-		float blendFactor = this.blendFactor * pixelColor[3];
-		float oneMinusFactor = 1.0f - blendFactor, col;
+    public Image blend(Image image, Image baseImage, BlenderContext blenderContext) {
+        this.prepareImagesForBlending(image, baseImage);
 
-		switch (blendType) {
-			case MTEX_BLEND:
-				result[0] = blendFactor * pixelColor[0] + oneMinusFactor * materialColor[0];
-				result[1] = blendFactor * pixelColor[1] + oneMinusFactor * materialColor[1];
-				result[2] = blendFactor * pixelColor[2] + oneMinusFactor * materialColor[2];
-				break;
-			case MTEX_MUL:
-				result[0] = (oneMinusFactor + blendFactor * materialColor[0]) * pixelColor[0];
-				result[1] = (oneMinusFactor + blendFactor * materialColor[1]) * pixelColor[1];
-				result[2] = (oneMinusFactor + blendFactor * materialColor[2]) * pixelColor[2];
-				break;
-			case MTEX_DIV:
-				if (pixelColor[0] != 0.0) {
-					result[0] = (oneMinusFactor * materialColor[0] + blendFactor * materialColor[0] / pixelColor[0]) * 0.5f;
-				}
-				if (pixelColor[1] != 0.0) {
-					result[1] = (oneMinusFactor * materialColor[1] + blendFactor * materialColor[1] / pixelColor[1]) * 0.5f;
-				}
-				if (pixelColor[2] != 0.0) {
-					result[2] = (oneMinusFactor * materialColor[2] + blendFactor * materialColor[2] / pixelColor[2]) * 0.5f;
-				}
-				break;
-			case MTEX_SCREEN:
-				result[0] = 1.0f - (oneMinusFactor + blendFactor * (1.0f - materialColor[0])) * (1.0f - pixelColor[0]);
-				result[1] = 1.0f - (oneMinusFactor + blendFactor * (1.0f - materialColor[1])) * (1.0f - pixelColor[1]);
-				result[2] = 1.0f - (oneMinusFactor + blendFactor * (1.0f - materialColor[2])) * (1.0f - pixelColor[2]);
-				break;
-			case MTEX_OVERLAY:
-				if (materialColor[0] < 0.5f) {
-					result[0] = pixelColor[0] * (oneMinusFactor + 2.0f * blendFactor * materialColor[0]);
-				} else {
-					result[0] = 1.0f - (oneMinusFactor + 2.0f * blendFactor * (1.0f - materialColor[0])) * (1.0f - pixelColor[0]);
-				}
-				if (materialColor[1] < 0.5f) {
-					result[1] = pixelColor[1] * (oneMinusFactor + 2.0f * blendFactor * materialColor[1]);
-				} else {
-					result[1] = 1.0f - (oneMinusFactor + 2.0f * blendFactor * (1.0f - materialColor[1])) * (1.0f - pixelColor[1]);
-				}
-				if (materialColor[2] < 0.5f) {
-					result[2] = pixelColor[2] * (oneMinusFactor + 2.0f * blendFactor * materialColor[2]);
-				} else {
-					result[2] = 1.0f - (oneMinusFactor + 2.0f * blendFactor * (1.0f - materialColor[2])) * (1.0f - pixelColor[2]);
-				}
-				break;
-			case MTEX_SUB:
-				result[0] = materialColor[0] - blendFactor * pixelColor[0];
-				result[1] = materialColor[1] - blendFactor * pixelColor[1];
-				result[2] = materialColor[2] - blendFactor * pixelColor[2];
-				result[0] = FastMath.clamp(result[0], 0.0f, 1.0f);
-				result[1] = FastMath.clamp(result[1], 0.0f, 1.0f);
-				result[2] = FastMath.clamp(result[2], 0.0f, 1.0f);
-				break;
-			case MTEX_ADD:
-				result[0] = (blendFactor * pixelColor[0] + materialColor[0]) * 0.5f;
-				result[1] = (blendFactor * pixelColor[1] + materialColor[1]) * 0.5f;
-				result[2] = (blendFactor * pixelColor[2] + materialColor[2]) * 0.5f;
-				break;
-			case MTEX_DIFF:
-				result[0] = oneMinusFactor * materialColor[0] + blendFactor * Math.abs(materialColor[0] - pixelColor[0]);
-				result[1] = oneMinusFactor * materialColor[1] + blendFactor * Math.abs(materialColor[1] - pixelColor[1]);
-				result[2] = oneMinusFactor * materialColor[2] + blendFactor * Math.abs(materialColor[2] - pixelColor[2]);
-				break;
-			case MTEX_DARK:
-				col = blendFactor * pixelColor[0];
-				result[0] = col < materialColor[0] ? col : materialColor[0];
-				col = blendFactor * pixelColor[1];
-				result[1] = col < materialColor[1] ? col : materialColor[1];
-				col = blendFactor * pixelColor[2];
-				result[2] = col < materialColor[2] ? col : materialColor[2];
-				break;
-			case MTEX_LIGHT:
-				col = blendFactor * pixelColor[0];
-				result[0] = col > materialColor[0] ? col : materialColor[0];
-				col = blendFactor * pixelColor[1];
-				result[1] = col > materialColor[1] ? col : materialColor[1];
-				col = blendFactor * pixelColor[2];
-				result[2] = col > materialColor[2] ? col : materialColor[2];
-				break;
-			case MTEX_BLEND_HUE:
-			case MTEX_BLEND_SAT:
-			case MTEX_BLEND_VAL:
-			case MTEX_BLEND_COLOR:
-				System.arraycopy(materialColor, 0, result, 0, 3);
-				this.blendHSV(blendType, result, blendFactor, pixelColor, blenderContext);
-				break;
-			default:
-				throw new IllegalStateException("Unknown blend type: " + blendType);
-		}
-	}
+        float[] pixelColor = new float[] { color[0], color[1], color[2], 1.0f };
+        Format format = image.getFormat();
+
+        PixelInputOutput basePixelIO = null, pixelReader = PixelIOFactory.getPixelIO(format);
+        TexturePixel basePixel = null, pixel = new TexturePixel();
+        float[] materialColor = this.materialColor;
+        if (baseImage != null) {
+            basePixelIO = PixelIOFactory.getPixelIO(baseImage.getFormat());
+            materialColor = new float[this.materialColor.length];
+            basePixel = new TexturePixel();
+        }
+
+        int width = image.getWidth();
+        int height = image.getHeight();
+        int depth = image.getDepth();
+        if (depth == 0) {
+            depth = 1;
+        }
+        ArrayList<ByteBuffer> dataArray = new ArrayList<ByteBuffer>(depth);
+
+        float[] resultPixel = new float[4];
+        for (int dataLayerIndex = 0; dataLayerIndex < depth; ++dataLayerIndex) {
+            ByteBuffer data = image.getData(dataLayerIndex);
+            data.rewind();
+            ByteBuffer newData = BufferUtils.createByteBuffer(width * height * 4);
+
+            int dataIndex = 0, x = 0, y = 0, index = 0;
+            while (index < data.limit()) {
+                // getting the proper material color if the base texture is applied
+                if (basePixelIO != null) {
+                    basePixelIO.read(baseImage, dataLayerIndex, basePixel, x, y);
+                    basePixel.toRGBA(materialColor);
+                }
+
+                // reading the current texture's pixel
+                pixelReader.read(image, dataLayerIndex, pixel, index);
+                index += image.getFormat().getBitsPerPixel() >> 3;
+                pixel.toRGBA(pixelColor);
+                if (negateTexture) {
+                    pixel.negate();
+                }
+
+                this.blendPixel(resultPixel, materialColor, pixelColor, blenderContext);
+                newData.put(dataIndex++, (byte) (resultPixel[0] * 255.0f));
+                newData.put(dataIndex++, (byte) (resultPixel[1] * 255.0f));
+                newData.put(dataIndex++, (byte) (resultPixel[2] * 255.0f));
+                newData.put(dataIndex++, (byte) (pixelColor[3] * 255.0f));
+
+                ++x;
+                if (x >= width) {
+                    x = 0;
+                    ++y;
+                }
+            }
+            dataArray.add(newData);
+        }
+
+        Image result = depth > 1 ? new Image(Format.RGBA8, width, height, depth, dataArray) : new Image(Format.RGBA8, width, height, dataArray.get(0));
+        if (image.getMipMapSizes() != null) {
+            result.setMipMapSizes(image.getMipMapSizes().clone());
+        }
+        return result;
+    }
+
+    /**
+     * This method blends the single pixel depending on the blending type.
+     * 
+     * @param result
+     *            the result pixel
+     * @param materialColor
+     *            the material color
+     * @param pixelColor
+     *            the pixel color
+     * @param blenderContext
+     *            the blender context
+     */
+    protected void blendPixel(float[] result, float[] materialColor, float[] pixelColor, BlenderContext blenderContext) {
+        float blendFactor = this.blendFactor * pixelColor[3];
+        float oneMinusFactor = 1.0f - blendFactor, col;
+
+        switch (blendType) {
+            case MTEX_BLEND:
+                result[0] = blendFactor * pixelColor[0] + oneMinusFactor * materialColor[0];
+                result[1] = blendFactor * pixelColor[1] + oneMinusFactor * materialColor[1];
+                result[2] = blendFactor * pixelColor[2] + oneMinusFactor * materialColor[2];
+                break;
+            case MTEX_MUL:
+                result[0] = (oneMinusFactor + blendFactor * materialColor[0]) * pixelColor[0];
+                result[1] = (oneMinusFactor + blendFactor * materialColor[1]) * pixelColor[1];
+                result[2] = (oneMinusFactor + blendFactor * materialColor[2]) * pixelColor[2];
+                break;
+            case MTEX_DIV:
+                if (pixelColor[0] != 0.0) {
+                    result[0] = (oneMinusFactor * materialColor[0] + blendFactor * materialColor[0] / pixelColor[0]) * 0.5f;
+                }
+                if (pixelColor[1] != 0.0) {
+                    result[1] = (oneMinusFactor * materialColor[1] + blendFactor * materialColor[1] / pixelColor[1]) * 0.5f;
+                }
+                if (pixelColor[2] != 0.0) {
+                    result[2] = (oneMinusFactor * materialColor[2] + blendFactor * materialColor[2] / pixelColor[2]) * 0.5f;
+                }
+                break;
+            case MTEX_SCREEN:
+                result[0] = 1.0f - (oneMinusFactor + blendFactor * (1.0f - materialColor[0])) * (1.0f - pixelColor[0]);
+                result[1] = 1.0f - (oneMinusFactor + blendFactor * (1.0f - materialColor[1])) * (1.0f - pixelColor[1]);
+                result[2] = 1.0f - (oneMinusFactor + blendFactor * (1.0f - materialColor[2])) * (1.0f - pixelColor[2]);
+                break;
+            case MTEX_OVERLAY:
+                if (materialColor[0] < 0.5f) {
+                    result[0] = pixelColor[0] * (oneMinusFactor + 2.0f * blendFactor * materialColor[0]);
+                } else {
+                    result[0] = 1.0f - (oneMinusFactor + 2.0f * blendFactor * (1.0f - materialColor[0])) * (1.0f - pixelColor[0]);
+                }
+                if (materialColor[1] < 0.5f) {
+                    result[1] = pixelColor[1] * (oneMinusFactor + 2.0f * blendFactor * materialColor[1]);
+                } else {
+                    result[1] = 1.0f - (oneMinusFactor + 2.0f * blendFactor * (1.0f - materialColor[1])) * (1.0f - pixelColor[1]);
+                }
+                if (materialColor[2] < 0.5f) {
+                    result[2] = pixelColor[2] * (oneMinusFactor + 2.0f * blendFactor * materialColor[2]);
+                } else {
+                    result[2] = 1.0f - (oneMinusFactor + 2.0f * blendFactor * (1.0f - materialColor[2])) * (1.0f - pixelColor[2]);
+                }
+                break;
+            case MTEX_SUB:
+                result[0] = materialColor[0] - blendFactor * pixelColor[0];
+                result[1] = materialColor[1] - blendFactor * pixelColor[1];
+                result[2] = materialColor[2] - blendFactor * pixelColor[2];
+                result[0] = FastMath.clamp(result[0], 0.0f, 1.0f);
+                result[1] = FastMath.clamp(result[1], 0.0f, 1.0f);
+                result[2] = FastMath.clamp(result[2], 0.0f, 1.0f);
+                break;
+            case MTEX_ADD:
+                result[0] = (blendFactor * pixelColor[0] + materialColor[0]) * 0.5f;
+                result[1] = (blendFactor * pixelColor[1] + materialColor[1]) * 0.5f;
+                result[2] = (blendFactor * pixelColor[2] + materialColor[2]) * 0.5f;
+                break;
+            case MTEX_DIFF:
+                result[0] = oneMinusFactor * materialColor[0] + blendFactor * Math.abs(materialColor[0] - pixelColor[0]);
+                result[1] = oneMinusFactor * materialColor[1] + blendFactor * Math.abs(materialColor[1] - pixelColor[1]);
+                result[2] = oneMinusFactor * materialColor[2] + blendFactor * Math.abs(materialColor[2] - pixelColor[2]);
+                break;
+            case MTEX_DARK:
+                col = blendFactor * pixelColor[0];
+                result[0] = col < materialColor[0] ? col : materialColor[0];
+                col = blendFactor * pixelColor[1];
+                result[1] = col < materialColor[1] ? col : materialColor[1];
+                col = blendFactor * pixelColor[2];
+                result[2] = col < materialColor[2] ? col : materialColor[2];
+                break;
+            case MTEX_LIGHT:
+                col = blendFactor * pixelColor[0];
+                result[0] = col > materialColor[0] ? col : materialColor[0];
+                col = blendFactor * pixelColor[1];
+                result[1] = col > materialColor[1] ? col : materialColor[1];
+                col = blendFactor * pixelColor[2];
+                result[2] = col > materialColor[2] ? col : materialColor[2];
+                break;
+            case MTEX_BLEND_HUE:
+            case MTEX_BLEND_SAT:
+            case MTEX_BLEND_VAL:
+            case MTEX_BLEND_COLOR:
+                System.arraycopy(materialColor, 0, result, 0, 3);
+                this.blendHSV(blendType, result, blendFactor, pixelColor, blenderContext);
+                break;
+            default:
+                throw new IllegalStateException("Unknown blend type: " + blendType);
+        }
+    }
 }

+ 100 - 104
engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderDDS.java

@@ -12,113 +12,109 @@ import java.util.ArrayList;
 import jme3tools.converters.RGB565;
 
 /**
- * The class that is responsible for blending the following texture types:
- * <li> DXT1
- * <li> DXT1A
- * <li> DXT3
- * <li> DXT5
+ * The class that is responsible for blending the following texture types: <li>DXT1 <li>DXT1A <li>DXT3 <li>DXT5
  * @author Marcin Roguski (Kaelthas)
  */
 public class TextureBlenderDDS extends TextureBlenderAWT {
-	public TextureBlenderDDS(int flag, boolean negateTexture, int blendType, float[] materialColor, float[] color, float blendFactor) {
-		super(flag, negateTexture, blendType, materialColor, color, blendFactor);
-	}
-	
-	@Override
-	public Image blend(Image image, Image baseImage, BlenderContext blenderContext) {
-		this.prepareImagesForBlending(image, baseImage);
-		
-		Format format = image.getFormat();
-		int width = image.getWidth();
-		int height = image.getHeight();
-		int depth = image.getDepth();
-		if (depth == 0) {
-			depth = 1;
-		}
-		ArrayList<ByteBuffer> dataArray = new ArrayList<ByteBuffer>(depth);
-		
-		PixelInputOutput basePixelIO = null;
-		float[][] compressedMaterialColor = null;
-		TexturePixel[] baseTextureColors = null;
-		if(baseImage != null) {
-			basePixelIO = PixelIOFactory.getPixelIO(baseImage.getFormat());
-			compressedMaterialColor = new float[2][4];
-			baseTextureColors = new TexturePixel[] { new TexturePixel(), new TexturePixel() };
-		}
-		
-		float[] resultPixel = new float[4];
-		float[] pixelColor = new float[4];
-		TexturePixel[] colors = new TexturePixel[] { new TexturePixel(), new TexturePixel() };
-		int baseXTexelIndex = 0, baseYTexelIndex = 0;
-		float[] alphas = new float[] {1, 1};
-		for (int dataLayerIndex = 0; dataLayerIndex < depth; ++dataLayerIndex) {
-			ByteBuffer data = image.getData(dataLayerIndex);
-			data.rewind();
-			ByteBuffer newData = BufferUtils.createByteBuffer(data.remaining());
-			while (data.hasRemaining()) {
-				if(format == Format.DXT3) {
-					long alpha = data.getLong();
-					//get alpha for first and last pixel that is compressed in the texel
-					byte alpha0 = (byte)(alpha << 4 & 0xFF);
-					byte alpha1 = (byte)(alpha >> 60 & 0xFF);
-					alphas[0] = alpha0 >= 0 ? alpha0 / 255.0f : 1.0f - ~alpha0 / 255.0f;
-					alphas[1] = alpha1 >= 0 ? alpha1 / 255.0f : 1.0f - ~alpha1 / 255.0f;
-					newData.putLong(alpha);
-				} else if(format == Format.DXT5) {
-					byte alpha0 = data.get();
-					byte alpha1 = data.get();
-					alphas[0] = alpha0 >= 0 ? alpha0 / 255.0f : 1.0f - ~alpha0 / 255.0f;
-					alphas[1] = alpha1 >= 0 ? alpha0 / 255.0f : 1.0f - ~alpha0 / 255.0f;
-					newData.put(alpha0);
-					newData.put(alpha1);
-					//only read the next 6 bytes (these are alpha indexes)
-					newData.putInt(data.getInt());
-					newData.putShort(data.getShort());
-				}
-				int col0 = RGB565.RGB565_to_ARGB8(data.getShort());
-				int col1 = RGB565.RGB565_to_ARGB8(data.getShort());
-				colors[0].fromARGB8(col0);
-				colors[1].fromARGB8(col1);
-				
-				//compressing 16 pixels from the base texture as if they belonged to a texel
-				if(baseImage != null) {
-					//reading pixels (first and last of the 16 colors array)
-					basePixelIO.read(baseImage, dataLayerIndex, baseTextureColors[0], baseXTexelIndex << 2, baseYTexelIndex << 2);//first pixel
-					basePixelIO.read(baseImage, dataLayerIndex, baseTextureColors[1], baseXTexelIndex << 2 + 4, baseYTexelIndex << 2 + 4);//last pixel
-					baseTextureColors[0].toRGBA(compressedMaterialColor[0]);
-					baseTextureColors[1].toRGBA(compressedMaterialColor[1]);
-				}
+    public TextureBlenderDDS(int flag, boolean negateTexture, int blendType, float[] materialColor, float[] color, float blendFactor) {
+        super(flag, negateTexture, blendType, materialColor, color, blendFactor);
+    }
 
-				// blending colors
-				for (int i = 0; i < colors.length; ++i) {
-					if (negateTexture) {
-						colors[i].negate();
-					}
-					colors[i].toRGBA(pixelColor);
-					pixelColor[3] = alphas[i];
-					this.blendPixel(resultPixel, compressedMaterialColor != null ? compressedMaterialColor[i] : materialColor, pixelColor, blenderContext);
-					colors[i].fromARGB(1, resultPixel[0], resultPixel[1], resultPixel[2]);
-					int argb8 = colors[i].toARGB8();
-					short rgb565 = RGB565.ARGB8_to_RGB565(argb8);
-					newData.putShort(rgb565);
-				}
+    @Override
+    public Image blend(Image image, Image baseImage, BlenderContext blenderContext) {
+        this.prepareImagesForBlending(image, baseImage);
 
-				// just copy the remaining 4 bytes of the current texel
-				newData.putInt(data.getInt());
-				
-				++baseXTexelIndex;
-				if(baseXTexelIndex > image.getWidth() >> 2) {
-					baseXTexelIndex = 0;
-					++baseYTexelIndex;
-				}
-			}
-			dataArray.add(newData);
-		}
-		
-		Image result = dataArray.size() > 1 ? new Image(format, width, height, depth, dataArray) : new Image(format, width, height, dataArray.get(0));
-		if(image.getMipMapSizes() != null) {
-			result.setMipMapSizes(image.getMipMapSizes().clone());
-		}
-		return result;
-	}
+        Format format = image.getFormat();
+        int width = image.getWidth();
+        int height = image.getHeight();
+        int depth = image.getDepth();
+        if (depth == 0) {
+            depth = 1;
+        }
+        ArrayList<ByteBuffer> dataArray = new ArrayList<ByteBuffer>(depth);
+
+        PixelInputOutput basePixelIO = null;
+        float[][] compressedMaterialColor = null;
+        TexturePixel[] baseTextureColors = null;
+        if (baseImage != null) {
+            basePixelIO = PixelIOFactory.getPixelIO(baseImage.getFormat());
+            compressedMaterialColor = new float[2][4];
+            baseTextureColors = new TexturePixel[] { new TexturePixel(), new TexturePixel() };
+        }
+
+        float[] resultPixel = new float[4];
+        float[] pixelColor = new float[4];
+        TexturePixel[] colors = new TexturePixel[] { new TexturePixel(), new TexturePixel() };
+        int baseXTexelIndex = 0, baseYTexelIndex = 0;
+        float[] alphas = new float[] { 1, 1 };
+        for (int dataLayerIndex = 0; dataLayerIndex < depth; ++dataLayerIndex) {
+            ByteBuffer data = image.getData(dataLayerIndex);
+            data.rewind();
+            ByteBuffer newData = BufferUtils.createByteBuffer(data.remaining());
+            while (data.hasRemaining()) {
+                if (format == Format.DXT3) {
+                    long alpha = data.getLong();
+                    // get alpha for first and last pixel that is compressed in the texel
+                    byte alpha0 = (byte) (alpha << 4 & 0xFF);
+                    byte alpha1 = (byte) (alpha >> 60 & 0xFF);
+                    alphas[0] = alpha0 >= 0 ? alpha0 / 255.0f : 1.0f - ~alpha0 / 255.0f;
+                    alphas[1] = alpha1 >= 0 ? alpha1 / 255.0f : 1.0f - ~alpha1 / 255.0f;
+                    newData.putLong(alpha);
+                } else if (format == Format.DXT5) {
+                    byte alpha0 = data.get();
+                    byte alpha1 = data.get();
+                    alphas[0] = alpha0 >= 0 ? alpha0 / 255.0f : 1.0f - ~alpha0 / 255.0f;
+                    alphas[1] = alpha1 >= 0 ? alpha0 / 255.0f : 1.0f - ~alpha0 / 255.0f;
+                    newData.put(alpha0);
+                    newData.put(alpha1);
+                    // only read the next 6 bytes (these are alpha indexes)
+                    newData.putInt(data.getInt());
+                    newData.putShort(data.getShort());
+                }
+                int col0 = RGB565.RGB565_to_ARGB8(data.getShort());
+                int col1 = RGB565.RGB565_to_ARGB8(data.getShort());
+                colors[0].fromARGB8(col0);
+                colors[1].fromARGB8(col1);
+
+                // compressing 16 pixels from the base texture as if they belonged to a texel
+                if (baseImage != null) {
+                    // reading pixels (first and last of the 16 colors array)
+                    basePixelIO.read(baseImage, dataLayerIndex, baseTextureColors[0], baseXTexelIndex << 2, baseYTexelIndex << 2);// first pixel
+                    basePixelIO.read(baseImage, dataLayerIndex, baseTextureColors[1], baseXTexelIndex << 2 + 4, baseYTexelIndex << 2 + 4);// last pixel
+                    baseTextureColors[0].toRGBA(compressedMaterialColor[0]);
+                    baseTextureColors[1].toRGBA(compressedMaterialColor[1]);
+                }
+
+                // blending colors
+                for (int i = 0; i < colors.length; ++i) {
+                    if (negateTexture) {
+                        colors[i].negate();
+                    }
+                    colors[i].toRGBA(pixelColor);
+                    pixelColor[3] = alphas[i];
+                    this.blendPixel(resultPixel, compressedMaterialColor != null ? compressedMaterialColor[i] : materialColor, pixelColor, blenderContext);
+                    colors[i].fromARGB(1, resultPixel[0], resultPixel[1], resultPixel[2]);
+                    int argb8 = colors[i].toARGB8();
+                    short rgb565 = RGB565.ARGB8_to_RGB565(argb8);
+                    newData.putShort(rgb565);
+                }
+
+                // just copy the remaining 4 bytes of the current texel
+                newData.putInt(data.getInt());
+
+                ++baseXTexelIndex;
+                if (baseXTexelIndex > image.getWidth() >> 2) {
+                    baseXTexelIndex = 0;
+                    ++baseYTexelIndex;
+                }
+            }
+            dataArray.add(newData);
+        }
+
+        Image result = dataArray.size() > 1 ? new Image(format, width, height, depth, dataArray) : new Image(format, width, height, dataArray.get(0));
+        if (image.getMipMapSizes() != null) {
+            result.setMipMapSizes(image.getMipMapSizes().clone());
+        }
+        return result;
+    }
 }

+ 79 - 79
engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderFactory.java

@@ -43,86 +43,86 @@ import java.util.logging.Logger;
  * @author Marcin Roguski (Kaelthas)
  */
 public class TextureBlenderFactory {
-	private static final Logger	LOGGER	= Logger.getLogger(TextureBlenderFactory.class.getName());
+    private static final Logger LOGGER = Logger.getLogger(TextureBlenderFactory.class.getName());
 
-	/**
-	 * This method creates the blending class.
-	 * 
-	 * @param format
-	 *            the texture format
-	 * @return texture blending class
-	 */
-	public static TextureBlender createTextureBlender(Format format, int flag, boolean negate, int blendType, float[] materialColor, float[] color, float colfac) {
-		switch (format) {
-			case Luminance8:
-			case Luminance8Alpha8:
-			case Luminance16:
-			case Luminance16Alpha16:
-			case Luminance16F:
-			case Luminance16FAlpha16F:
-			case Luminance32F:
-				return new TextureBlenderLuminance(flag, negate, blendType, materialColor, color, colfac);
-			case RGBA8:
-			case ABGR8:
-			case BGR8:
-			case RGB8:
-			case RGB10:
-			case RGB111110F:
-			case RGB16:
-			case RGB16F:
-			case RGB16F_to_RGB111110F:
-			case RGB16F_to_RGB9E5:
-			case RGB32F:
-			case RGB565:
-			case RGB5A1:
-			case RGB9E5:
-			case RGBA16:
-			case RGBA16F:
-			case RGBA32F:
-				return new TextureBlenderAWT(flag, negate, blendType, materialColor, color, colfac);
-			case DXT1:
-			case DXT1A:
-			case DXT3:
-			case DXT5:
-				return new TextureBlenderDDS(flag, negate, blendType, materialColor, color, colfac);
-			case Alpha16:
-			case Alpha8:
-			case ARGB4444:
-			case Depth:
-			case Depth16:
-			case Depth24:
-			case Depth32:
-			case Depth32F:
-			case Intensity16:
-			case Intensity8:
-			case LATC:
-			case LTC:
-				LOGGER.log(Level.WARNING, "Image type not yet supported for blending: {0}. Returning a blender that does not change the texture.", format);
-				return new TextureBlender() {
-					public Image blend(Image image, Image baseImage, BlenderContext blenderContext) {
-						return image;
-					}
+    /**
+     * This method creates the blending class.
+     * 
+     * @param format
+     *            the texture format
+     * @return texture blending class
+     */
+    public static TextureBlender createTextureBlender(Format format, int flag, boolean negate, int blendType, float[] materialColor, float[] color, float colfac) {
+        switch (format) {
+            case Luminance8:
+            case Luminance8Alpha8:
+            case Luminance16:
+            case Luminance16Alpha16:
+            case Luminance16F:
+            case Luminance16FAlpha16F:
+            case Luminance32F:
+                return new TextureBlenderLuminance(flag, negate, blendType, materialColor, color, colfac);
+            case RGBA8:
+            case ABGR8:
+            case BGR8:
+            case RGB8:
+            case RGB10:
+            case RGB111110F:
+            case RGB16:
+            case RGB16F:
+            case RGB16F_to_RGB111110F:
+            case RGB16F_to_RGB9E5:
+            case RGB32F:
+            case RGB565:
+            case RGB5A1:
+            case RGB9E5:
+            case RGBA16:
+            case RGBA16F:
+            case RGBA32F:
+                return new TextureBlenderAWT(flag, negate, blendType, materialColor, color, colfac);
+            case DXT1:
+            case DXT1A:
+            case DXT3:
+            case DXT5:
+                return new TextureBlenderDDS(flag, negate, blendType, materialColor, color, colfac);
+            case Alpha16:
+            case Alpha8:
+            case ARGB4444:
+            case Depth:
+            case Depth16:
+            case Depth24:
+            case Depth32:
+            case Depth32F:
+            case Intensity16:
+            case Intensity8:
+            case LATC:
+            case LTC:
+                LOGGER.log(Level.WARNING, "Image type not yet supported for blending: {0}. Returning a blender that does not change the texture.", format);
+                return new TextureBlender() {
+                    public Image blend(Image image, Image baseImage, BlenderContext blenderContext) {
+                        return image;
+                    }
 
-					public void copyBlendingData(TextureBlender textureBlender) {
-					}
-				};
-			default:
-				throw new IllegalStateException("Unknown image format type: " + format);
-		}
-	}
+                    public void copyBlendingData(TextureBlender textureBlender) {
+                    }
+                };
+            default:
+                throw new IllegalStateException("Unknown image format type: " + format);
+        }
+    }
 
-	/**
-	 * This method changes the image format in the texture blender.
-	 * 
-	 * @param format
-	 *            the new image format
-	 * @param textureBlender
-	 *            the texture blender that will be altered
-	 * @return altered texture blender
-	 */
-	public static TextureBlender alterTextureType(Format format, TextureBlender textureBlender) {
-		TextureBlender result = TextureBlenderFactory.createTextureBlender(format, 0, false, 0, null, null, 0);
-		result.copyBlendingData(textureBlender);
-		return result;
-	}
+    /**
+     * This method changes the image format in the texture blender.
+     * 
+     * @param format
+     *            the new image format
+     * @param textureBlender
+     *            the texture blender that will be altered
+     * @return altered texture blender
+     */
+    public static TextureBlender alterTextureType(Format format, TextureBlender textureBlender) {
+        TextureBlender result = TextureBlenderFactory.createTextureBlender(format, 0, false, 0, null, null, 0);
+        result.copyBlendingData(textureBlender);
+        return result;
+    }
 }

+ 216 - 224
engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderLuminance.java

@@ -14,236 +14,228 @@ import java.util.logging.Level;
 import java.util.logging.Logger;
 
 /**
- * The class that is responsible for blending the following texture types:
- * <li> Luminance8
- * <li> Luminance8Alpha8
- * Not yet supported (but will be):
- * <li> Luminance16:
- * <li> Luminance16Alpha16:
- * <li> Luminance16F:
- * <li> Luminance16FAlpha16F:
- * <li> Luminance32F:
+ * The class that is responsible for blending the following texture types: <li>Luminance8 <li>Luminance8Alpha8 Not yet supported (but will be): <li>Luminance16: <li>Luminance16Alpha16: <li>Luminance16F: <li>Luminance16FAlpha16F: <li>Luminance32F:
  * @author Marcin Roguski (Kaelthas)
  */
 public class TextureBlenderLuminance extends AbstractTextureBlender {
-	private static final Logger	LOGGER	= Logger.getLogger(TextureBlenderLuminance.class.getName());
+    private static final Logger LOGGER = Logger.getLogger(TextureBlenderLuminance.class.getName());
 
-	public TextureBlenderLuminance(int flag, boolean negateTexture, int blendType, float[] materialColor, float[] color, float blendFactor) {
-		super(flag, negateTexture, blendType, materialColor, color, blendFactor);
-	}
-	
-	public Image blend(Image image, Image baseImage, BlenderContext blenderContext) {
-		this.prepareImagesForBlending(image, baseImage);
-		
-		Format format = image.getFormat();
-		PixelInputOutput basePixelIO = null;
-		TexturePixel basePixel = null;
-		float[] materialColor = this.materialColor;
-		if(baseImage != null) {
-			basePixelIO = PixelIOFactory.getPixelIO(baseImage.getFormat());
-			materialColor = new float[this.materialColor.length];
-			basePixel = new TexturePixel();
-		}
-		
-		int width = image.getWidth();
-		int height = image.getHeight();
-		int depth = image.getDepth();
-		if (depth == 0) {
-			depth = 1;
-		}
-		ArrayList<ByteBuffer> dataArray = new ArrayList<ByteBuffer>(depth);
+    public TextureBlenderLuminance(int flag, boolean negateTexture, int blendType, float[] materialColor, float[] color, float blendFactor) {
+        super(flag, negateTexture, blendType, materialColor, color, blendFactor);
+    }
 
-		float[] resultPixel = new float[4];
-		float[] tinAndAlpha = new float[2];
-		for (int dataLayerIndex = 0; dataLayerIndex < depth; ++dataLayerIndex) {
-			ByteBuffer data = image.getData(dataLayerIndex);
-			data.rewind();
-			ByteBuffer newData = BufferUtils.createByteBuffer(width * height * 4);
-			
-			int dataIndex = 0, x = 0, y = 0;
-			while (data.hasRemaining()) {
-				//getting the proper material color if the base texture is applied
-				if(basePixelIO != null) {
-					basePixelIO.read(baseImage, dataLayerIndex, basePixel, x, y);
-					basePixel.toRGBA(materialColor);
-				}
-				
-				this.getTinAndAlpha(data, format, negateTexture, tinAndAlpha);
-				this.blendPixel(resultPixel, materialColor, color, tinAndAlpha[0], blendFactor, blendType, blenderContext);
-				newData.put(dataIndex++, (byte) (resultPixel[0] * 255.0f));
-				newData.put(dataIndex++, (byte) (resultPixel[1] * 255.0f));
-				newData.put(dataIndex++, (byte) (resultPixel[2] * 255.0f));
-				newData.put(dataIndex++, (byte) (tinAndAlpha[1] * 255.0f));
-				
-				++x;
-				if(x >= width) {
-					x = 0;
-					++y;
-				}
-			}
-			dataArray.add(newData);
-		}
-		
-		Image result = depth > 1 ? new Image(Format.RGBA8, width, height, depth, dataArray) : new Image(Format.RGBA8, width, height, dataArray.get(0));
-		if(image.getMipMapSizes() != null) {
-			result.setMipMapSizes(image.getMipMapSizes().clone());
-		}
-		return result;
-	}
+    public Image blend(Image image, Image baseImage, BlenderContext blenderContext) {
+        this.prepareImagesForBlending(image, baseImage);
 
-	/**
-	 * This method return texture intensity and alpha value.
-	 * 
-	 * @param data
-	 *            the texture data
-	 * @param imageFormat
-	 *            the image format
-	 * @param neg
-	 *            indicates if the texture is negated
-	 * @param result
-	 *            the table (2 elements) where the result is being stored
-	 */
-	protected void getTinAndAlpha(ByteBuffer data, Format imageFormat, boolean neg, float[] result) {
-		byte pixelValue = data.get();// at least one byte is always taken
-		float firstPixelValue = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - ~pixelValue / 255.0f;
-		switch (imageFormat) {
-			case Luminance8:
-				result[0] = neg ? 1.0f - firstPixelValue : firstPixelValue;
-				result[1] = 1.0f;
-				break;
-			case Luminance8Alpha8:
-				result[0] = neg ? 1.0f - firstPixelValue : firstPixelValue;
-				pixelValue = data.get();
-				result[1] = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - ~pixelValue / 255.0f;
-				break;
-			case Luminance16:
-			case Luminance16Alpha16:
-			case Luminance16F:
-			case Luminance16FAlpha16F:
-			case Luminance32F:
-				LOGGER.log(Level.WARNING, "Image type not yet supported for blending: {0}", imageFormat);
-				break;
-			default:
-				throw new IllegalStateException("Invalid image format type for Luminance texture blender: " + imageFormat);
-		}
-	}
+        Format format = image.getFormat();
+        PixelInputOutput basePixelIO = null;
+        TexturePixel basePixel = null;
+        float[] materialColor = this.materialColor;
+        if (baseImage != null) {
+            basePixelIO = PixelIOFactory.getPixelIO(baseImage.getFormat());
+            materialColor = new float[this.materialColor.length];
+            basePixel = new TexturePixel();
+        }
 
-	/**
-	 * This method blends the texture with an appropriate color.
-	 * 
-	 * @param result
-	 *            the result color (variable 'in' in blender source code)
-	 * @param materialColor
-	 *            the texture color (variable 'out' in blender source coude)
-	 * @param color
-	 *            the previous color (variable 'tex' in blender source code)
-	 * @param textureIntensity
-	 *            texture intensity (variable 'fact' in blender source code)
-	 * @param textureFactor
-	 *            texture affection factor (variable 'facg' in blender source
-	 *            code)
-	 * @param blendtype
-	 *            the blend type
-	 * @param blenderContext
-	 *            the blender context
-	 */
-	protected void blendPixel(float[] result, float[] materialColor, float[] color, float textureIntensity, float textureFactor, int blendtype, BlenderContext blenderContext) {
-		float oneMinusFactor, col;
-		textureIntensity *= textureFactor;
+        int width = image.getWidth();
+        int height = image.getHeight();
+        int depth = image.getDepth();
+        if (depth == 0) {
+            depth = 1;
+        }
+        ArrayList<ByteBuffer> dataArray = new ArrayList<ByteBuffer>(depth);
 
-		switch (blendtype) {
-			case MTEX_BLEND:
-				oneMinusFactor = 1.0f - textureIntensity;
-				result[0] = textureIntensity * color[0] + oneMinusFactor * materialColor[0];
-				result[1] = textureIntensity * color[1] + oneMinusFactor * materialColor[1];
-				result[2] = textureIntensity * color[2] + oneMinusFactor * materialColor[2];
-				break;
-			case MTEX_MUL:
-				oneMinusFactor = 1.0f - textureFactor;
-				result[0] = (oneMinusFactor + textureIntensity * materialColor[0]) * color[0];
-				result[1] = (oneMinusFactor + textureIntensity * materialColor[1]) * color[1];
-				result[2] = (oneMinusFactor + textureIntensity * materialColor[2]) * color[2];
-				break;
-			case MTEX_DIV:
-				oneMinusFactor = 1.0f - textureIntensity;
-				if (color[0] != 0.0) {
-					result[0] = (oneMinusFactor * materialColor[0] + textureIntensity * materialColor[0] / color[0]) * 0.5f;
-				}
-				if (color[1] != 0.0) {
-					result[1] = (oneMinusFactor * materialColor[1] + textureIntensity * materialColor[1] / color[1]) * 0.5f;
-				}
-				if (color[2] != 0.0) {
-					result[2] = (oneMinusFactor * materialColor[2] + textureIntensity * materialColor[2] / color[2]) * 0.5f;
-				}
-				break;
-			case MTEX_SCREEN:
-				oneMinusFactor = 1.0f - textureFactor;
-				result[0] = 1.0f - (oneMinusFactor + textureIntensity * (1.0f - materialColor[0])) * (1.0f - color[0]);
-				result[1] = 1.0f - (oneMinusFactor + textureIntensity * (1.0f - materialColor[1])) * (1.0f - color[1]);
-				result[2] = 1.0f - (oneMinusFactor + textureIntensity * (1.0f - materialColor[2])) * (1.0f - color[2]);
-				break;
-			case MTEX_OVERLAY:
-				oneMinusFactor = 1.0f - textureFactor;
-				if (materialColor[0] < 0.5f) {
-					result[0] = color[0] * (oneMinusFactor + 2.0f * textureIntensity * materialColor[0]);
-				} else {
-					result[0] = 1.0f - (oneMinusFactor + 2.0f * textureIntensity * (1.0f - materialColor[0])) * (1.0f - color[0]);
-				}
-				if (materialColor[1] < 0.5f) {
-					result[1] = color[1] * (oneMinusFactor + 2.0f * textureIntensity * materialColor[1]);
-				} else {
-					result[1] = 1.0f - (oneMinusFactor + 2.0f * textureIntensity * (1.0f - materialColor[1])) * (1.0f - color[1]);
-				}
-				if (materialColor[2] < 0.5f) {
-					result[2] = color[2] * (oneMinusFactor + 2.0f * textureIntensity * materialColor[2]);
-				} else {
-					result[2] = 1.0f - (oneMinusFactor + 2.0f * textureIntensity * (1.0f - materialColor[2])) * (1.0f - color[2]);
-				}
-				break;
-			case MTEX_SUB:
-				result[0] = materialColor[0] - textureIntensity * color[0];
-				result[1] = materialColor[1] - textureIntensity * color[1];
-				result[2] = materialColor[2] - textureIntensity * color[2];
-				result[0] = FastMath.clamp(result[0], 0.0f, 1.0f);
-				result[1] = FastMath.clamp(result[1], 0.0f, 1.0f);
-				result[2] = FastMath.clamp(result[2], 0.0f, 1.0f);
-				break;
-			case MTEX_ADD:
-				result[0] = (textureIntensity * color[0] + materialColor[0]) * 0.5f;
-				result[1] = (textureIntensity * color[1] + materialColor[1]) * 0.5f;
-				result[2] = (textureIntensity * color[2] + materialColor[2]) * 0.5f;
-				break;
-			case MTEX_DIFF:
-				oneMinusFactor = 1.0f - textureIntensity;
-				result[0] = oneMinusFactor * materialColor[0] + textureIntensity * Math.abs(materialColor[0] - color[0]);
-				result[1] = oneMinusFactor * materialColor[1] + textureIntensity * Math.abs(materialColor[1] - color[1]);
-				result[2] = oneMinusFactor * materialColor[2] + textureIntensity * Math.abs(materialColor[2] - color[2]);
-				break;
-			case MTEX_DARK:
-				col = textureIntensity * color[0];
-				result[0] = col < materialColor[0] ? col : materialColor[0];
-				col = textureIntensity * color[1];
-				result[1] = col < materialColor[1] ? col : materialColor[1];
-				col = textureIntensity * color[2];
-				result[2] = col < materialColor[2] ? col : materialColor[2];
-				break;
-			case MTEX_LIGHT:
-				col = textureIntensity * color[0];
-				result[0] = col > materialColor[0] ? col : materialColor[0];
-				col = textureIntensity * color[1];
-				result[1] = col > materialColor[1] ? col : materialColor[1];
-				col = textureIntensity * color[2];
-				result[2] = col > materialColor[2] ? col : materialColor[2];
-				break;
-			case MTEX_BLEND_HUE:
-			case MTEX_BLEND_SAT:
-			case MTEX_BLEND_VAL:
-			case MTEX_BLEND_COLOR:
-				System.arraycopy(materialColor, 0, result, 0, 3);
-				this.blendHSV(blendtype, result, textureIntensity, color, blenderContext);
-				break;
-			default:
-				throw new IllegalStateException("Unknown blend type: " + blendtype);
-		}
-	}
+        float[] resultPixel = new float[4];
+        float[] tinAndAlpha = new float[2];
+        for (int dataLayerIndex = 0; dataLayerIndex < depth; ++dataLayerIndex) {
+            ByteBuffer data = image.getData(dataLayerIndex);
+            data.rewind();
+            ByteBuffer newData = BufferUtils.createByteBuffer(width * height * 4);
+
+            int dataIndex = 0, x = 0, y = 0;
+            while (data.hasRemaining()) {
+                // getting the proper material color if the base texture is applied
+                if (basePixelIO != null) {
+                    basePixelIO.read(baseImage, dataLayerIndex, basePixel, x, y);
+                    basePixel.toRGBA(materialColor);
+                }
+
+                this.getTinAndAlpha(data, format, negateTexture, tinAndAlpha);
+                this.blendPixel(resultPixel, materialColor, color, tinAndAlpha[0], blendFactor, blendType, blenderContext);
+                newData.put(dataIndex++, (byte) (resultPixel[0] * 255.0f));
+                newData.put(dataIndex++, (byte) (resultPixel[1] * 255.0f));
+                newData.put(dataIndex++, (byte) (resultPixel[2] * 255.0f));
+                newData.put(dataIndex++, (byte) (tinAndAlpha[1] * 255.0f));
+
+                ++x;
+                if (x >= width) {
+                    x = 0;
+                    ++y;
+                }
+            }
+            dataArray.add(newData);
+        }
+
+        Image result = depth > 1 ? new Image(Format.RGBA8, width, height, depth, dataArray) : new Image(Format.RGBA8, width, height, dataArray.get(0));
+        if (image.getMipMapSizes() != null) {
+            result.setMipMapSizes(image.getMipMapSizes().clone());
+        }
+        return result;
+    }
+
+    /**
+     * This method return texture intensity and alpha value.
+     * 
+     * @param data
+     *            the texture data
+     * @param imageFormat
+     *            the image format
+     * @param neg
+     *            indicates if the texture is negated
+     * @param result
+     *            the table (2 elements) where the result is being stored
+     */
+    protected void getTinAndAlpha(ByteBuffer data, Format imageFormat, boolean neg, float[] result) {
+        byte pixelValue = data.get();// at least one byte is always taken
+        float firstPixelValue = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - ~pixelValue / 255.0f;
+        switch (imageFormat) {
+            case Luminance8:
+                result[0] = neg ? 1.0f - firstPixelValue : firstPixelValue;
+                result[1] = 1.0f;
+                break;
+            case Luminance8Alpha8:
+                result[0] = neg ? 1.0f - firstPixelValue : firstPixelValue;
+                pixelValue = data.get();
+                result[1] = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - ~pixelValue / 255.0f;
+                break;
+            case Luminance16:
+            case Luminance16Alpha16:
+            case Luminance16F:
+            case Luminance16FAlpha16F:
+            case Luminance32F:
+                LOGGER.log(Level.WARNING, "Image type not yet supported for blending: {0}", imageFormat);
+                break;
+            default:
+                throw new IllegalStateException("Invalid image format type for Luminance texture blender: " + imageFormat);
+        }
+    }
+
+    /**
+     * This method blends the texture with an appropriate color.
+     * 
+     * @param result
+     *            the result color (variable 'in' in blender source code)
+     * @param materialColor
+     *            the texture color (variable 'out' in blender source coude)
+     * @param color
+     *            the previous color (variable 'tex' in blender source code)
+     * @param textureIntensity
+     *            texture intensity (variable 'fact' in blender source code)
+     * @param textureFactor
+     *            texture affection factor (variable 'facg' in blender source
+     *            code)
+     * @param blendtype
+     *            the blend type
+     * @param blenderContext
+     *            the blender context
+     */
+    protected void blendPixel(float[] result, float[] materialColor, float[] color, float textureIntensity, float textureFactor, int blendtype, BlenderContext blenderContext) {
+        float oneMinusFactor, col;
+        textureIntensity *= textureFactor;
+
+        switch (blendtype) {
+            case MTEX_BLEND:
+                oneMinusFactor = 1.0f - textureIntensity;
+                result[0] = textureIntensity * color[0] + oneMinusFactor * materialColor[0];
+                result[1] = textureIntensity * color[1] + oneMinusFactor * materialColor[1];
+                result[2] = textureIntensity * color[2] + oneMinusFactor * materialColor[2];
+                break;
+            case MTEX_MUL:
+                oneMinusFactor = 1.0f - textureFactor;
+                result[0] = (oneMinusFactor + textureIntensity * materialColor[0]) * color[0];
+                result[1] = (oneMinusFactor + textureIntensity * materialColor[1]) * color[1];
+                result[2] = (oneMinusFactor + textureIntensity * materialColor[2]) * color[2];
+                break;
+            case MTEX_DIV:
+                oneMinusFactor = 1.0f - textureIntensity;
+                if (color[0] != 0.0) {
+                    result[0] = (oneMinusFactor * materialColor[0] + textureIntensity * materialColor[0] / color[0]) * 0.5f;
+                }
+                if (color[1] != 0.0) {
+                    result[1] = (oneMinusFactor * materialColor[1] + textureIntensity * materialColor[1] / color[1]) * 0.5f;
+                }
+                if (color[2] != 0.0) {
+                    result[2] = (oneMinusFactor * materialColor[2] + textureIntensity * materialColor[2] / color[2]) * 0.5f;
+                }
+                break;
+            case MTEX_SCREEN:
+                oneMinusFactor = 1.0f - textureFactor;
+                result[0] = 1.0f - (oneMinusFactor + textureIntensity * (1.0f - materialColor[0])) * (1.0f - color[0]);
+                result[1] = 1.0f - (oneMinusFactor + textureIntensity * (1.0f - materialColor[1])) * (1.0f - color[1]);
+                result[2] = 1.0f - (oneMinusFactor + textureIntensity * (1.0f - materialColor[2])) * (1.0f - color[2]);
+                break;
+            case MTEX_OVERLAY:
+                oneMinusFactor = 1.0f - textureFactor;
+                if (materialColor[0] < 0.5f) {
+                    result[0] = color[0] * (oneMinusFactor + 2.0f * textureIntensity * materialColor[0]);
+                } else {
+                    result[0] = 1.0f - (oneMinusFactor + 2.0f * textureIntensity * (1.0f - materialColor[0])) * (1.0f - color[0]);
+                }
+                if (materialColor[1] < 0.5f) {
+                    result[1] = color[1] * (oneMinusFactor + 2.0f * textureIntensity * materialColor[1]);
+                } else {
+                    result[1] = 1.0f - (oneMinusFactor + 2.0f * textureIntensity * (1.0f - materialColor[1])) * (1.0f - color[1]);
+                }
+                if (materialColor[2] < 0.5f) {
+                    result[2] = color[2] * (oneMinusFactor + 2.0f * textureIntensity * materialColor[2]);
+                } else {
+                    result[2] = 1.0f - (oneMinusFactor + 2.0f * textureIntensity * (1.0f - materialColor[2])) * (1.0f - color[2]);
+                }
+                break;
+            case MTEX_SUB:
+                result[0] = materialColor[0] - textureIntensity * color[0];
+                result[1] = materialColor[1] - textureIntensity * color[1];
+                result[2] = materialColor[2] - textureIntensity * color[2];
+                result[0] = FastMath.clamp(result[0], 0.0f, 1.0f);
+                result[1] = FastMath.clamp(result[1], 0.0f, 1.0f);
+                result[2] = FastMath.clamp(result[2], 0.0f, 1.0f);
+                break;
+            case MTEX_ADD:
+                result[0] = (textureIntensity * color[0] + materialColor[0]) * 0.5f;
+                result[1] = (textureIntensity * color[1] + materialColor[1]) * 0.5f;
+                result[2] = (textureIntensity * color[2] + materialColor[2]) * 0.5f;
+                break;
+            case MTEX_DIFF:
+                oneMinusFactor = 1.0f - textureIntensity;
+                result[0] = oneMinusFactor * materialColor[0] + textureIntensity * Math.abs(materialColor[0] - color[0]);
+                result[1] = oneMinusFactor * materialColor[1] + textureIntensity * Math.abs(materialColor[1] - color[1]);
+                result[2] = oneMinusFactor * materialColor[2] + textureIntensity * Math.abs(materialColor[2] - color[2]);
+                break;
+            case MTEX_DARK:
+                col = textureIntensity * color[0];
+                result[0] = col < materialColor[0] ? col : materialColor[0];
+                col = textureIntensity * color[1];
+                result[1] = col < materialColor[1] ? col : materialColor[1];
+                col = textureIntensity * color[2];
+                result[2] = col < materialColor[2] ? col : materialColor[2];
+                break;
+            case MTEX_LIGHT:
+                col = textureIntensity * color[0];
+                result[0] = col > materialColor[0] ? col : materialColor[0];
+                col = textureIntensity * color[1];
+                result[1] = col > materialColor[1] ? col : materialColor[1];
+                col = textureIntensity * color[2];
+                result[2] = col > materialColor[2] ? col : materialColor[2];
+                break;
+            case MTEX_BLEND_HUE:
+            case MTEX_BLEND_SAT:
+            case MTEX_BLEND_VAL:
+            case MTEX_BLEND_COLOR:
+                System.arraycopy(materialColor, 0, result, 0, 3);
+                this.blendHSV(blendtype, result, textureIntensity, color, blenderContext);
+                break;
+            default:
+                throw new IllegalStateException("Unknown blend type: " + blendtype);
+        }
+    }
 }

+ 138 - 145
engine/src/blender/com/jme3/scene/plugins/blender/textures/generating/NoiseGenerator.java

@@ -51,37 +51,37 @@ import java.util.logging.Logger;
  * It is only used by TextureHelper.
  * @author Marcin Roguski (Kaelthas)
  */
-/*package*/ class NoiseGenerator extends AbstractBlenderHelper {
-    private static final Logger LOGGER = Logger.getLogger(NoiseGenerator.class.getName());
-    
+/* package */class NoiseGenerator extends AbstractBlenderHelper {
+    private static final Logger LOGGER       = Logger.getLogger(NoiseGenerator.class.getName());
+
     // tex->stype
-    protected static final int TEX_PLASTIC = 0;
-    protected static final int TEX_WALLIN = 1;
-    protected static final int TEX_WALLOUT = 2;
+    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;
+    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;
+    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;
+    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
+     *            the number of blender version
      */
     public NoiseGenerator(String blenderVersion) {
         super(blenderVersion, false);
@@ -116,11 +116,11 @@ import java.util.logging.Logger;
             }
         }
     }
-    
-    protected static Map<Integer, NoiseFunction> noiseFunctions = new HashMap<Integer, NoiseFunction>();
+
+    protected static Map<Integer, NoiseFunction>    noiseFunctions    = new HashMap<Integer, NoiseFunction>();
     static {
         noiseFunctions.put(Integer.valueOf(0), new NoiseFunction() {
-        	// originalBlenderNoise
+            // originalBlenderNoise
             public float execute(float x, float y, float z) {
                 return NoiseFunctions.originalBlenderNoise(x, y, z);
             }
@@ -130,7 +130,7 @@ import java.util.logging.Logger;
             }
         });
         noiseFunctions.put(Integer.valueOf(1), new NoiseFunction() {
-        	// orgPerlinNoise
+            // orgPerlinNoise
             public float execute(float x, float y, float z) {
                 return 0.5f + 0.5f * NoiseFunctions.noise3Perlin(x, y, z);
             }
@@ -140,7 +140,7 @@ import java.util.logging.Logger;
             }
         });
         noiseFunctions.put(Integer.valueOf(2), new NoiseFunction() {
-        	// newPerlin
+            // newPerlin
             public float execute(float x, float y, float z) {
                 return 0.5f + 0.5f * NoiseFunctions.newPerlin(x, y, z);
             }
@@ -150,7 +150,7 @@ import java.util.logging.Logger;
             }
         });
         noiseFunctions.put(Integer.valueOf(3), new NoiseFunction() {
-        	// voronoi_F1
+            // voronoi_F1
             public float execute(float x, float y, float z) {
                 float[] da = new float[4], pa = new float[12];
                 NoiseFunctions.voronoi(x, y, z, da, pa, 1, 0);
@@ -164,7 +164,7 @@ import java.util.logging.Logger;
             }
         });
         noiseFunctions.put(Integer.valueOf(4), new NoiseFunction() {
-        	// voronoi_F2
+            // voronoi_F2
             public float execute(float x, float y, float z) {
                 float[] da = new float[4], pa = new float[12];
                 NoiseFunctions.voronoi(x, y, z, da, pa, 1, 0);
@@ -178,7 +178,7 @@ import java.util.logging.Logger;
             }
         });
         noiseFunctions.put(Integer.valueOf(5), new NoiseFunction() {
-        	// voronoi_F3
+            // voronoi_F3
             public float execute(float x, float y, float z) {
                 float[] da = new float[4], pa = new float[12];
                 NoiseFunctions.voronoi(x, y, z, da, pa, 1, 0);
@@ -192,7 +192,7 @@ import java.util.logging.Logger;
             }
         });
         noiseFunctions.put(Integer.valueOf(6), new NoiseFunction() {
-        	// voronoi_F4
+            // voronoi_F4
             public float execute(float x, float y, float z) {
                 float[] da = new float[4], pa = new float[12];
                 NoiseFunctions.voronoi(x, y, z, da, pa, 1, 0);
@@ -206,7 +206,7 @@ import java.util.logging.Logger;
             }
         });
         noiseFunctions.put(Integer.valueOf(7), new NoiseFunction() {
-        	// voronoi_F1F2
+            // voronoi_F1F2
             public float execute(float x, float y, float z) {
                 float[] da = new float[4], pa = new float[12];
                 NoiseFunctions.voronoi(x, y, z, da, pa, 1, 0);
@@ -220,7 +220,7 @@ import java.util.logging.Logger;
             }
         });
         noiseFunctions.put(Integer.valueOf(8), new NoiseFunction() {
-        	// voronoi_Cr
+            // voronoi_Cr
             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;
@@ -232,7 +232,7 @@ import java.util.logging.Logger;
             }
         });
         noiseFunctions.put(Integer.valueOf(14), new NoiseFunction() {
-        	// cellNoise
+            // cellNoise
             public float execute(float x, float y, float z) {
                 int xi = (int) Math.floor(x);
                 int yi = (int) Math.floor(y);
@@ -252,25 +252,25 @@ import java.util.logging.Logger;
 
     static {
         distanceFunctions.put(Integer.valueOf(0), new DistanceFunction() {
-        	// real distance
+            // real distance
             public float execute(float x, float y, float z, float e) {
                 return (float) Math.sqrt(x * x + y * y + z * z);
             }
         });
         distanceFunctions.put(Integer.valueOf(1), new DistanceFunction() {
-        	// distance squared
+            // distance squared
             public float execute(float x, float y, float z, float e) {
                 return x * x + y * y + z * z;
             }
         });
         distanceFunctions.put(Integer.valueOf(2), new DistanceFunction() {
-        	// manhattan/taxicab/cityblock distance
+            // manhattan/taxicab/cityblock distance
             public float execute(float x, float y, float z, float e) {
                 return FastMath.abs(x) + FastMath.abs(y) + FastMath.abs(z);
             }
         });
         distanceFunctions.put(Integer.valueOf(3), new DistanceFunction() {
-        	// Chebychev
+            // Chebychev
             public float execute(float x, float y, float z, float e) {
                 x = FastMath.abs(x);
                 y = FastMath.abs(y);
@@ -280,14 +280,14 @@ import java.util.logging.Logger;
             }
         });
         distanceFunctions.put(Integer.valueOf(4), new DistanceFunction() {
-        	// Minkovsky, preset exponent 0.5 (MinkovskyH)
+            // Minkovsky, preset exponent 0.5 (MinkovskyH)
             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;
             }
         });
         distanceFunctions.put(Integer.valueOf(5), new DistanceFunction() {
-        	// Minkovsky, preset exponent 0.25 (Minkovsky4)
+            // Minkovsky, preset exponent 0.25 (Minkovsky4)
             public float execute(float x, float y, float z, float e) {
                 x *= x;
                 y *= y;
@@ -296,13 +296,13 @@ import java.util.logging.Logger;
             }
         });
         distanceFunctions.put(Integer.valueOf(6), new DistanceFunction() {
-        	// Minkovsky, general case
+            // Minkovsky, general case
             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, NoiseGenerator.MusgraveFunction>();
     static {
         musgraveFunctions.put(Integer.valueOf(TEX_MFRACTAL), new MusgraveFunction() {
@@ -460,64 +460,64 @@ import java.util.logging.Logger;
             }
         });
     }
-    
+
     public static class NoiseFunctions {
-    	public static float noise(float x, float y, float z, float noiseSize, int noiseDepth, int noiseBasis, boolean isHard) {
-    		NoiseFunction abstractNoiseFunc = noiseFunctions.get(Integer.valueOf(noiseBasis));
-    		if (abstractNoiseFunc == null) {
-	            abstractNoiseFunc = noiseFunctions.get(0);
-	            noiseBasis = 0;
-	        }
-    		
-    		if (noiseBasis == 0) {
-    			++x;
-	            ++y;
-	            ++z;
-	        }
-
-	        if (noiseSize != 0.0) {
-	            noiseSize = 1.0f / noiseSize;
-	            x *= noiseSize;
-	            y *= noiseSize;
-	            z *= noiseSize;
-	        }
-	        float result = abstractNoiseFunc.execute(x, y, z);
-	        return isHard ? Math.abs(2.0f * result - 1.0f) : result;
-    	}
-    	
-    	public static float turbulence(float x, float y, float z, float noiseSize, int noiseDepth, int noiseBasis, boolean isHard) {
-    		NoiseFunction abstractNoiseFunc = noiseFunctions.get(Integer.valueOf(noiseBasis));
-    		if (abstractNoiseFunc == null) {
-	            abstractNoiseFunc = noiseFunctions.get(0);
-	            noiseBasis = 0;
-	        }
-    		
-    		if (noiseBasis == 0) {
-	            ++x;
-	            ++y;
-	            ++z;
-	        }
-	        if (noiseSize != 0.0) {
-	            noiseSize = 1.0f / noiseSize;
-	            x *= noiseSize;
-	            y *= noiseSize;
-	            z *= noiseSize;
-	        }
-	        
-	        float sum = 0, t, amp = 1, fscale = 1;
-	        for (int i = 0; i <= noiseDepth; ++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 << noiseDepth) / (float) ((1 << noiseDepth + 1) - 1);
-	        return sum;
-    	}
-    	
-    	/**
+        public static float noise(float x, float y, float z, float noiseSize, int noiseDepth, int noiseBasis, boolean isHard) {
+            NoiseFunction abstractNoiseFunc = noiseFunctions.get(Integer.valueOf(noiseBasis));
+            if (abstractNoiseFunc == null) {
+                abstractNoiseFunc = noiseFunctions.get(0);
+                noiseBasis = 0;
+            }
+
+            if (noiseBasis == 0) {
+                ++x;
+                ++y;
+                ++z;
+            }
+
+            if (noiseSize != 0.0) {
+                noiseSize = 1.0f / noiseSize;
+                x *= noiseSize;
+                y *= noiseSize;
+                z *= noiseSize;
+            }
+            float result = abstractNoiseFunc.execute(x, y, z);
+            return isHard ? Math.abs(2.0f * result - 1.0f) : result;
+        }
+
+        public static float turbulence(float x, float y, float z, float noiseSize, int noiseDepth, int noiseBasis, boolean isHard) {
+            NoiseFunction abstractNoiseFunc = noiseFunctions.get(Integer.valueOf(noiseBasis));
+            if (abstractNoiseFunc == null) {
+                abstractNoiseFunc = noiseFunctions.get(0);
+                noiseBasis = 0;
+            }
+
+            if (noiseBasis == 0) {
+                ++x;
+                ++y;
+                ++z;
+            }
+            if (noiseSize != 0.0) {
+                noiseSize = 1.0f / noiseSize;
+                x *= noiseSize;
+                y *= noiseSize;
+                z *= noiseSize;
+            }
+
+            float sum = 0, t, amp = 1, fscale = 1;
+            for (int i = 0; i <= noiseDepth; ++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 << noiseDepth) / (float) ((1 << noiseDepth + 1) - 1);
+            return sum;
+        }
+
+        /**
          * Not 'pure' Worley, but the results are virtually the same. Returns distances in da and point coords in pa
          */
         public static void voronoi(float x, float y, float z, float[] da, float[] pa, float distanceExponent, int distanceType) {
@@ -531,7 +531,7 @@ import java.util.logging.Logger;
             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] = Float.MAX_VALUE;//1e10f;
+            da[0] = da[1] = da[2] = da[3] = Float.MAX_VALUE;// 1e10f;
             for (int i = xi - 1; i <= xi + 1; ++i) {
                 for (int j = yi - 1; j <= yi + 1; ++j) {
                     for (int k = zi - 1; k <= zi + 1; ++k) {
@@ -589,7 +589,7 @@ import java.util.logging.Logger;
                 }
             }
         }
-        
+
         // instead of adding another permutation array, just use hash table defined above
         public static float newPerlin(float x, float y, float z) {
             int A, AA, AB, B, BA, BB;
@@ -598,7 +598,7 @@ import java.util.logging.Logger;
             x -= floorX;
             y -= floorY;
             z -= floorZ;
-            //computing fading curves
+            // computing fading curves
             floorX = NoiseMath.npfade(x);
             floorY = NoiseMath.npfade(y);
             floorZ = NoiseMath.npfade(z);
@@ -608,14 +608,8 @@ import java.util.logging.Logger;
             B = hash[intX + 1] + intY;
             BA = hash[B] + intZ;
             BB = hash[B + 1] + intZ;
-            return  NoiseMath.lerp(floorZ, NoiseMath.lerp(floorY, NoiseMath.lerp(floorX, NoiseMath.grad(hash[AA], x, y, z),
-            		NoiseMath.grad(hash[BA], x - 1, y, z)),
-            		NoiseMath.lerp(floorX, NoiseMath.grad(hash[AB], x, y - 1, z),
-            		NoiseMath.grad(hash[BB], x - 1, y - 1, z))),
-            		NoiseMath.lerp(floorY, NoiseMath.lerp(floorX, NoiseMath.grad(hash[AA + 1], x, y, z - 1),
-            		NoiseMath.grad(hash[BA + 1], x - 1, y, z - 1)),
-            		NoiseMath.lerp(floorX, NoiseMath.grad(hash[AB + 1], x, y - 1, z - 1), 
-            		NoiseMath.grad(hash[BB + 1], x - 1, y - 1, z - 1))));
+            return NoiseMath.lerp(floorZ, NoiseMath.lerp(floorY, NoiseMath.lerp(floorX, NoiseMath.grad(hash[AA], x, y, z), NoiseMath.grad(hash[BA], x - 1, y, z)), NoiseMath.lerp(floorX, NoiseMath.grad(hash[AB], x, y - 1, z), NoiseMath.grad(hash[BB], x - 1, y - 1, z))),
+                    NoiseMath.lerp(floorY, NoiseMath.lerp(floorX, NoiseMath.grad(hash[AA + 1], x, y, z - 1), NoiseMath.grad(hash[BA + 1], x - 1, y, z - 1)), NoiseMath.lerp(floorX, NoiseMath.grad(hash[AB + 1], x, y - 1, z - 1), NoiseMath.grad(hash[BB + 1], x - 1, y - 1, z - 1))));
         }
 
         public static float noise3Perlin(float x, float y, float z) {
@@ -624,13 +618,13 @@ import java.util.logging.Logger;
             int bx1 = bx0 + 1 & 0xFF;
             float rx0 = t - (int) t;
             float rx1 = rx0 - 1.0f;
-            
+
             t = y + 10000.0f;
             int by0 = (int) t & 0xFF;
             int by1 = by0 + 1 & 0xFF;
             float ry0 = t - (int) t;
             float ry1 = ry0 - 1.0f;
-            
+
             t = z + 10000.0f;
             int bz0 = (int) t & 0xFF;
             int bz1 = bz0 + 1 & 0xFF;
@@ -685,7 +679,7 @@ import java.util.logging.Logger;
             int ix = (int) Math.floor(x);
             int iy = (int) Math.floor(y);
             int iz = (int) Math.floor(z);
-            
+
             float ox = x - ix;
             float oy = y - iy;
             float oz = z - iz;
@@ -707,24 +701,23 @@ import java.util.logging.Logger;
             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;
-            float[] cn = new float[] {cn1 * cn2 * cn3, cn1 * cn2 * cn6, cn1 * cn5 * cn3, cn1 * cn5 * cn6,
-					  cn4 * cn2 * cn3, cn4 * cn2 * cn6, cn4 * cn5 * cn3, cn4 * cn5 * cn6,};
-            
+            float[] cn = new float[] { cn1 * cn2 * cn3, cn1 * cn2 * cn6, cn1 * cn5 * cn3, cn1 * cn5 * cn6, cn4 * cn2 * cn3, cn4 * cn2 * cn6, cn4 * cn5 * cn3, cn4 * cn5 * cn6, };
+
             int b00 = hash[hash[ix & 0xFF] + (iy & 0xFF)];
             int b01 = hash[hash[ix & 0xFF] + (iy + 1 & 0xFF)];
             int b10 = hash[hash[ix + 1 & 0xFF] + (iy & 0xFF)];
             int b11 = hash[hash[ix + 1 & 0xFF] + (iy + 1 & 0xFF)];
-            int[] b1 = new int[] {b00, b00, b01, b01, b10, b10, b11, b11};
-            
-            int[] b2 = new int[] {iz & 0xFF, iz + 1 & 0xFF};
-            
-            float[] xFactor = new float[] {ox, ox, ox, ox, jx, jx, jx, jx};
-            float[] yFactor = new float[] {oy, oy, jy, jy, oy, oy, jy, jy};
-            float[] zFactor = new float[] {oz, jz, oz, jz, oz, jz, oz, jz};
-            
-            for(int i=0;i<8;++i) {
-            	int hIndex = 3 * hash[b1[i] + b2[i%2]];
-            	n += cn[i] * (hashvectf[hIndex] * xFactor[i] + hashvectf[hIndex + 1] * yFactor[i] + hashvectf[hIndex + 2] * zFactor[i]);
+            int[] b1 = new int[] { b00, b00, b01, b01, b10, b10, b11, b11 };
+
+            int[] b2 = new int[] { iz & 0xFF, iz + 1 & 0xFF };
+
+            float[] xFactor = new float[] { ox, ox, ox, ox, jx, jx, jx, jx };
+            float[] yFactor = new float[] { oy, oy, jy, jy, oy, oy, jy, jy };
+            float[] zFactor = new float[] { oz, jz, oz, jz, oz, jz, oz, jz };
+
+            for (int i = 0; i < 8; ++i) {
+                int hIndex = 3 * hash[b1[i] + b2[i % 2]];
+                n += cn[i] * (hashvectf[hIndex] * xFactor[i] + hashvectf[hIndex + 1] * yFactor[i] + hashvectf[hIndex + 2] * zFactor[i]);
             }
 
             if (n < 0.0f) {
@@ -746,11 +739,11 @@ import java.util.logging.Logger;
         /**
          * This method calculates the unsigned value of the noise.
          * @param x
-         *        the x texture coordinate
+         *            the x texture coordinate
          * @param y
-         *        the y texture coordinate
+         *            the y texture coordinate
          * @param z
-         *        the z texture coordinate
+         *            the z texture coordinate
          * @return value of the noise
          */
         float execute(float x, float y, float z);
@@ -758,49 +751,49 @@ import java.util.logging.Logger;
         /**
          * This method calculates the signed value of the noise.
          * @param x
-         *        the x texture coordinate
+         *            the x texture coordinate
          * @param y
-         *        the y texture coordinate
+         *            the y texture coordinate
          * @param z
-         *        the z texture coordinate
+         *            the z texture coordinate
          * @return value of the noise
          */
         float executeSigned(float x, float y, float z);
     }
-    
+
     public static class NoiseMath {
-    	public static float lerp(float t, float a, float b) {
+        public static float lerp(float t, float a, float b) {
             return a + t * (b - a);
         }
 
-    	public static float npfade(float t) {
+        public static float npfade(float t) {
             return t * t * t * (t * (t * 6.0f - 15.0f) + 10.0f);
         }
 
-    	public static float grad(int hash, float x, float y, float z) {
+        public static float grad(int hash, float x, float y, float z) {
             int h = hash & 0x0F;
             float u = h < 8 ? x : y, v = h < 4 ? y : h == 12 || h == 14 ? x : z;
             return ((h & 1) == 0 ? u : -u) + ((h & 2) == 0 ? v : -v);
         }
 
-    	public static float surve(float t) {
+        public static float surve(float t) {
             return t * t * (3.0f - 2.0f * t);
         }
 
-    	public static float at(float x, float y, float z, float[] q) {
+        public static float at(float x, float y, float z, float[] q) {
             return x * q[0] + y * q[1] + z * q[2];
         }
-    	
-    	public static void hash(int x, int y, int z, float[] result) {
+
+        public static void hash(int x, int y, int z, float[] result) {
             result[0] = hashpntf[3 * hash[hash[hash[z & 0xFF] + y & 0xFF] + x & 0xFF]];
             result[1] = hashpntf[3 * hash[hash[hash[z & 0xFF] + y & 0xFF] + x & 0xFF] + 1];
             result[2] = hashpntf[3 * hash[hash[hash[z & 0xFF] + y & 0xFF] + x & 0xFF] + 2];
         }
     }
-    
+
     @Override
     public boolean shouldBeLoaded(Structure structure, BlenderContext blenderContext) {
-    	return true;
+        return true;
     }
 
     /**
@@ -812,13 +805,13 @@ import java.util.logging.Logger;
         /**
          * This method calculates the distance for voronoi algorithms.
          * @param x
-         *        the x coordinate
+         *            the x coordinate
          * @param y
-         *        the y coordinate
+         *            the y coordinate
          * @param z
-         *        the z coordinate
+         *            the z coordinate
          * @param e
-         *        this parameter used in Monkovsky (no idea what it really is ;)
+         *            this parameter used in Monkovsky (no idea what it really is ;)
          * @return
          */
         float execute(float x, float y, float z, float e);

+ 59 - 57
engine/src/blender/com/jme3/scene/plugins/blender/textures/generating/TextureGenerator.java

@@ -42,43 +42,44 @@ import com.jme3.texture.Image.Format;
  * @author Marcin Roguski (Kaelthas)
  */
 public abstract class TextureGenerator {
-	protected NoiseGenerator	noiseGenerator;
-	protected int flag;
-	protected float[][] colorBand;
-	protected BrightnessAndContrastData bacd;
-	protected Format imageFormat;
-	
-	public TextureGenerator(NoiseGenerator noiseGenerator, Format imageFormat) {
-		this.noiseGenerator = noiseGenerator;
-		this.imageFormat = imageFormat;
-	}
+    protected NoiseGenerator            noiseGenerator;
+    protected int                       flag;
+    protected float[][]                 colorBand;
+    protected BrightnessAndContrastData bacd;
+    protected Format                    imageFormat;
 
-	public Format getImageFormat() {
-		return imageFormat;
-	}
-	
-	public void readData(Structure tex, BlenderContext blenderContext) {
-		flag = ((Number) tex.getFieldValue("flag")).intValue();
-		colorBand = new ColorBand(tex, blenderContext).computeValues();
-		bacd = new BrightnessAndContrastData(tex);
-		if(colorBand != null) {
-			imageFormat = Format.RGBA8;
-		}
-	}
-	
-	public abstract void getPixel(TexturePixel pixel, float x, float y, float z);
-	
-	/**
-	 * This method applies brightness and contrast for RGB textures.
-	 * @param tex texture structure
-	 * @param texres
-	 */
-	protected void applyBrightnessAndContrast(BrightnessAndContrastData bacd, TexturePixel texres) {
+    public TextureGenerator(NoiseGenerator noiseGenerator, Format imageFormat) {
+        this.noiseGenerator = noiseGenerator;
+        this.imageFormat = imageFormat;
+    }
+
+    public Format getImageFormat() {
+        return imageFormat;
+    }
+
+    public void readData(Structure tex, BlenderContext blenderContext) {
+        flag = ((Number) tex.getFieldValue("flag")).intValue();
+        colorBand = new ColorBand(tex, blenderContext).computeValues();
+        bacd = new BrightnessAndContrastData(tex);
+        if (colorBand != null) {
+            imageFormat = Format.RGBA8;
+        }
+    }
+
+    public abstract void getPixel(TexturePixel pixel, float x, float y, float z);
+
+    /**
+     * This method applies brightness and contrast for RGB textures.
+     * @param tex
+     *            texture structure
+     * @param texres
+     */
+    protected void applyBrightnessAndContrast(BrightnessAndContrastData bacd, TexturePixel texres) {
         texres.red = (texres.red - 0.5f) * bacd.contrast + bacd.brightness;
         if (texres.red < 0.0f) {
             texres.red = 0.0f;
         }
-        texres.green =(texres.green - 0.5f) * bacd.contrast + bacd.brightness;
+        texres.green = (texres.green - 0.5f) * bacd.contrast + bacd.brightness;
         if (texres.green < 0.0f) {
             texres.green = 0.0f;
         }
@@ -87,14 +88,14 @@ public abstract class TextureGenerator {
             texres.blue = 0.0f;
         }
     }
-	
-	/**
-	 * This method applies brightness and contrast for Luminance textures.
-	 * @param texres
-	 * @param contrast
-	 * @param brightness
-	 */
-	protected void applyBrightnessAndContrast(TexturePixel texres, float contrast, float brightness) {
+
+    /**
+     * This method applies brightness and contrast for Luminance textures.
+     * @param texres
+     * @param contrast
+     * @param brightness
+     */
+    protected void applyBrightnessAndContrast(TexturePixel texres, float contrast, float brightness) {
         texres.intensity = (texres.intensity - 0.5f) * contrast + brightness;
         if (texres.intensity < 0.0f) {
             texres.intensity = 0.0f;
@@ -102,28 +103,29 @@ public abstract class TextureGenerator {
             texres.intensity = 1.0f;
         }
     }
-	
-	/**
-	 * This class contains brightness and contrast data.
-	 * @author Marcin Roguski (Kaelthas)
-	 */
-	protected static class BrightnessAndContrastData {
-		public final float contrast;
+
+    /**
+     * This class contains brightness and contrast data.
+     * @author Marcin Roguski (Kaelthas)
+     */
+    protected static class BrightnessAndContrastData {
+        public final float contrast;
         public final float brightness;
         public final float rFactor;
         public final float gFactor;
         public final float bFactor;
-        
+
         /**
          * Constructor reads the required data from the given structure.
-         * @param tex texture structure
+         * @param tex
+         *            texture structure
          */
-		public BrightnessAndContrastData(Structure tex) {
-			contrast = ((Number) tex.getFieldValue("contrast")).floatValue();
-	        brightness = ((Number) tex.getFieldValue("bright")).floatValue() - 0.5f;
-	        rFactor = ((Number) tex.getFieldValue("rfac")).floatValue();
-	        gFactor = ((Number) tex.getFieldValue("gfac")).floatValue();
-	        bFactor = ((Number) tex.getFieldValue("bfac")).floatValue();
-		}
-	}
+        public BrightnessAndContrastData(Structure tex) {
+            contrast = ((Number) tex.getFieldValue("contrast")).floatValue();
+            brightness = ((Number) tex.getFieldValue("bright")).floatValue() - 0.5f;
+            rFactor = ((Number) tex.getFieldValue("rfac")).floatValue();
+            gFactor = ((Number) tex.getFieldValue("gfac")).floatValue();
+            bFactor = ((Number) tex.getFieldValue("bfac")).floatValue();
+        }
+    }
 }

+ 82 - 82
engine/src/blender/com/jme3/scene/plugins/blender/textures/generating/TextureGeneratorBlend.java

@@ -42,90 +42,90 @@ import com.jme3.texture.Image.Format;
  * @author Marcin Roguski (Kaelthas)
  */
 public final class TextureGeneratorBlend extends TextureGenerator {
-    
+
     private static final IntensityFunction INTENSITY_FUNCTION[] = new IntensityFunction[7];
     static {
-    	INTENSITY_FUNCTION[0] = new IntensityFunction() {//Linear: stype = 0 (TEX_LIN)
-			public float getIntensity(float x, float y, float z) {
-				return (1.0f + x) * 0.5f;
-			}
-		};
-		INTENSITY_FUNCTION[1] = new IntensityFunction() {//Quad: stype = 1 (TEX_QUAD)
-			public float getIntensity(float x, float y, float z) {
-				float result = (1.0f + x) * 0.5f;
-				return result * result;
-			}
-		};
-		INTENSITY_FUNCTION[2] = new IntensityFunction() {//Ease: stype = 2 (TEX_EASE)
-			public float getIntensity(float x, float y, float z) {
-				float result = (1.0f + x) * 0.5f;
-				if (result <= 0.0f) {
-					return 0.0f;
-				} else if (result >= 1.0f) {
-					return 1.0f;
-				} else {
-					return result * result *(3.0f - 2.0f * result);
-				}
-			}
-		};
-		INTENSITY_FUNCTION[3] = new IntensityFunction() {//Diagonal: stype = 3 (TEX_DIAG)
-			public float getIntensity(float x, float y, float z) {
-				return (2.0f + x + y) * 0.25f;
-			}
-		};
-		INTENSITY_FUNCTION[4] = new IntensityFunction() {//Sphere: stype = 4 (TEX_SPHERE)
-			public float getIntensity(float x, float y, float z) {
-				float result = 1.0f - (float) Math.sqrt(x * x + y * y + z * z);
-				return result < 0.0f ? 0.0f : result;
-			}
-		};
-		INTENSITY_FUNCTION[5] = new IntensityFunction() {//Halo: stype = 5 (TEX_HALO)
-			public float getIntensity(float x, float y, float z) {
-				float result = 1.0f - (float) Math.sqrt(x * x + y * y + z * z);
-				return result <= 0.0f ? 0.0f : result * result;
-			}
-		};
-		INTENSITY_FUNCTION[6] = new IntensityFunction() {//Radial: stype = 6 (TEX_RAD)
-			public float getIntensity(float x, float y, float z) {
-				return (float) Math.atan2(y, x) * FastMath.INV_TWO_PI + 0.5f;
-			}
-		};
+        INTENSITY_FUNCTION[0] = new IntensityFunction() {// Linear: stype = 0 (TEX_LIN)
+            public float getIntensity(float x, float y, float z) {
+                return (1.0f + x) * 0.5f;
+            }
+        };
+        INTENSITY_FUNCTION[1] = new IntensityFunction() {// Quad: stype = 1 (TEX_QUAD)
+            public float getIntensity(float x, float y, float z) {
+                float result = (1.0f + x) * 0.5f;
+                return result * result;
+            }
+        };
+        INTENSITY_FUNCTION[2] = new IntensityFunction() {// Ease: stype = 2 (TEX_EASE)
+            public float getIntensity(float x, float y, float z) {
+                float result = (1.0f + x) * 0.5f;
+                if (result <= 0.0f) {
+                    return 0.0f;
+                } else if (result >= 1.0f) {
+                    return 1.0f;
+                } else {
+                    return result * result * (3.0f - 2.0f * result);
+                }
+            }
+        };
+        INTENSITY_FUNCTION[3] = new IntensityFunction() {// Diagonal: stype = 3 (TEX_DIAG)
+            public float getIntensity(float x, float y, float z) {
+                return (2.0f + x + y) * 0.25f;
+            }
+        };
+        INTENSITY_FUNCTION[4] = new IntensityFunction() {// Sphere: stype = 4 (TEX_SPHERE)
+            public float getIntensity(float x, float y, float z) {
+                float result = 1.0f - (float) Math.sqrt(x * x + y * y + z * z);
+                return result < 0.0f ? 0.0f : result;
+            }
+        };
+        INTENSITY_FUNCTION[5] = new IntensityFunction() {// Halo: stype = 5 (TEX_HALO)
+            public float getIntensity(float x, float y, float z) {
+                float result = 1.0f - (float) Math.sqrt(x * x + y * y + z * z);
+                return result <= 0.0f ? 0.0f : result * result;
+            }
+        };
+        INTENSITY_FUNCTION[6] = new IntensityFunction() {// Radial: stype = 6 (TEX_RAD)
+            public float getIntensity(float x, float y, float z) {
+                return (float) Math.atan2(y, x) * FastMath.INV_TWO_PI + 0.5f;
+            }
+        };
+    }
+
+    /**
+     * Constructor stores the given noise generator.
+     * @param noiseGenerator
+     *            the noise generator
+     */
+    public TextureGeneratorBlend(NoiseGenerator noiseGenerator) {
+        super(noiseGenerator, Format.Luminance8);
+    }
+
+    protected int stype;
+
+    @Override
+    public void readData(Structure tex, BlenderContext blenderContext) {
+        super.readData(tex, blenderContext);
+        stype = ((Number) tex.getFieldValue("stype")).intValue();
     }
-    
-	/**
-	 * Constructor stores the given noise generator.
-	 * @param noiseGenerator
-	 *        the noise generator
-	 */
-	public TextureGeneratorBlend(NoiseGenerator noiseGenerator) {
-		super(noiseGenerator, Format.Luminance8);
-	}
-	
-	protected int stype;
-	
-	@Override
-	public void readData(Structure tex, BlenderContext blenderContext) {
-		super.readData(tex, blenderContext);
-		stype = ((Number) tex.getFieldValue("stype")).intValue();
-	}
-	
-	@Override
-	public void getPixel(TexturePixel pixel, float x, float y, float z) {
-		pixel.intensity = INTENSITY_FUNCTION[stype].getIntensity(x, y, z);
-		
-		if (colorBand != null) {
-			int colorbandIndex = (int) (pixel.intensity * 1000.0f);
-			pixel.red = colorBand[colorbandIndex][0];
-			pixel.green = colorBand[colorbandIndex][1];
-			pixel.blue = colorBand[colorbandIndex][2];
-			
-			this.applyBrightnessAndContrast(bacd, pixel);
-		} else {
-			this.applyBrightnessAndContrast(pixel, bacd.contrast, bacd.brightness);
-		}
-	}
 
-	private static interface IntensityFunction {
-		float getIntensity(float x, float y, float z);
-	}
+    @Override
+    public void getPixel(TexturePixel pixel, float x, float y, float z) {
+        pixel.intensity = INTENSITY_FUNCTION[stype].getIntensity(x, y, z);
+
+        if (colorBand != null) {
+            int colorbandIndex = (int) (pixel.intensity * 1000.0f);
+            pixel.red = colorBand[colorbandIndex][0];
+            pixel.green = colorBand[colorbandIndex][1];
+            pixel.blue = colorBand[colorbandIndex][2];
+
+            this.applyBrightnessAndContrast(bacd, pixel);
+        } else {
+            this.applyBrightnessAndContrast(pixel, bacd.contrast, bacd.brightness);
+        }
+    }
+
+    private static interface IntensityFunction {
+        float getIntensity(float x, float y, float z);
+    }
 }

+ 61 - 61
engine/src/blender/com/jme3/scene/plugins/blender/textures/generating/TextureGeneratorClouds.java

@@ -42,68 +42,68 @@ import com.jme3.texture.Image.Format;
  * @author Marcin Roguski (Kaelthas)
  */
 public class TextureGeneratorClouds extends TextureGenerator {
-	// noiseType
+    // noiseType
     protected static final int TEX_NOISESOFT = 0;
     protected static final int TEX_NOISEPERL = 1;
-    
+
     // sType
-    protected static final int TEX_DEFAULT = 0;
-    protected static final int TEX_COLOR = 1;
-    
-    protected float noisesize;
-    protected int noiseDepth;
-    protected int noiseBasis;
-    protected int noiseType;
-    protected boolean isHard;
-    protected int sType;
-    
-	/**
-	 * Constructor stores the given noise generator.
-	 * @param noiseGenerator
-	 *        the noise generator
-	 */
-	public TextureGeneratorClouds(NoiseGenerator noiseGenerator) {
-		super(noiseGenerator, Format.Luminance8);
-	}
-	
-	@Override
-	public void readData(Structure tex, BlenderContext blenderContext) {
-		super.readData(tex, blenderContext);
-		noisesize = ((Number) tex.getFieldValue("noisesize")).floatValue();
-		noiseDepth = ((Number) tex.getFieldValue("noisedepth")).intValue();
-		noiseBasis = ((Number) tex.getFieldValue("noisebasis")).intValue();
-		noiseType = ((Number) tex.getFieldValue("noisetype")).intValue();
-		isHard = noiseType != TEX_NOISESOFT;
-		sType = ((Number) tex.getFieldValue("stype")).intValue();
-		if(sType == TEX_COLOR) {
-			this.imageFormat = Format.RGBA8;
-		}
-	}
-	
-	@Override
-	public void getPixel(TexturePixel pixel, float x, float y, float z) {
-		pixel.intensity = NoiseGenerator.NoiseFunctions.turbulence(x, y, z, noisesize, noiseDepth, noiseBasis, isHard);
-		pixel.intensity = FastMath.clamp(pixel.intensity, 0.0f, 1.0f);
-		if (colorBand != null) {
-			int colorbandIndex = (int) (pixel.intensity * 1000.0f);
-			pixel.red = colorBand[colorbandIndex][0];
-			pixel.green = colorBand[colorbandIndex][1];
-			pixel.blue = colorBand[colorbandIndex][2];
-			pixel.alpha = colorBand[colorbandIndex][3];
-			
-			this.applyBrightnessAndContrast(bacd, pixel);
-		} else if (sType == TEX_COLOR) {
-			pixel.red = pixel.intensity;
-			pixel.green = NoiseGenerator.NoiseFunctions.turbulence(y, x, z, noisesize, noiseDepth, noiseBasis, isHard);
-			pixel.blue = NoiseGenerator.NoiseFunctions.turbulence(y, z, x, noisesize, noiseDepth, noiseBasis, isHard);
-			
-			pixel.green = FastMath.clamp(pixel.green, 0.0f, 1.0f);
-			pixel.blue = FastMath.clamp(pixel.blue, 0.0f, 1.0f);
-			pixel.alpha = 1.0f;
-			
-			this.applyBrightnessAndContrast(bacd, pixel);
-		} else {
-			this.applyBrightnessAndContrast(pixel, bacd.contrast, bacd.brightness);
-		}
-	}
+    protected static final int TEX_DEFAULT   = 0;
+    protected static final int TEX_COLOR     = 1;
+
+    protected float            noisesize;
+    protected int              noiseDepth;
+    protected int              noiseBasis;
+    protected int              noiseType;
+    protected boolean          isHard;
+    protected int              sType;
+
+    /**
+     * Constructor stores the given noise generator.
+     * @param noiseGenerator
+     *            the noise generator
+     */
+    public TextureGeneratorClouds(NoiseGenerator noiseGenerator) {
+        super(noiseGenerator, Format.Luminance8);
+    }
+
+    @Override
+    public void readData(Structure tex, BlenderContext blenderContext) {
+        super.readData(tex, blenderContext);
+        noisesize = ((Number) tex.getFieldValue("noisesize")).floatValue();
+        noiseDepth = ((Number) tex.getFieldValue("noisedepth")).intValue();
+        noiseBasis = ((Number) tex.getFieldValue("noisebasis")).intValue();
+        noiseType = ((Number) tex.getFieldValue("noisetype")).intValue();
+        isHard = noiseType != TEX_NOISESOFT;
+        sType = ((Number) tex.getFieldValue("stype")).intValue();
+        if (sType == TEX_COLOR) {
+            this.imageFormat = Format.RGBA8;
+        }
+    }
+
+    @Override
+    public void getPixel(TexturePixel pixel, float x, float y, float z) {
+        pixel.intensity = NoiseGenerator.NoiseFunctions.turbulence(x, y, z, noisesize, noiseDepth, noiseBasis, isHard);
+        pixel.intensity = FastMath.clamp(pixel.intensity, 0.0f, 1.0f);
+        if (colorBand != null) {
+            int colorbandIndex = (int) (pixel.intensity * 1000.0f);
+            pixel.red = colorBand[colorbandIndex][0];
+            pixel.green = colorBand[colorbandIndex][1];
+            pixel.blue = colorBand[colorbandIndex][2];
+            pixel.alpha = colorBand[colorbandIndex][3];
+
+            this.applyBrightnessAndContrast(bacd, pixel);
+        } else if (sType == TEX_COLOR) {
+            pixel.red = pixel.intensity;
+            pixel.green = NoiseGenerator.NoiseFunctions.turbulence(y, x, z, noisesize, noiseDepth, noiseBasis, isHard);
+            pixel.blue = NoiseGenerator.NoiseFunctions.turbulence(y, z, x, noisesize, noiseDepth, noiseBasis, isHard);
+
+            pixel.green = FastMath.clamp(pixel.green, 0.0f, 1.0f);
+            pixel.blue = FastMath.clamp(pixel.blue, 0.0f, 1.0f);
+            pixel.alpha = 1.0f;
+
+            this.applyBrightnessAndContrast(bacd, pixel);
+        } else {
+            this.applyBrightnessAndContrast(pixel, bacd.contrast, bacd.brightness);
+        }
+    }
 }

+ 39 - 39
engine/src/blender/com/jme3/scene/plugins/blender/textures/generating/TextureGeneratorDistnoise.java

@@ -43,46 +43,46 @@ import com.jme3.texture.Image.Format;
  * @author Marcin Roguski (Kaelthas)
  */
 public class TextureGeneratorDistnoise extends TextureGenerator {
-	protected float noisesize;
-	protected float distAmount;
-	protected int noisebasis;
-	protected int noisebasis2;
-	
-	/**
-	 * Constructor stores the given noise generator.
-	 * @param noiseGenerator
-	 *        the noise generator
-	 */
-	public TextureGeneratorDistnoise(NoiseGenerator noiseGenerator) {
-		super(noiseGenerator, Format.Luminance8);
-	}
-	
-	@Override
-	public void readData(Structure tex, BlenderContext blenderContext) {
-		super.readData(tex, blenderContext);
-		noisesize = ((Number) tex.getFieldValue("noisesize")).floatValue();
-		distAmount = ((Number) tex.getFieldValue("dist_amount")).floatValue();
-		noisebasis = ((Number) tex.getFieldValue("noisebasis")).intValue();
-		noisebasis2 = ((Number) tex.getFieldValue("noisebasis2")).intValue();
-	}
-	
-	@Override
-	public void getPixel(TexturePixel pixel, float x, float y, float z) {
-		pixel.intensity = this.musgraveVariableLunacrityNoise(x * 4, y * 4, z * 4, distAmount, noisebasis, noisebasis2);
-		pixel.intensity = FastMath.clamp(pixel.intensity, 0.0f, 1.0f);
-		if (colorBand != null) {
-			int colorbandIndex = (int) (pixel.intensity * 1000.0f);
-			pixel.red = colorBand[colorbandIndex][0];
-			pixel.green = colorBand[colorbandIndex][1];
-			pixel.blue = colorBand[colorbandIndex][2];
+    protected float noisesize;
+    protected float distAmount;
+    protected int   noisebasis;
+    protected int   noisebasis2;
 
-			this.applyBrightnessAndContrast(bacd, pixel);
-		} else {
-			this.applyBrightnessAndContrast(pixel, bacd.contrast, bacd.brightness);
-		}
-	}
+    /**
+     * Constructor stores the given noise generator.
+     * @param noiseGenerator
+     *            the noise generator
+     */
+    public TextureGeneratorDistnoise(NoiseGenerator noiseGenerator) {
+        super(noiseGenerator, Format.Luminance8);
+    }
+
+    @Override
+    public void readData(Structure tex, BlenderContext blenderContext) {
+        super.readData(tex, blenderContext);
+        noisesize = ((Number) tex.getFieldValue("noisesize")).floatValue();
+        distAmount = ((Number) tex.getFieldValue("dist_amount")).floatValue();
+        noisebasis = ((Number) tex.getFieldValue("noisebasis")).intValue();
+        noisebasis2 = ((Number) tex.getFieldValue("noisebasis2")).intValue();
+    }
+
+    @Override
+    public void getPixel(TexturePixel pixel, float x, float y, float z) {
+        pixel.intensity = this.musgraveVariableLunacrityNoise(x * 4, y * 4, z * 4, distAmount, noisebasis, noisebasis2);
+        pixel.intensity = FastMath.clamp(pixel.intensity, 0.0f, 1.0f);
+        if (colorBand != null) {
+            int colorbandIndex = (int) (pixel.intensity * 1000.0f);
+            pixel.red = colorBand[colorbandIndex][0];
+            pixel.green = colorBand[colorbandIndex][1];
+            pixel.blue = colorBand[colorbandIndex][2];
+
+            this.applyBrightnessAndContrast(bacd, pixel);
+        } else {
+            this.applyBrightnessAndContrast(pixel, bacd.contrast, bacd.brightness);
+        }
+    }
 
-	/**
+    /**
      * "Variable Lacunarity Noise" A distorted variety of Perlin noise. This method is used to calculate distorted noise
      * texture.
      * @param x
@@ -106,6 +106,6 @@ public class TextureGeneratorDistnoise extends TextureGenerator {
         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.executeSigned(x + rx, y + ry, z + rz); //distorted-domain noise
+        return abstractNoiseFunc2.executeSigned(x + rx, y + ry, z + rz); // distorted-domain noise
     }
 }

+ 33 - 33
engine/src/blender/com/jme3/scene/plugins/blender/textures/generating/TextureGeneratorFactory.java

@@ -3,37 +3,37 @@ package com.jme3.scene.plugins.blender.textures.generating;
 import com.jme3.scene.plugins.blender.textures.TextureHelper;
 
 public class TextureGeneratorFactory {
-	
-	private NoiseGenerator noiseGenerator;
-	
-	public TextureGeneratorFactory(String blenderVersion) {
-		noiseGenerator = new NoiseGenerator(blenderVersion);
-	}
-	
-	public TextureGenerator createTextureGenerator(int generatedTexture) {
-		switch(generatedTexture) {
-			case TextureHelper.TEX_BLEND:
-				return new TextureGeneratorBlend(noiseGenerator);
-			case TextureHelper.TEX_CLOUDS:
-				return new TextureGeneratorClouds(noiseGenerator);
-			case TextureHelper.TEX_DISTNOISE:
-				return new TextureGeneratorDistnoise(noiseGenerator);
-			case TextureHelper.TEX_MAGIC:
-				return new TextureGeneratorMagic(noiseGenerator);
-			case TextureHelper.TEX_MARBLE:
-				return new TextureGeneratorMarble(noiseGenerator);
-			case TextureHelper.TEX_MUSGRAVE:
-				return new TextureGeneratorMusgrave(noiseGenerator);
-			case TextureHelper.TEX_NOISE:
-				return new TextureGeneratorNoise(noiseGenerator);
-			case TextureHelper.TEX_STUCCI:
-				return new TextureGeneratorStucci(noiseGenerator);
-			case TextureHelper.TEX_VORONOI:
-				return new TextureGeneratorVoronoi(noiseGenerator);
-			case TextureHelper.TEX_WOOD:
-				return new TextureGeneratorWood(noiseGenerator);
-			default:
-				throw new IllegalStateException("Unknown generated texture type: " + generatedTexture);
-		}
-	}
+
+    private NoiseGenerator noiseGenerator;
+
+    public TextureGeneratorFactory(String blenderVersion) {
+        noiseGenerator = new NoiseGenerator(blenderVersion);
+    }
+
+    public TextureGenerator createTextureGenerator(int generatedTexture) {
+        switch (generatedTexture) {
+            case TextureHelper.TEX_BLEND:
+                return new TextureGeneratorBlend(noiseGenerator);
+            case TextureHelper.TEX_CLOUDS:
+                return new TextureGeneratorClouds(noiseGenerator);
+            case TextureHelper.TEX_DISTNOISE:
+                return new TextureGeneratorDistnoise(noiseGenerator);
+            case TextureHelper.TEX_MAGIC:
+                return new TextureGeneratorMagic(noiseGenerator);
+            case TextureHelper.TEX_MARBLE:
+                return new TextureGeneratorMarble(noiseGenerator);
+            case TextureHelper.TEX_MUSGRAVE:
+                return new TextureGeneratorMusgrave(noiseGenerator);
+            case TextureHelper.TEX_NOISE:
+                return new TextureGeneratorNoise(noiseGenerator);
+            case TextureHelper.TEX_STUCCI:
+                return new TextureGeneratorStucci(noiseGenerator);
+            case TextureHelper.TEX_VORONOI:
+                return new TextureGeneratorVoronoi(noiseGenerator);
+            case TextureHelper.TEX_WOOD:
+                return new TextureGeneratorWood(noiseGenerator);
+            default:
+                throw new IllegalStateException("Unknown generated texture type: " + generatedTexture);
+        }
+    }
 }

+ 113 - 113
engine/src/blender/com/jme3/scene/plugins/blender/textures/generating/TextureGeneratorMagic.java

@@ -42,119 +42,119 @@ import com.jme3.texture.Image.Format;
  * @author Marcin Roguski (Kaelthas)
  */
 public class TextureGeneratorMagic extends TextureGenerator {
-	private static NoiseDepthFunction[] noiseDepthFunctions = new NoiseDepthFunction[10];
-	static {
-		noiseDepthFunctions[0] = new NoiseDepthFunction() {
-			public void compute(float[] xyz, float turbulence) {
-				xyz[1] = -(float) Math.cos(xyz[0] - xyz[1] + xyz[2]) * turbulence;
-			}
-		};
-		noiseDepthFunctions[1] = new NoiseDepthFunction() {
-			public void compute(float[] xyz, float turbulence) {
-				xyz[0] = (float) Math.cos(xyz[0] - xyz[1] - xyz[2]) * turbulence;
-			}
-		};
-		noiseDepthFunctions[2] = new NoiseDepthFunction() {
-			public void compute(float[] xyz, float turbulence) {
-				xyz[2] = (float) Math.sin(-xyz[0] - xyz[1] - xyz[2]) * turbulence;
-			}
-		};
-		noiseDepthFunctions[3] = new NoiseDepthFunction() {
-			public void compute(float[] xyz, float turbulence) {
-				xyz[0] = -(float) Math.cos(-xyz[0] + xyz[1] - xyz[2]) * turbulence;
-			}
-		};
-		noiseDepthFunctions[4] = new NoiseDepthFunction() {
-			public void compute(float[] xyz, float turbulence) {
-				xyz[1] = -(float) Math.sin(-xyz[0] + xyz[1] + xyz[2]) * turbulence;
-			}
-		};
-		noiseDepthFunctions[5] = new NoiseDepthFunction() {
-			public void compute(float[] xyz, float turbulence) {
-				xyz[1] = -(float) Math.cos(-xyz[0] + xyz[1] + xyz[2]) * turbulence;
-			}
-		};
-		noiseDepthFunctions[6] = new NoiseDepthFunction() {
-			public void compute(float[] xyz, float turbulence) {
-				xyz[0] = (float) Math.cos(xyz[0] + xyz[1] + xyz[2]) * turbulence;
-			}
-		};
-		noiseDepthFunctions[7] = new NoiseDepthFunction() {
-			public void compute(float[] xyz, float turbulence) {
-				xyz[2] = (float) Math.sin(xyz[0] + xyz[1] - xyz[2]) * turbulence;
-			}
-		};
-		noiseDepthFunctions[8] = new NoiseDepthFunction() {
-			public void compute(float[] xyz, float turbulence) {
-				xyz[0] = -(float) Math.cos(-xyz[0] - xyz[1] + xyz[2]) * turbulence;
-			}
-		};
-		noiseDepthFunctions[9] = new NoiseDepthFunction() {
-			public void compute(float[] xyz, float turbulence) {
-				xyz[1] = -(float) Math.sin(xyz[0] - xyz[1] + xyz[2]) * turbulence;
-			}
-		};
-	}
-	
-	protected int noisedepth;
-	protected float turbul;
-	protected float[] xyz = new float[3];
-	
-	/**
-	 * Constructor stores the given noise generator.
-	 * @param noiseGenerator
-	 *        the noise generator
-	 */
-	public TextureGeneratorMagic(NoiseGenerator noiseGenerator) {
-		super(noiseGenerator, Format.RGBA8);
-	}
-	
-	@Override
-	public void readData(Structure tex, BlenderContext blenderContext) {
-		super.readData(tex, blenderContext);
-		noisedepth = ((Number) tex.getFieldValue("noisedepth")).intValue();
-		turbul = ((Number) tex.getFieldValue("turbul")).floatValue() / 5.0f;
-	}
-	
-	@Override
-	public void getPixel(TexturePixel pixel, float x, float y, float z) {
-		float turb = turbul;
-		xyz[0] = (float) Math.sin((x + y + z) * 5.0f);
-		xyz[1] = (float) Math.cos((-x + y - z) * 5.0f);
-		xyz[2] = -(float) Math.cos((-x - y + z) * 5.0f);
+    private static NoiseDepthFunction[] noiseDepthFunctions = new NoiseDepthFunction[10];
+    static {
+        noiseDepthFunctions[0] = new NoiseDepthFunction() {
+            public void compute(float[] xyz, float turbulence) {
+                xyz[1] = -(float) Math.cos(xyz[0] - xyz[1] + xyz[2]) * turbulence;
+            }
+        };
+        noiseDepthFunctions[1] = new NoiseDepthFunction() {
+            public void compute(float[] xyz, float turbulence) {
+                xyz[0] = (float) Math.cos(xyz[0] - xyz[1] - xyz[2]) * turbulence;
+            }
+        };
+        noiseDepthFunctions[2] = new NoiseDepthFunction() {
+            public void compute(float[] xyz, float turbulence) {
+                xyz[2] = (float) Math.sin(-xyz[0] - xyz[1] - xyz[2]) * turbulence;
+            }
+        };
+        noiseDepthFunctions[3] = new NoiseDepthFunction() {
+            public void compute(float[] xyz, float turbulence) {
+                xyz[0] = -(float) Math.cos(-xyz[0] + xyz[1] - xyz[2]) * turbulence;
+            }
+        };
+        noiseDepthFunctions[4] = new NoiseDepthFunction() {
+            public void compute(float[] xyz, float turbulence) {
+                xyz[1] = -(float) Math.sin(-xyz[0] + xyz[1] + xyz[2]) * turbulence;
+            }
+        };
+        noiseDepthFunctions[5] = new NoiseDepthFunction() {
+            public void compute(float[] xyz, float turbulence) {
+                xyz[1] = -(float) Math.cos(-xyz[0] + xyz[1] + xyz[2]) * turbulence;
+            }
+        };
+        noiseDepthFunctions[6] = new NoiseDepthFunction() {
+            public void compute(float[] xyz, float turbulence) {
+                xyz[0] = (float) Math.cos(xyz[0] + xyz[1] + xyz[2]) * turbulence;
+            }
+        };
+        noiseDepthFunctions[7] = new NoiseDepthFunction() {
+            public void compute(float[] xyz, float turbulence) {
+                xyz[2] = (float) Math.sin(xyz[0] + xyz[1] - xyz[2]) * turbulence;
+            }
+        };
+        noiseDepthFunctions[8] = new NoiseDepthFunction() {
+            public void compute(float[] xyz, float turbulence) {
+                xyz[0] = -(float) Math.cos(-xyz[0] - xyz[1] + xyz[2]) * turbulence;
+            }
+        };
+        noiseDepthFunctions[9] = new NoiseDepthFunction() {
+            public void compute(float[] xyz, float turbulence) {
+                xyz[1] = -(float) Math.sin(xyz[0] - xyz[1] + xyz[2]) * turbulence;
+            }
+        };
+    }
 
-		if (colorBand != null) {
-			pixel.intensity = FastMath.clamp(0.3333f * (xyz[0] + xyz[1] + xyz[2]), 0.0f, 1.0f);
-			int colorbandIndex = (int) (pixel.intensity * 1000.0f);
-			pixel.red = colorBand[colorbandIndex][0];
-			pixel.green = colorBand[colorbandIndex][1];
-			pixel.blue = colorBand[colorbandIndex][2];
-			pixel.alpha = colorBand[colorbandIndex][3];
-		} else {
-			if (noisedepth > 0) {
-				xyz[0] *= turb;
-				xyz[1] *= turb;
-				xyz[2] *= turb;
-				for (int m=0;m<noisedepth;++m) {
-					noiseDepthFunctions[m].compute(xyz, turb);
-				}
-			}
+    protected int                       noisedepth;
+    protected float                     turbul;
+    protected float[]                   xyz                 = new float[3];
 
-			if (turb != 0.0f) {
-				turb *= 2.0f;
-				xyz[0] /= turb;
-				xyz[1] /= turb;
-				xyz[2] /= turb;
-			}
-			pixel.red = 0.5f - xyz[0];
-			pixel.green = 0.5f - xyz[1];
-			pixel.blue = 0.5f - xyz[2];
-			pixel.alpha = 1.0f;
-		}
-		this.applyBrightnessAndContrast(bacd, pixel);
-	}
-	
-	private static interface NoiseDepthFunction {
-		void compute(float[] xyz, float turbulence);
-	}
+    /**
+     * Constructor stores the given noise generator.
+     * @param noiseGenerator
+     *            the noise generator
+     */
+    public TextureGeneratorMagic(NoiseGenerator noiseGenerator) {
+        super(noiseGenerator, Format.RGBA8);
+    }
+
+    @Override
+    public void readData(Structure tex, BlenderContext blenderContext) {
+        super.readData(tex, blenderContext);
+        noisedepth = ((Number) tex.getFieldValue("noisedepth")).intValue();
+        turbul = ((Number) tex.getFieldValue("turbul")).floatValue() / 5.0f;
+    }
+
+    @Override
+    public void getPixel(TexturePixel pixel, float x, float y, float z) {
+        float turb = turbul;
+        xyz[0] = (float) Math.sin((x + y + z) * 5.0f);
+        xyz[1] = (float) Math.cos((-x + y - z) * 5.0f);
+        xyz[2] = -(float) Math.cos((-x - y + z) * 5.0f);
+
+        if (colorBand != null) {
+            pixel.intensity = FastMath.clamp(0.3333f * (xyz[0] + xyz[1] + xyz[2]), 0.0f, 1.0f);
+            int colorbandIndex = (int) (pixel.intensity * 1000.0f);
+            pixel.red = colorBand[colorbandIndex][0];
+            pixel.green = colorBand[colorbandIndex][1];
+            pixel.blue = colorBand[colorbandIndex][2];
+            pixel.alpha = colorBand[colorbandIndex][3];
+        } else {
+            if (noisedepth > 0) {
+                xyz[0] *= turb;
+                xyz[1] *= turb;
+                xyz[2] *= turb;
+                for (int m = 0; m < noisedepth; ++m) {
+                    noiseDepthFunctions[m].compute(xyz, turb);
+                }
+            }
+
+            if (turb != 0.0f) {
+                turb *= 2.0f;
+                xyz[0] /= turb;
+                xyz[1] /= turb;
+                xyz[2] /= turb;
+            }
+            pixel.red = 0.5f - xyz[0];
+            pixel.green = 0.5f - xyz[1];
+            pixel.blue = 0.5f - xyz[2];
+            pixel.alpha = 1.0f;
+        }
+        this.applyBrightnessAndContrast(bacd, pixel);
+    }
+
+    private static interface NoiseDepthFunction {
+        void compute(float[] xyz, float turbulence);
+    }
 }

+ 49 - 49
engine/src/blender/com/jme3/scene/plugins/blender/textures/generating/TextureGeneratorMarble.java

@@ -40,50 +40,50 @@ import com.jme3.scene.plugins.blender.textures.TexturePixel;
  * @author Marcin Roguski (Kaelthas)
  */
 public class TextureGeneratorMarble extends TextureGeneratorWood {
-	// tex->stype
-    protected static final int TEX_SOFT = 0;
-    protected static final int TEX_SHARP = 1;
+    // tex->stype
+    protected static final int TEX_SOFT    = 0;
+    protected static final int TEX_SHARP   = 1;
     protected static final int TEX_SHARPER = 2;
-    
-    protected MarbleData marbleData;
 
-	/**
-	 * Constructor stores the given noise generator.
-	 * @param noiseGenerator
-	 *        the noise generator
-	 */
-	public TextureGeneratorMarble(NoiseGenerator noiseGenerator) {
-		super(noiseGenerator);
-	}
-	
-	@Override
-	public void readData(Structure tex, BlenderContext blenderContext) {
-		super.readData(tex, blenderContext);
-		marbleData = new MarbleData(tex);
-	}
-	
-	@Override
-	public void getPixel(TexturePixel pixel, float x, float y, float z) {
-		pixel.intensity = this.marbleInt(marbleData, x, y, z);
-		if (colorBand != null) {
-			int colorbandIndex = (int) (pixel.intensity * 1000.0f);
-			pixel.red = colorBand[colorbandIndex][0];
-			pixel.green = colorBand[colorbandIndex][1];
-			pixel.blue = colorBand[colorbandIndex][2];
-			
-			this.applyBrightnessAndContrast(bacd, pixel);
-			pixel.alpha = colorBand[colorbandIndex][3];
-		} else {
-			this.applyBrightnessAndContrast(pixel, bacd.contrast, bacd.brightness);
-		}
-	}
+    protected MarbleData       marbleData;
+
+    /**
+     * Constructor stores the given noise generator.
+     * @param noiseGenerator
+     *            the noise generator
+     */
+    public TextureGeneratorMarble(NoiseGenerator noiseGenerator) {
+        super(noiseGenerator);
+    }
+
+    @Override
+    public void readData(Structure tex, BlenderContext blenderContext) {
+        super.readData(tex, blenderContext);
+        marbleData = new MarbleData(tex);
+    }
+
+    @Override
+    public void getPixel(TexturePixel pixel, float x, float y, float z) {
+        pixel.intensity = this.marbleInt(marbleData, x, y, z);
+        if (colorBand != null) {
+            int colorbandIndex = (int) (pixel.intensity * 1000.0f);
+            pixel.red = colorBand[colorbandIndex][0];
+            pixel.green = colorBand[colorbandIndex][1];
+            pixel.blue = colorBand[colorbandIndex][2];
+
+            this.applyBrightnessAndContrast(bacd, pixel);
+            pixel.alpha = colorBand[colorbandIndex][3];
+        } else {
+            this.applyBrightnessAndContrast(pixel, bacd.contrast, bacd.brightness);
+        }
+    }
 
     public float marbleInt(MarbleData marbleData, float x, float y, float z) {
-    	int waveform;
+        int waveform;
         if (marbleData.waveform > TEX_TRI || marbleData.waveform < TEX_SIN) {
-        	waveform = 0;
+            waveform = 0;
         } else {
-        	waveform = marbleData.waveform;
+            waveform = marbleData.waveform;
         }
 
         float n = 5.0f * (x + y + z);
@@ -99,18 +99,18 @@ public class TextureGeneratorMarble extends TextureGeneratorWood {
         }
         return mi;
     }
-    
+
     private static class MarbleData {
-    	public final float noisesize;
-    	public final int noisebasis;
-    	public final int noisedepth;
-    	public final int stype;
-    	public final float turbul;
-    	public final int waveform;
-    	public final boolean isHard;
-        
+        public final float   noisesize;
+        public final int     noisebasis;
+        public final int     noisedepth;
+        public final int     stype;
+        public final float   turbul;
+        public final int     waveform;
+        public final boolean isHard;
+
         public MarbleData(Structure tex) {
-        	noisesize = ((Number) tex.getFieldValue("noisesize")).floatValue();
+            noisesize = ((Number) tex.getFieldValue("noisesize")).floatValue();
             noisebasis = ((Number) tex.getFieldValue("noisebasis")).intValue();
             noisedepth = ((Number) tex.getFieldValue("noisedepth")).intValue();
             stype = ((Number) tex.getFieldValue("stype")).intValue();
@@ -118,6 +118,6 @@ public class TextureGeneratorMarble extends TextureGeneratorWood {
             int noisetype = ((Number) tex.getFieldValue("noisetype")).intValue();
             waveform = ((Number) tex.getFieldValue("noisebasis2")).intValue();
             isHard = noisetype != TEX_NOISESOFT;
-		}
+        }
     }
 }

Bu fark içinde çok fazla dosya değişikliği olduğu için bazı dosyalar gösterilmiyor