瀏覽代碼

Textures loading refactoring.

New features:
- support for loading both 2D and 3D textures (they are merged and flattened to Texture2D)
- support for colorband usage in flat textures
- support for using color factors for flat images

Bugfixes:
- blend texture should be now calculated properly at the ends of the object
- blend texture is now properly directed
- flat texture projection (flat, bude and tube) should now be properly directed

Other stuff:
- better code separation and improved readability

git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@9329 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
Kae..pl 13 年之前
父節點
當前提交
8de8bf2d3e
共有 50 個文件被更改,包括 4115 次插入2711 次删除
  1. 0 7
      engine/src/blender/Common/MatDefs/Texture3D/tex3D.frag
  2. 0 16
      engine/src/blender/Common/MatDefs/Texture3D/tex3D.j3md
  3. 0 11
      engine/src/blender/Common/MatDefs/Texture3D/tex3D.vert
  4. 50 77
      engine/src/blender/com/jme3/asset/BlenderKey.java
  5. 17 18
      engine/src/blender/com/jme3/scene/plugins/blender/AbstractBlenderLoader.java
  6. 0 28
      engine/src/blender/com/jme3/scene/plugins/blender/BlenderContext.java
  7. 6 10
      engine/src/blender/com/jme3/scene/plugins/blender/BlenderLoader.java
  8. 15 7
      engine/src/blender/com/jme3/scene/plugins/blender/curves/CurvesHelper.java
  9. 159 275
      engine/src/blender/com/jme3/scene/plugins/blender/materials/MaterialContext.java
  10. 16 129
      engine/src/blender/com/jme3/scene/plugins/blender/materials/MaterialHelper.java
  11. 42 90
      engine/src/blender/com/jme3/scene/plugins/blender/meshes/MeshHelper.java
  12. 315 0
      engine/src/blender/com/jme3/scene/plugins/blender/textures/ColorBand.java
  13. 367 0
      engine/src/blender/com/jme3/scene/plugins/blender/textures/CombinedTexture.java
  14. 131 0
      engine/src/blender/com/jme3/scene/plugins/blender/textures/DDSTexelData.java
  15. 151 0
      engine/src/blender/com/jme3/scene/plugins/blender/textures/GeneratedTexture.java
  16. 0 416
      engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGenerator.java
  17. 0 131
      engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorClouds.java
  18. 0 106
      engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorNoise.java
  19. 0 125
      engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorStucci.java
  20. 0 171
      engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorVoronoi.java
  21. 327 218
      engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureHelper.java
  22. 70 78
      engine/src/blender/com/jme3/scene/plugins/blender/textures/TexturePixel.java
  23. 721 0
      engine/src/blender/com/jme3/scene/plugins/blender/textures/TriangulatedTexture.java
  24. 168 115
      engine/src/blender/com/jme3/scene/plugins/blender/textures/UVCoordinatesGenerator.java
  25. 76 55
      engine/src/blender/com/jme3/scene/plugins/blender/textures/UVProjectionGenerator.java
  26. 36 115
      engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/AbstractTextureBlender.java
  27. 16 14
      engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlender.java
  28. 149 99
      engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderAWT.java
  29. 20 19
      engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderDDS.java
  30. 26 7
      engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderFactory.java
  31. 45 19
      engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderLuminance.java
  32. 4 15
      engine/src/blender/com/jme3/scene/plugins/blender/textures/generating/NoiseGenerator.java
  33. 129 0
      engine/src/blender/com/jme3/scene/plugins/blender/textures/generating/TextureGenerator.java
  34. 24 54
      engine/src/blender/com/jme3/scene/plugins/blender/textures/generating/TextureGeneratorBlend.java
  35. 109 0
      engine/src/blender/com/jme3/scene/plugins/blender/textures/generating/TextureGeneratorClouds.java
  36. 31 55
      engine/src/blender/com/jme3/scene/plugins/blender/textures/generating/TextureGeneratorDistnoise.java
  37. 39 0
      engine/src/blender/com/jme3/scene/plugins/blender/textures/generating/TextureGeneratorFactory.java
  38. 46 67
      engine/src/blender/com/jme3/scene/plugins/blender/textures/generating/TextureGeneratorMagic.java
  39. 24 49
      engine/src/blender/com/jme3/scene/plugins/blender/textures/generating/TextureGeneratorMarble.java
  40. 38 61
      engine/src/blender/com/jme3/scene/plugins/blender/textures/generating/TextureGeneratorMusgrave.java
  41. 85 0
      engine/src/blender/com/jme3/scene/plugins/blender/textures/generating/TextureGeneratorNoise.java
  42. 101 0
      engine/src/blender/com/jme3/scene/plugins/blender/textures/generating/TextureGeneratorStucci.java
  43. 141 0
      engine/src/blender/com/jme3/scene/plugins/blender/textures/generating/TextureGeneratorVoronoi.java
  44. 24 54
      engine/src/blender/com/jme3/scene/plugins/blender/textures/generating/TextureGeneratorWood.java
  45. 0 0
      engine/src/blender/com/jme3/scene/plugins/blender/textures/generating/noiseconstants.dat
  46. 75 0
      engine/src/blender/com/jme3/scene/plugins/blender/textures/io/AWTPixelInputOutput.java
  47. 174 0
      engine/src/blender/com/jme3/scene/plugins/blender/textures/io/DDSPixelInputOutput.java
  48. 33 0
      engine/src/blender/com/jme3/scene/plugins/blender/textures/io/LuminancePixelInputOutput.java
  49. 50 0
      engine/src/blender/com/jme3/scene/plugins/blender/textures/io/PixelIOFactory.java
  50. 65 0
      engine/src/blender/com/jme3/scene/plugins/blender/textures/io/PixelInputOutput.java

+ 0 - 7
engine/src/blender/Common/MatDefs/Texture3D/tex3D.frag

@@ -1,7 +0,0 @@
-uniform sampler3D m_Texture;
-
-varying vec3 texCoord;
-
-void main(){
-    gl_FragColor= texture3D(m_Texture,texCoord);
-}

+ 0 - 16
engine/src/blender/Common/MatDefs/Texture3D/tex3D.j3md

@@ -1,16 +0,0 @@
-MaterialDef My MaterialDef {
-
-    MaterialParameters {
-        Texture3D Texture
-    }
-
-    Technique {
-        VertexShader GLSL100:   Common/MatDefs/Texture3D/tex3D.vert
-        FragmentShader GLSL100: Common/MatDefs/Texture3D/tex3D.frag
-
-        WorldParameters {
-            WorldViewProjectionMatrix
-        }
-    }
-
-}

+ 0 - 11
engine/src/blender/Common/MatDefs/Texture3D/tex3D.vert

@@ -1,11 +0,0 @@
-uniform mat4 g_WorldViewProjectionMatrix;
-
-attribute vec3 inTexCoord;
-attribute vec3 inPosition;
-
-varying vec3 texCoord;
-
-void main(){
-    gl_Position = g_WorldViewProjectionMatrix * vec4(inPosition,1.0);
-    texCoord=inTexCoord;
-}

+ 50 - 77
engine/src/blender/com/jme3/asset/BlenderKey.java

@@ -31,6 +31,13 @@
  */
 package com.jme3.asset;
 
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Queue;
+
+import org.lwjgl.opengl.GL11;
+
 import com.jme3.bounding.BoundingVolume;
 import com.jme3.collision.Collidable;
 import com.jme3.collision.CollisionResults;
@@ -49,10 +56,6 @@ import com.jme3.scene.SceneGraphVisitor;
 import com.jme3.scene.Spatial;
 import com.jme3.scene.plugins.ogre.AnimData;
 import com.jme3.texture.Texture;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Queue;
 
 /**
  * Blender key. Contains path of the blender file and its loading properties.
@@ -66,12 +69,6 @@ public class BlenderKey extends ModelKey {
 	 * between the frames.
 	 */
 	protected int								fps						= DEFAULT_FPS;
-	/** Width of generated textures (in pixels). */
-	protected int								generatedTextureWidth	= 60;
-	/** Height of generated textures (in pixels). */
-	protected int								generatedTextureHeight	= 60;
-	/** Depth of generated textures (in pixels). */
-	protected int								generatedTextureDepth	= 60;
 	/**
 	 * This variable is a bitwise flag of FeatureToLoad interface values; By default everything is being loaded.
 	 */
@@ -82,6 +79,8 @@ public class BlenderKey extends ModelKey {
 	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.
@@ -101,6 +100,8 @@ public class BlenderKey extends ModelKey {
 	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;
 	
 	/**
 	 * Constructor used by serialization mechanisms.
@@ -133,57 +134,6 @@ public class BlenderKey extends ModelKey {
 		this.fps = fps;
 	}
 
-	/**
-	 * This method sets the width of generated texture (in pixels). By default the value is 140 px.
-	 * @param generatedTextureWidth
-	 *        the width of generated texture
-	 */
-	public void setGeneratedTextureWidth(int generatedTextureWidth) {
-		this.generatedTextureWidth = generatedTextureWidth;
-	}
-
-	/**
-	 * This method returns the width of generated texture (in pixels). By default the value is 140 px.
-	 * @return the width of generated texture
-	 */
-	public int getGeneratedTextureWidth() {
-		return generatedTextureWidth;
-	}
-
-	/**
-	 * This method sets the height of generated texture (in pixels). By default the value is 20 px.
-	 * @param generatedTextureHeight
-	 *        the height of generated texture
-	 */
-	public void setGeneratedTextureHeight(int generatedTextureHeight) {
-		this.generatedTextureHeight = generatedTextureHeight;
-	}
-
-	/**
-	 * This method returns the height of generated texture (in pixels). By default the value is 20 px.
-	 * @return the height of generated texture
-	 */
-	public int getGeneratedTextureHeight() {
-		return generatedTextureHeight;
-	}
-	
-	/**
-	 * This method sets the depth of generated texture (in pixels). By default the value is 20 px.
-	 * @param generatedTextureDepth
-	 *        the depth of generated texture
-	 */
-	public void setGeneratedTextureDepth(int generatedTextureDepth) {
-		this.generatedTextureDepth = generatedTextureDepth;
-	}
-
-	/**
-	 * This method returns the depth of generated texture (in pixels). By default the value is 20 px.
-	 * @return the depth of generated texture
-	 */
-	public int getGeneratedTextureDepth() {
-		return generatedTextureDepth;
-	}
-
 	/**
 	 * This method returns the face cull mode.
 	 * @return the face cull mode
@@ -234,6 +184,24 @@ public class BlenderKey extends ModelKey {
 		return loadObjectProperties;
 	}
 	
+	/**
+	 * @return maximum texture size (width/height)
+	 */
+	public int getMaxTextureSize() {
+		if(maxTextureSize <= 0) {
+			maxTextureSize = GL11.glGetInteger(GL11.GL_MAX_TEXTURE_SIZE);
+		}
+		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 asset root path.
 	 * @param assetRootPath
@@ -329,6 +297,21 @@ public class BlenderKey extends ModelKey {
 		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;
+	}
+	
 	/**
 	 * 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
@@ -370,13 +353,11 @@ public class BlenderKey extends ModelKey {
 		super.write(e);
 		OutputCapsule oc = e.getCapsule(this);
 		oc.write(fps, "fps", DEFAULT_FPS);
-		oc.write(generatedTextureWidth, "generated-texture-width", 20);
-		oc.write(generatedTextureHeight, "generated-texture-height", 20);
-		oc.write(generatedTextureDepth, "generated-texture-depth", 20);
 		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);
@@ -388,13 +369,11 @@ public class BlenderKey extends ModelKey {
 		super.read(e);
 		InputCapsule ic = e.getCapsule(this);
 		fps = ic.readInt("fps", DEFAULT_FPS);
-		generatedTextureWidth = ic.readInt("generated-texture-width", 20);
-		generatedTextureHeight = ic.readInt("generated-texture-height", 20);
-		generatedTextureDepth = ic.readInt("generated-texture-depth", 20);
 		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);
@@ -411,9 +390,7 @@ public class BlenderKey extends ModelKey {
 		result = prime * result + featuresToLoad;
 		result = prime * result + (fixUpAxis ? 1231 : 1237);
 		result = prime * result + fps;
-		result = prime * result + generatedTextureDepth;
-		result = prime * result + generatedTextureHeight;
-		result = prime * result + generatedTextureWidth;
+		result = prime * result + generatedTexturePPU;
 		result = prime * result + layersToLoad;
 		result = prime * result + (loadUnlinkedAssets ? 1231 : 1237);
 		result = prime * result + (usedWorld == null ? 0 : usedWorld.hashCode());
@@ -458,13 +435,7 @@ public class BlenderKey extends ModelKey {
 		if (fps != other.fps) {
 			return false;
 		}
-		if (generatedTextureDepth != other.generatedTextureDepth) {
-			return false;
-		}
-		if (generatedTextureHeight != other.generatedTextureHeight) {
-			return false;
-		}
-		if (generatedTextureWidth != other.generatedTextureWidth) {
+		if (generatedTexturePPU != other.generatedTexturePPU) {
 			return false;
 		}
 		if (layersToLoad != other.layersToLoad) {
@@ -483,6 +454,8 @@ public class BlenderKey extends ModelKey {
 		return true;
 	}
 
+
+
 	/**
 	 * This interface describes the features of the scene that are to be loaded.
 	 * @author Marcin Roguski (Kaelthas)

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

@@ -31,12 +31,15 @@
  */
 package com.jme3.scene.plugins.blender;
 
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
 import com.jme3.asset.AssetLoader;
 import com.jme3.asset.BlenderKey.FeaturesToLoad;
 import com.jme3.asset.BlenderKey.WorldData;
 import com.jme3.light.AmbientLight;
 import com.jme3.light.Light;
-import com.jme3.material.Material;
 import com.jme3.math.ColorRGBA;
 import com.jme3.renderer.Camera;
 import com.jme3.scene.Geometry;
@@ -47,12 +50,8 @@ import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
 import com.jme3.scene.plugins.blender.file.Pointer;
 import com.jme3.scene.plugins.blender.file.Structure;
 import com.jme3.scene.plugins.blender.lights.LightHelper;
-import com.jme3.scene.plugins.blender.materials.MaterialHelper;
 import com.jme3.scene.plugins.blender.meshes.MeshHelper;
 import com.jme3.scene.plugins.blender.objects.ObjectHelper;
-import java.util.List;
-import java.util.logging.Level;
-import java.util.logging.Logger;
 
 /**
  * This class converts blender file blocks into jMonkeyEngine data structures.
@@ -150,19 +149,19 @@ import java.util.logging.Logger;
 		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

+ 0 - 28
engine/src/blender/com/jme3/scene/plugins/blender/BlenderContext.java

@@ -53,7 +53,6 @@ import com.jme3.scene.plugins.blender.file.BlenderInputStream;
 import com.jme3.scene.plugins.blender.file.DnaBlockData;
 import com.jme3.scene.plugins.blender.file.FileBlockHeader;
 import com.jme3.scene.plugins.blender.file.Structure;
-import com.jme3.scene.plugins.blender.materials.MaterialContext;
 import com.jme3.scene.plugins.blender.meshes.MeshContext;
 import com.jme3.scene.plugins.blender.modifiers.Modifier;
 import com.jme3.scene.plugins.ogre.AnimData;
@@ -117,8 +116,6 @@ public class BlenderContext {
 	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 of material contexts. */
-	protected Map<Material, MaterialContext>	materialContexts		= new HashMap<Material, MaterialContext>();
 	/** A map og helpers that perform loading. */
 	private Map<String, AbstractBlenderHelper>	helpers					= new HashMap<String, AbstractBlenderHelper>();
 
@@ -589,31 +586,6 @@ public class BlenderContext {
 		return boneContexts.get(boneOMA);
 	}
 
-	/**
-	 * This method sets the material context for the given material. If the
-	 * context is already set it will be replaced.
-	 * 
-	 * @param material
-	 *            the material
-	 * @param materialContext
-	 *            the material's context
-	 */
-	public void setMaterialContext(Material material, MaterialContext materialContext) {
-		this.materialContexts.put(material, materialContext);
-	}
-
-	/**
-	 * This method returns the material context for the given material. If no
-	 * context exists then <b>null</b> is returned.
-	 * 
-	 * @param material
-	 *            the material
-	 * @return material's context
-	 */
-	public MaterialContext getMaterialContext(Material material) {
-		return materialContexts.get(material);
-	}
-
 	/**
 	 * This metod returns the default material.
 	 * 

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

@@ -103,11 +103,11 @@ public class BlenderLoader extends AbstractBlenderLoader {
 							}
 						}
 						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_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) {
 							loadingResults.addScene(this.toScene(block.getStructure(blenderContext)));
@@ -203,11 +203,7 @@ public class BlenderLoader extends AbstractBlenderLoader {
 		blenderContext.putHelper(IpoHelper.class, new IpoHelper(inputStream.getVersionNumber(), blenderKey.isFixUpAxis()));
 		blenderContext.putHelper(ParticlesHelper.class, new ParticlesHelper(inputStream.getVersionNumber(), blenderKey.isFixUpAxis()));
 
-		// setting additional data to helpers
-		MaterialHelper materialHelper = blenderContext.getHelper(MaterialHelper.class);
-		materialHelper.setFaceCullMode(blenderKey.getFaceCullMode());
-
-		// reading the blocks (dna block is automatically saved in the blender context when found)//TODO: zmienić to
+		// reading the blocks (dna block is automatically saved in the blender context when found)
 		FileBlockHeader sceneFileBlock = null;
 		do {
 			fileBlock = new FileBlockHeader(inputStream, blenderContext);

+ 15 - 7
engine/src/blender/com/jme3/scene/plugins/blender/curves/CurvesHelper.java

@@ -11,6 +11,7 @@ import com.jme3.scene.plugins.blender.AbstractBlenderHelper;
 import com.jme3.scene.plugins.blender.BlenderContext;
 import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
 import com.jme3.scene.plugins.blender.file.*;
+import com.jme3.scene.plugins.blender.materials.MaterialContext;
 import com.jme3.scene.plugins.blender.materials.MaterialHelper;
 import com.jme3.scene.plugins.blender.meshes.MeshHelper;
 import com.jme3.scene.plugins.blender.objects.Properties;
@@ -88,12 +89,15 @@ public class CurvesHelper extends AbstractBlenderHelper {
 
         //getting materials
         MaterialHelper materialHelper = blenderContext.getHelper(MaterialHelper.class);
-        Material[] materials = materialHelper.getMaterials(curveStructure, blenderContext);
-        if (materials == null) {
-            materials = new Material[]{blenderContext.getDefaultMaterial().clone()};
-        }
-        for (Material material : materials) {
-            material.getAdditionalRenderState().setFaceCullMode(FaceCullMode.Off);
+        MaterialContext[] materialContexts = materialHelper.getMaterials(curveStructure, blenderContext);
+        Material defaultMaterial = null;
+        if (materialContexts != null) {
+        	for (MaterialContext materialContext : materialContexts) {
+	        	materialContext.setFaceCullMode(FaceCullMode.Off);
+	        }
+        } else {
+        	defaultMaterial = blenderContext.getDefaultMaterial().clone();
+        	defaultMaterial.getAdditionalRenderState().setFaceCullMode(FaceCullMode.Off);
         }
 
         //getting or creating bevel object
@@ -179,7 +183,11 @@ public class CurvesHelper extends AbstractBlenderHelper {
                 }
                 if (nurbGeoms != null) {//setting the name and assigning materials
                     for (Geometry nurbGeom : nurbGeoms) {
-                        nurbGeom.setMaterial(materials[nurbEntry.getKey().intValue()]);
+                    	if(materialContexts != null) {
+                    		materialContexts[nurbEntry.getKey().intValue()].applyMaterial(nurbGeom, curveStructure.getOldMemoryAddress(), false, null, blenderContext);
+                    	} else {
+                    		nurbGeom.setMaterial(defaultMaterial);
+                    	}
                         nurbGeom.setName(name);
                         result.add(nurbGeom);
                     }

+ 159 - 275
engine/src/blender/com/jme3/scene/plugins/blender/materials/MaterialContext.java

@@ -1,6 +1,22 @@
 package com.jme3.scene.plugins.blender.materials;
 
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.logging.Logger;
+
+import com.jme3.material.Material;
+import com.jme3.material.RenderState.BlendMode;
+import com.jme3.material.RenderState.FaceCullMode;
 import com.jme3.math.ColorRGBA;
+import com.jme3.math.Vector2f;
+import com.jme3.renderer.queue.RenderQueue.Bucket;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.VertexBuffer;
+import com.jme3.scene.VertexBuffer.Format;
+import com.jme3.scene.VertexBuffer.Usage;
 import com.jme3.scene.plugins.blender.BlenderContext;
 import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
 import com.jme3.scene.plugins.blender.file.DynamicArray;
@@ -8,19 +24,12 @@ import com.jme3.scene.plugins.blender.file.Pointer;
 import com.jme3.scene.plugins.blender.file.Structure;
 import com.jme3.scene.plugins.blender.materials.MaterialHelper.DiffuseShader;
 import com.jme3.scene.plugins.blender.materials.MaterialHelper.SpecularShader;
+import com.jme3.scene.plugins.blender.textures.CombinedTexture;
 import com.jme3.scene.plugins.blender.textures.TextureHelper;
 import com.jme3.scene.plugins.blender.textures.blending.TextureBlender;
 import com.jme3.scene.plugins.blender.textures.blending.TextureBlenderFactory;
 import com.jme3.texture.Texture;
-import com.jme3.texture.Texture.Type;
-import com.jme3.texture.Texture.WrapMode;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.logging.Level;
-import java.util.logging.Logger;
+import com.jme3.util.BufferUtils;
 
 /**
  * This class holds the data about the material.
@@ -37,12 +46,7 @@ public final class MaterialContext {
 	public static final int				MTEX_ALPHA = 0x80;
 	
 	/* package */final String			name;
-	/* package */final List<Structure>	mTexs;
-	/* package */final List<Structure>	textures;
-	/* package */final Map<Number, Texture> loadedTextures;
-	/* package */final Map<Texture, Structure> textureToMTexMap;
-	/* package */final int				texturesCount;
-	/* package */final Type				textureType;
+	/* package */final Map<Number, CombinedTexture> loadedTextures;
 
 	/* package */final ColorRGBA		diffuseColor;
 	/* package */final DiffuseShader 	diffuseShader;
@@ -54,10 +58,8 @@ public final class MaterialContext {
 	/* package */final boolean			vertexColor;
 	/* package */final boolean			transparent;
 	/* package */final boolean			vTangent;
-
-	/* package */int					uvCoordinatesType	= -1;
-	/* package */int					projectionType;
-
+	/* package */FaceCullMode 			faceCullMode;
+	
 	@SuppressWarnings("unchecked")
 	/* package */MaterialContext(Structure structure, BlenderContext blenderContext) throws BlenderFileException {
 		name = structure.getName();
@@ -97,85 +99,61 @@ public final class MaterialContext {
 			this.shininess = shininess > 0.0f ? shininess : MaterialHelper.DEFAULT_SHININESS;
 		}
 		
-		mTexs = new ArrayList<Structure>();
-		textures = new ArrayList<Structure>();
-		
 		DynamicArray<Pointer> mtexsArray = (DynamicArray<Pointer>) structure.getFieldValue("mtex");
 		int separatedTextures = ((Number) structure.getFieldValue("septex")).intValue();
-		Type firstTextureType = null;
+		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) {
-				Structure mtex = p.fetchData(blenderContext.getInputStream()).get(0);
-
-				// the first texture determines the texture coordinates type
-				if (uvCoordinatesType == -1) {
-					uvCoordinatesType = ((Number) mtex.getFieldValue("texco")).intValue();
-					projectionType = ((Number) mtex.getFieldValue("mapping")).intValue();
-				} else if (uvCoordinatesType != ((Number) mtex.getFieldValue("texco")).intValue()) {
-					LOGGER.log(Level.WARNING, "The texture with index: {0} has different UV coordinates type than the first texture! This texture will NOT be loaded!", i + 1);
-					continue;
-				}
+				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) mtex.getFieldValue("tex");
+				Pointer pTex = (Pointer) textureData.mtex.getFieldValue("tex");
 				if (pTex.isNotNull()) {
 					Structure tex = pTex.fetchData(blenderContext.getInputStream()).get(0);
-					int type = ((Number) tex.getFieldValue("type")).intValue();
-					Type textureType = this.getType(type);
-					if (textureType != null) {
-						if (firstTextureType == null) {
-							firstTextureType = textureType;
-							mTexs.add(mtex);
-							textures.add(tex);
-						} else if (firstTextureType == textureType) {
-							mTexs.add(mtex);
-							textures.add(tex);
-						} else {
-							LOGGER.log(Level.WARNING, "The texture with index: {0} is of different dimension than the first one! This texture will NOT be loaded!", i + 1);
-						}
-					}
+					textureData.textureStructure = tex;
+					texturesList.add(textureData);
 				}
 			}
 		}
 		
 		//loading the textures and merging them
-		Map<Number, List<Structure[]>> sortedTextures = this.sortAndFilterTextures();
-		loadedTextures = new HashMap<Number, Texture>(sortedTextures.size());
-		textureToMTexMap = new HashMap<Texture, Structure>();
+		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<Structure[]>> entry : sortedTextures.entrySet()) {
+		for(Entry<Number, List<TextureData>> entry : textureDataMap.entrySet()) {
 			if(entry.getValue().size()>0) {
-				List<Texture> textures = new ArrayList<Texture>(entry.getValue().size());
-				for(Structure[] mtexAndTex : entry.getValue()) {
-					int texflag = ((Number) mtexAndTex[0].getFieldValue("texflag")).intValue();
+				CombinedTexture combinedTexture = loadedTextures.get(entry.getKey());
+				if(combinedTexture == null) {
+					combinedTexture = new CombinedTexture();
+					loadedTextures.put(entry.getKey(), combinedTexture);
+				}
+				for(TextureData textureData : entry.getValue()) {
+					int texflag = ((Number) textureData.mtex.getFieldValue("texflag")).intValue();
 					boolean negateTexture = (texflag & 0x04) != 0;
-					Texture texture = textureHelper.getTexture(mtexAndTex[1], blenderContext);
-					int blendType = ((Number) mtexAndTex[0].getFieldValue("blendtype")).intValue();
-					float[] color = new float[] { ((Number) mtexAndTex[0].getFieldValue("r")).floatValue(), 
-												  ((Number) mtexAndTex[0].getFieldValue("g")).floatValue(), 
-												  ((Number) mtexAndTex[0].getFieldValue("b")).floatValue() };
-					float colfac = ((Number) mtexAndTex[0].getFieldValue("colfac")).floatValue();
-					TextureBlender textureBlender = TextureBlenderFactory.createTextureBlender(texture.getImage().getFormat());
-					texture = textureBlender.blend(diffuseColorArray, texture, color, colfac, blendType, negateTexture, blenderContext);
-					texture.setWrap(WrapMode.Repeat);
-					textures.add(texture);
-					textureToMTexMap.put(texture, mtexAndTex[0]);
+					Texture texture = textureHelper.getTexture(textureData.textureStructure, textureData.mtex, blenderContext);
+					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);
 				}
-				loadedTextures.put(entry.getKey(), textureHelper.mergeTextures(textures, this));
 			}
 		}
 
-		this.texturesCount = mTexs.size();
-		this.textureType = firstTextureType;
-		
 		//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(sortedTextures.size() > 0) {//texutre covers the material color
+			if(textureDataMap.size() > 0) {//texutre covers the material color
 				diffuseColor.set(1, 1, 1, 1);
 			}
 		}
@@ -188,6 +166,94 @@ public final class MaterialContext {
 		this.transparent = transparent;
 	}
 	
+	public void applyMaterial(Geometry geometry, Long geometriesOMA, boolean noTextures, 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
+		for(Entry<Number, CombinedTexture> entry : loadedTextures.entrySet()) {
+			CombinedTexture combinedTexture = entry.getValue();
+			combinedTexture.flatten(geometry, geometriesOMA, userDefinedUVCoordinates, blenderContext);
+			VertexBuffer.Type uvCoordinatesType = null;
+			
+			switch(entry.getKey().intValue()) {
+				case MTEX_COL:
+					uvCoordinatesType = VertexBuffer.Type.TexCoord;
+					material.setTexture(shadeless ? MaterialHelper.TEXTURE_TYPE_COLOR : MaterialHelper.TEXTURE_TYPE_DIFFUSE, 
+							combinedTexture.getResultTexture());
+					break;
+				case MTEX_NOR:
+					uvCoordinatesType = VertexBuffer.Type.TexCoord2;
+					material.setTexture(MaterialHelper.TEXTURE_TYPE_NORMAL, combinedTexture.getResultTexture());
+					break;
+				case MTEX_SPEC:
+					uvCoordinatesType = VertexBuffer.Type.TexCoord3;
+					material.setTexture(MaterialHelper.TEXTURE_TYPE_SPECULAR, combinedTexture.getResultTexture());
+					break;
+				case MTEX_EMIT:
+					uvCoordinatesType = VertexBuffer.Type.TexCoord4;
+					material.setTexture(MaterialHelper.TEXTURE_TYPE_GLOW, combinedTexture.getResultTexture());
+					break;
+				case MTEX_ALPHA:
+					uvCoordinatesType = VertexBuffer.Type.TexCoord5;
+					material.setTexture(MaterialHelper.TEXTURE_TYPE_ALPHA, combinedTexture.getResultTexture());
+					break;
+				default:
+					LOGGER.severe("Unknown mapping type: " + entry.getKey().intValue());
+			}
+			
+			//applying texture coordinates
+			if(uvCoordinatesType != null) {
+				VertexBuffer uvCoordsBuffer = new VertexBuffer(uvCoordinatesType);
+	            uvCoordsBuffer.setupData(Usage.Static, 2, Format.Float,
+	                    BufferUtils.createFloatBuffer(combinedTexture.getResultUVS().toArray(new Vector2f[combinedTexture.getResultUVS().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);
+            geometry.setQueueBucket(Bucket.Transparent);
+        }
+        
+        geometry.setMaterial(material);
+	}
+	
 	/**
 	 * This method sorts the textures by their mapping type.
 	 * In each group only textures of one type are put (either two- or three-dimensional).
@@ -195,100 +261,33 @@ public final class MaterialContext {
 	 * 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<Structure[]>> sortAndFilterTextures() {
-		Map<Number, List<Structure[]>> result = new HashMap<Number, List<Structure[]>>();
-		for (int i = 0; i < mTexs.size(); ++i) {
-			Structure mTex = mTexs.get(i);
-			Structure texture  = textures.get(i);
-			Number mapto = (Number) mTex.getFieldValue("mapto");
-			List<Structure[]> mtexs = result.get(mapto);
-			if(mtexs==null) {
-				mtexs = new ArrayList<Structure[]>();
-				result.put(mapto, mtexs);
-			}
-			if(mapto.intValue() == MTEX_COL && this.isWithoutAlpha(textures.get(i))) {
-				mtexs.clear();//remove previous textures, they will be covered anyway
+	private Map<Number, List<TextureData>> sortAndFilterTextures(List<TextureData> textures) {
+		Map<Number, List<TextureData>> result = new HashMap<Number, List<TextureData>>();
+		for (TextureData data : textures) {
+			Number mapto = (Number) data.mtex.getFieldValue("mapto");
+			List<TextureData> datas = result.get(mapto);
+			if(datas==null) {
+				datas = new ArrayList<TextureData>();
+				result.put(mapto, datas);
 			}
-			mtexs.add(new Structure[] {mTex, texture});
+			datas.add(data);
 		}
 		return result;
 	}
 	
 	/**
-	 * 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(Structure texture) {
-		int flag = ((Number) texture.getFieldValue("flag")).intValue();
-		if((flag & 0x01) == 0) {//the texture has no colorband
-			int type = ((Number) texture.getFieldValue("type")).intValue();
-			if(type==TextureHelper.TEX_MAGIC) {
-				return true;
-			}
-			if(type==TextureHelper.TEX_VORONOI) {
-				int voronoiColorType = ((Number) texture.getFieldValue("vn_coltype")).intValue();
-				return voronoiColorType != 0;//voronoiColorType == 0: intensity, voronoiColorType != 0: col1, col2 or col3
-			}
-			if(type==TextureHelper.TEX_CLOUDS) {
-				int sType = ((Number) texture.getFieldValue("stype")).intValue();
-				return sType == 1;//sType==0: without colors, sType==1: with colors
-			}
-		}
-		return false;
-	}
-
-	/**
-	 * This method returns the current material's texture UV coordinates type.
-	 * @return uv coordinates type
-	 */
-	public int getUvCoordinatesType() {
-		return uvCoordinatesType;
-	}
-
-	/**
-	 * This method returns the proper projection type for the material's texture.
-	 * This applies only to 2D textures.
-	 * @return texture's projection type
-	 */
-	public int getProjectionType() {
-		return projectionType;
-	}
-
-	/**
-	 * This method returns current material's texture dimension.
-	 * @return the material's texture dimension
-	 */
-	public int getTextureDimension() {
-		return this.textureType == Type.TwoDimensional ? 2 : 3;
-	}
-
-	/**
-	 * This method returns the amount of textures applied for the current
-	 * material.
-	 * 
-	 * @return the amount of textures applied for the current material
+	 * This method sets the face cull mode.
+	 * @param faceCullMode the face cull mode
 	 */
-	public int getTexturesCount() {
-		return textures == null ? 0 : textures.size();
+	public void setFaceCullMode(FaceCullMode faceCullMode) {
+		this.faceCullMode = faceCullMode;
 	}
-
+	
 	/**
-	 * This method returns the projection array that indicates where the current coordinate factor X, Y or Z (represented
-	 * by the index in the array) will be used where (indicated by the value in the array).
-	 * For example the configuration: [1,2,3] means that X - coordinate will be used as X, Y as Y and Z as Z.
-	 * The configuration [2,1,0] means that Z will be used instead of X coordinate, Y will be used as Y and
-	 * Z will not be used at all (0 will be in its place).
-	 * @param textureIndex
-	 *        the index of the texture
-	 * @return texture projection array
+	 * @return the face cull mode
 	 */
-	public int[] getProjection(int textureIndex) {
-		Structure mtex = mTexs.get(textureIndex);
-		return new int[] { ((Number) mtex.getFieldValue("projx")).intValue(), ((Number) mtex.getFieldValue("projy")).intValue(), ((Number) mtex.getFieldValue("projz")).intValue() };
+	public FaceCullMode getFaceCullMode() {
+		return faceCullMode;
 	}
 	
 	/**
@@ -358,126 +357,11 @@ public final class MaterialContext {
 		}
 		return new ColorRGBA(r, g, b, alpha);
 	}
-
-	/**
-	 * This method determines the type of the texture.
-	 * @param texType
-	 *        texture type (from blender)
-	 * @return texture type (used by jme)
-	 */
-	private Type getType(int texType) {
-		switch (texType) {
-			case TextureHelper.TEX_IMAGE:// (it is first because probably this will be most commonly used)
-				return Type.TwoDimensional;
-			case TextureHelper.TEX_CLOUDS:
-			case TextureHelper.TEX_WOOD:
-			case TextureHelper.TEX_MARBLE:
-			case TextureHelper.TEX_MAGIC:
-			case TextureHelper.TEX_BLEND:
-			case TextureHelper.TEX_STUCCI:
-			case TextureHelper.TEX_NOISE:
-			case TextureHelper.TEX_MUSGRAVE:
-			case TextureHelper.TEX_VORONOI:
-			case TextureHelper.TEX_DISTNOISE:
-				return Type.ThreeDimensional;
-			case TextureHelper.TEX_NONE:// No texture, do nothing
-				return null;
-			case TextureHelper.TEX_POINTDENSITY:
-			case TextureHelper.TEX_VOXELDATA:
-			case TextureHelper.TEX_PLUGIN:
-			case TextureHelper.TEX_ENVMAP:
-				LOGGER.log(Level.WARNING, "Texture type NOT supported: {0}", texType);
-				return null;
-			default:
-				throw new IllegalStateException("Unknown texture type: " + texType);
-		}
-	}
-	
-	/**
-	 * @return he material's name
-	 */
-	public String getName() {
-		return name;
-	}
-	
-	/**
-	 * @return a copy of diffuse color
-	 */
-	public ColorRGBA getDiffuseColor() {
-		return diffuseColor.clone();
-	}
-	
-	/**
-	 * @return an enum describing the type of a diffuse shader used by this material
-	 */
-	public DiffuseShader getDiffuseShader() {
-		return diffuseShader;
-	}
-	
-	/**
-	 * @return a copy of specular color
-	 */
-	public ColorRGBA getSpecularColor() {
-		return specularColor.clone();
-	}
 	
-	/**
-	 * @return an enum describing the type of a specular shader used by this material
-	 */
-	public SpecularShader getSpecularShader() {
-		return specularShader;
-	}
-	
-	/**
-	 * @return an ambient color used by the material
-	 */
-	public ColorRGBA getAmbientColor() {
-		return ambientColor;
-	}
-	
-	/**
-	 * @return the sihiness of this material
-	 */
-	public float getShininess() {
-		return shininess;
-	}
-	
-	/**
-	 * @return <b>true</b> if the material is shadeless and <b>false</b> otherwise
-	 */
-	public boolean isShadeless() {
-		return shadeless;
-	}
-	
-	/**
-	 * @return <b>true</b> if the material uses vertex color and <b>false</b> otherwise
-	 */
-	public boolean isVertexColor() {
-		return vertexColor;
-	}
-	
-	/**
-	 * @return <b>true</b> if the material is transparent and <b>false</b> otherwise
-	 */
-	public boolean isTransparent() {
-		return transparent;
-	}
-	
-	/**
-	 * @return <b>true</b> if the material uses tangents and <b>false</b> otherwise
-	 */
-	public boolean isvTangent() {
-		return vTangent;
-	}
-	
-	/**
-	 * @param texture
-	 *            the texture for which its mtex structure definition will be
-	 *            fetched
-	 * @return mtex structure of the current texture or <b>null</b> if none
-	 *         exists
-	 */
-	public Structure getMTex(Texture texture) {
-		return textureToMTexMap.get(texture);
+	private static class TextureData {
+		Structure mtex;
+		Structure textureStructure;
+		int uvCoordinatesType;
+		int projectionType;
 	}
 }

+ 16 - 129
engine/src/blender/com/jme3/scene/plugins/blender/materials/MaterialHelper.java

@@ -31,12 +31,18 @@
  */
 package com.jme3.scene.plugins.blender.materials;
 
+import java.nio.ByteBuffer;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
 import com.jme3.asset.BlenderKey.FeaturesToLoad;
 import com.jme3.material.MatParam;
 import com.jme3.material.MatParamTexture;
 import com.jme3.material.Material;
-import com.jme3.material.RenderState.BlendMode;
-import com.jme3.material.RenderState.FaceCullMode;
 import com.jme3.math.ColorRGBA;
 import com.jme3.math.FastMath;
 import com.jme3.scene.plugins.blender.AbstractBlenderHelper;
@@ -49,21 +55,12 @@ import com.jme3.shader.VarType;
 import com.jme3.texture.Image;
 import com.jme3.texture.Image.Format;
 import com.jme3.texture.Texture;
-import com.jme3.texture.Texture.Type;
 import com.jme3.util.BufferUtils;
-import java.nio.ByteBuffer;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-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_3D			= "Texture";
 	public static final String					TEXTURE_TYPE_COLOR		= "ColorMap";
 	public static final String					TEXTURE_TYPE_DIFFUSE	= "DiffuseMap";
 	public static final String					TEXTURE_TYPE_NORMAL		= "NormalMap";
@@ -91,9 +88,6 @@ public class MaterialHelper extends AbstractBlenderHelper {
 		COOKTORRENCE, PHONG, BLINN, TOON, WARDISO
 	}
 
-	/** Face cull mode. Should be excplicitly set before this helper is used. */
-	protected FaceCullMode	faceCullMode;
-
 	/**
 	 * This constructor parses the given blender version and stores the result. Some functionalities may differ in different blender
 	 * versions.
@@ -165,16 +159,6 @@ public class MaterialHelper extends AbstractBlenderHelper {
 		});
 	}
 
-	/**
-	 * This method sets the face cull mode to be used with every loaded material.
-	 * 
-	 * @param faceCullMode
-	 *        the face cull mode
-	 */
-	public void setFaceCullMode(FaceCullMode faceCullMode) {
-		this.faceCullMode = faceCullMode;
-	}
-
 	/**
 	 * This method converts the material structure to jme Material.
 	 * @param structure
@@ -185,105 +169,15 @@ public class MaterialHelper extends AbstractBlenderHelper {
 	 * @throws BlenderFileException
 	 *         an exception is throw when problems with blend file occur
 	 */
-	public Material toMaterial(Structure structure, BlenderContext blenderContext) throws BlenderFileException {
+	public MaterialContext toMaterialContext(Structure structure, BlenderContext blenderContext) throws BlenderFileException {
 		LOGGER.log(Level.INFO, "Loading material.");
-		if (structure == null) {
-			return blenderContext.getDefaultMaterial();
-		}
-		Material result = (Material) blenderContext.getLoadedFeature(structure.getOldMemoryAddress(), LoadedFeatureDataType.LOADED_FEATURE);
+		MaterialContext result = (MaterialContext) blenderContext.getLoadedFeature(structure.getOldMemoryAddress(), LoadedFeatureDataType.LOADED_FEATURE);
 		if (result != null) {
 			return result;
 		}
 		
-		MaterialContext materialContext = new MaterialContext(structure, blenderContext);
-		LOGGER.log(Level.INFO, "Material's name: {0}", materialContext.name);
-		
-		if(materialContext.textures.size() > 1) {
-			LOGGER.log(Level.WARNING, "Attetion! Many textures found for material: {0}. Only the first of each supported mapping types will be used!", materialContext.name);
-		}
-		
-		// texture
-		Type colorTextureType = null;
-		Map<String, Texture> texturesMap = new HashMap<String, Texture>();
-		for(Entry<Number, Texture> textureEntry : materialContext.loadedTextures.entrySet()) {
-			int mapto = textureEntry.getKey().intValue();
-			Texture texture = textureEntry.getValue();
-			if ((mapto & MaterialContext.MTEX_COL) != 0) {
-				colorTextureType = texture.getType();
-				if (materialContext.shadeless) {
-					texturesMap.put(colorTextureType==Type.ThreeDimensional ? TEXTURE_TYPE_3D : TEXTURE_TYPE_COLOR, texture);
-				} else {
-					texturesMap.put(colorTextureType==Type.ThreeDimensional ? TEXTURE_TYPE_3D : TEXTURE_TYPE_DIFFUSE, texture);
-				}
-			}
-			if(texture.getType()==Type.TwoDimensional) {//so far only 2D textures can be mapped in other way than color
-				if ((mapto & MaterialContext.MTEX_NOR) != 0 && !materialContext.shadeless) {
-					//Structure mTex = materialContext.getMTex(texture);
-					//Texture normalMapTexture = textureHelper.convertToNormalMapTexture(texture, ((Number) mTex.getFieldValue("norfac")).floatValue());
-					//texturesMap.put(TEXTURE_TYPE_NORMAL, normalMapTexture);
-                                        texturesMap.put(TEXTURE_TYPE_NORMAL, texture);
-				}
-				if ((mapto & MaterialContext.MTEX_EMIT) != 0) {
-					texturesMap.put(TEXTURE_TYPE_GLOW, texture);
-				}
-				if ((mapto & MaterialContext.MTEX_SPEC) != 0 && !materialContext.shadeless) {
-					texturesMap.put(TEXTURE_TYPE_SPECULAR, texture);
-				}
-				if ((mapto & MaterialContext.MTEX_ALPHA) != 0 && !materialContext.shadeless) {
-					texturesMap.put(TEXTURE_TYPE_ALPHA, texture);
-				}
-			}
-		}
-		
-		//creating the material
-		if(colorTextureType==Type.ThreeDimensional) {
-			result = new Material(blenderContext.getAssetManager(), "Common/MatDefs/Texture3D/tex3D.j3md");
-		} else {
-			if (materialContext.shadeless) {
-				result = new Material(blenderContext.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");
-                
-                if (!materialContext.transparent) {
-                    materialContext.diffuseColor.a = 1;
-                }
-                
-                result.setColor("Color", materialContext.diffuseColor);
-			} else {
-				result = new Material(blenderContext.getAssetManager(), "Common/MatDefs/Light/Lighting.j3md");
-				result.setBoolean("UseMaterialColors", Boolean.TRUE);
-
-				// setting the colors
-				result.setBoolean("Minnaert", materialContext.diffuseShader == DiffuseShader.MINNAERT);
-				if (!materialContext.transparent) {
-					materialContext.diffuseColor.a = 1;
-				}
-				result.setColor("Diffuse", materialContext.diffuseColor);
-
-				result.setBoolean("WardIso", materialContext.specularShader == SpecularShader.WARDISO);
-				result.setColor("Specular", materialContext.specularColor);
-
-				result.setColor("Ambient", materialContext.ambientColor);
-				result.setFloat("Shininess", materialContext.shininess);
-			}
-			
-			if (materialContext.vertexColor) {
-				result.setBoolean(materialContext.shadeless ? "VertexColor" : "UseVertexColor", true);
-			}
-		}
-		
-		//applying textures
-		for(Entry<String, Texture> textureEntry : texturesMap.entrySet()) {
-			result.setTexture(textureEntry.getKey(), textureEntry.getValue());
-		}
-		
-		//applying other data
-		result.getAdditionalRenderState().setFaceCullMode(faceCullMode);
-		if (materialContext.transparent) {
-			result.setTransparent(true);
-			result.getAdditionalRenderState().setBlendMode(BlendMode.Alpha);
-		}
-		
-		result.setName(materialContext.getName());
-		blenderContext.setMaterialContext(result, materialContext);
+		result = new MaterialContext(structure, blenderContext);
+		LOGGER.log(Level.INFO, "Material's name: {0}", result.name);
 		blenderContext.addLoadedFeatures(structure.getOldMemoryAddress(), structure.getName(), structure, result);
 		return result;
 	}
@@ -392,9 +286,6 @@ public class MaterialHelper extends AbstractBlenderHelper {
 	 */
 	public boolean hasTexture(Material material) {
 		if (material != null) {
-			if (material.getTextureParam(TEXTURE_TYPE_3D) != null) {
-				return true;
-			}
 			if (material.getTextureParam(TEXTURE_TYPE_ALPHA) != null) {
 				return true;
 			}
@@ -445,21 +336,17 @@ public class MaterialHelper extends AbstractBlenderHelper {
 	 * @throws BlenderFileException
 	 *         this exception is thrown when the blend file structure is somehow invalid or corrupted
 	 */
-	public Material[] getMaterials(Structure structureWithMaterials, BlenderContext blenderContext) throws BlenderFileException {
+	public MaterialContext[] getMaterials(Structure structureWithMaterials, BlenderContext blenderContext) throws BlenderFileException {
 		Pointer ppMaterials = (Pointer) structureWithMaterials.getFieldValue("mat");
-		Material[] materials = null;
+		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 Material[materialStructures.size()];
+				materials = new MaterialContext[materialStructures.size()];
 				int i = 0;
 				for (Structure s : materialStructures) {
-					Material material = (Material) blenderContext.getLoadedFeature(s.getOldMemoryAddress(), LoadedFeatureDataType.LOADED_FEATURE);
-					if (material == null) {
-						material = materialHelper.toMaterial(s, blenderContext);
-					}
-					materials[i++] = material;
+					materials[i++] = materialHelper.toMaterialContext(s, blenderContext);
 				}
 			}
 		}

+ 42 - 90
engine/src/blender/com/jme3/scene/plugins/blender/meshes/MeshHelper.java

@@ -40,11 +40,9 @@ import java.util.Map;
 import java.util.Map.Entry;
 
 import com.jme3.asset.BlenderKey.FeaturesToLoad;
-import com.jme3.material.Material;
 import com.jme3.math.FastMath;
 import com.jme3.math.Vector2f;
 import com.jme3.math.Vector3f;
-import com.jme3.renderer.queue.RenderQueue.Bucket;
 import com.jme3.scene.Geometry;
 import com.jme3.scene.Mesh;
 import com.jme3.scene.VertexBuffer;
@@ -62,7 +60,6 @@ import com.jme3.scene.plugins.blender.materials.MaterialContext;
 import com.jme3.scene.plugins.blender.materials.MaterialHelper;
 import com.jme3.scene.plugins.blender.objects.Properties;
 import com.jme3.scene.plugins.blender.textures.TextureHelper;
-import com.jme3.scene.plugins.blender.textures.UVCoordinatesGenerator;
 import com.jme3.texture.Texture;
 import com.jme3.util.BufferUtils;
 
@@ -135,7 +132,7 @@ public class MeshHelper extends AbstractBlenderHelper {
         }
 
         Pointer pMTFace = (Pointer) structure.getFieldValue("mtface");
-        List<Vector2f> uvCoordinates = null;
+        Map<Integer, List<Vector2f>> uvCoordinates = new HashMap<Integer, List<Vector2f>>();//<material_number; list of uv coordinates for mesh's vertices>
         List<Structure> mtFaces = null;
 
         if (pMTFace.isNotNull()) {
@@ -144,7 +141,6 @@ public class MeshHelper extends AbstractBlenderHelper {
             if (mtFaces.size() != facesAmount) {
                 throw new BlenderFileException("The amount of faces uv coordinates is not equal to faces amount!");
             }
-            uvCoordinates = new ArrayList<Vector2f>();
         }
 
         // normalMap merges normals of faces that will be rendered smooth
@@ -160,21 +156,28 @@ public class MeshHelper extends AbstractBlenderHelper {
         int vertexColorIndex = 0;
         for (int i = 0; i < mFaces.size(); ++i) {
             Structure mFace = mFaces.get(i);
+            int matNr = ((Number) mFace.getFieldValue("mat_nr")).intValue();
             boolean smooth = (((Number) mFace.getFieldValue("flag")).byteValue() & 0x01) != 0x00;
             DynamicArray<Number> uvs = null;
             boolean materialWithoutTextures = false;
             Pointer pImage = null;
+            
+            List<Vector2f> uvCoordinatesList = uvCoordinates.get(Integer.valueOf(matNr));
+            if(uvCoordinatesList == null) {
+            	uvCoordinatesList = new ArrayList<Vector2f>();
+            	uvCoordinates.put(Integer.valueOf(matNr), uvCoordinatesList);
+            }
+            
             if (mtFaces != null) {
                 Structure mtFace = mtFaces.get(i);
                 pImage = (Pointer) mtFace.getFieldValue("tpage");
                 materialWithoutTextures = pImage.isNull();
                 // uvs always must be added wheater we have texture or not
                 uvs = (DynamicArray<Number>) mtFace.getFieldValue("uv");
-                uvCoordinates.add(new Vector2f(uvs.get(0, 0).floatValue(), uvs.get(0, 1).floatValue()));
-                uvCoordinates.add(new Vector2f(uvs.get(1, 0).floatValue(), uvs.get(1, 1).floatValue()));
-                uvCoordinates.add(new Vector2f(uvs.get(2, 0).floatValue(), uvs.get(2, 1).floatValue()));
+                uvCoordinatesList.add(new Vector2f(uvs.get(0, 0).floatValue(), uvs.get(0, 1).floatValue()));
+                uvCoordinatesList.add(new Vector2f(uvs.get(1, 0).floatValue(), uvs.get(1, 1).floatValue()));
+                uvCoordinatesList.add(new Vector2f(uvs.get(2, 0).floatValue(), uvs.get(2, 1).floatValue()));
             }
-            int matNr = ((Number) mFace.getFieldValue("mat_nr")).intValue();
             Integer materialNumber = Integer.valueOf(materialWithoutTextures ? -1 * matNr - 1 : matNr);
             List<Integer> indexList = meshesMap.get(materialNumber);
             if (indexList == null) {
@@ -216,9 +219,9 @@ public class MeshHelper extends AbstractBlenderHelper {
 
             if (v4 > 0) {
                 if (uvs != null) {
-                    uvCoordinates.add(new Vector2f(uvs.get(0, 0).floatValue(), uvs.get(0, 1).floatValue()));
-                    uvCoordinates.add(new Vector2f(uvs.get(2, 0).floatValue(), uvs.get(2, 1).floatValue()));
-                    uvCoordinates.add(new Vector2f(uvs.get(3, 0).floatValue(), uvs.get(3, 1).floatValue()));
+                	uvCoordinatesList.add(new Vector2f(uvs.get(0, 0).floatValue(), uvs.get(0, 1).floatValue()));
+                	uvCoordinatesList.add(new Vector2f(uvs.get(2, 0).floatValue(), uvs.get(2, 1).floatValue()));
+                	uvCoordinatesList.add(new Vector2f(uvs.get(3, 0).floatValue(), uvs.get(3, 1).floatValue()));
                 }
                 this.appendVertexReference(v1, vertexList.size(), vertexReferenceMap);
                 indexList.add(vertexList.size());
@@ -266,11 +269,9 @@ public class MeshHelper extends AbstractBlenderHelper {
 
         // reading materials
         MaterialHelper materialHelper = blenderContext.getHelper(MaterialHelper.class);
-        Material[] materials = null;
-        Material[] nonTexturedMaterials = null;
+        MaterialContext[] materials = null;
         if ((blenderContext.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.MATERIALS) != 0) {
             materials = materialHelper.getMaterials(structure, blenderContext);
-            nonTexturedMaterials = materials == null ? null : new Material[materials.length];// fill it when needed
         }
 
         // creating the result meshes
@@ -291,23 +292,20 @@ public class MeshHelper extends AbstractBlenderHelper {
         VertexBuffer normalsBind = new VertexBuffer(Type.BindPoseNormal);
         normalsBind.setupData(Usage.CpuOnly, 3, Format.Float, BufferUtils.clone(normalsBuffer.getData()));
 
-        VertexBuffer uvCoordsBuffer = null;
-        if (uvCoordinates != null) {
-            uvCoordsBuffer = new VertexBuffer(Type.TexCoord);
-            uvCoordsBuffer.setupData(Usage.Static, 2, Format.Float,
-                    BufferUtils.createFloatBuffer(uvCoordinates.toArray(new Vector2f[uvCoordinates.size()])));
-        }
-
         //reading custom properties
         Properties properties = this.loadProperties(structure, blenderContext);
 
         // generating meshes
         //FloatBuffer verticesColorsBuffer = this.createFloatBuffer(verticesColors);
-        ByteBuffer verticesColorsBuffer = createByteBuffer(verticesColors);
+        ByteBuffer verticesColorsBuffer = this.createByteBuffer(verticesColors);
         verticesAmount = vertexList.size();
+        Map<Mesh, Integer> meshToMAterialMap = new HashMap<Mesh, Integer>(meshesMap.size());
         for (Entry<Integer, List<Integer>> meshEntry : meshesMap.entrySet()) {
+        	//key is the material index (or -1 if the material has no texture)
+        	//value is a list of vertex indices
             Mesh mesh = new Mesh();
-
+            meshToMAterialMap.put(mesh, meshEntry.getKey());
+            
             // creating vertices indices for this mesh
             List<Integer> indexList = meshEntry.getValue();
             if(verticesAmount <= Short.MAX_VALUE) {
@@ -339,76 +337,32 @@ public class MeshHelper extends AbstractBlenderHelper {
 
             // creating the result
             Geometry geometry = new Geometry(name + (geometries.size() + 1), mesh);
-            if (materials != null) {
-                int materialNumber = meshEntry.getKey().intValue();
-                Material material;
-                if (materialNumber >= 0) {
-                    material = materials[materialNumber];
-                    if (materialNumberToTexture.containsKey(Integer.valueOf(materialNumber))) {
-                        if (material.getMaterialDef().getAssetName().contains("Lighting")) {
-                            if (!materialHelper.hasTexture(material, MaterialHelper.TEXTURE_TYPE_DIFFUSE)) {
-                                material = material.clone();
-                                material.setTexture(MaterialHelper.TEXTURE_TYPE_DIFFUSE,
-                                        materialNumberToTexture.get(Integer.valueOf(materialNumber)));
-                            }
-                        } else {
-                            if (!materialHelper.hasTexture(material, MaterialHelper.TEXTURE_TYPE_COLOR)) {
-                                material = material.clone();
-                                material.setTexture(MaterialHelper.TEXTURE_TYPE_COLOR,
-                                        materialNumberToTexture.get(Integer.valueOf(materialNumber)));
-                            }
-                        }
-                    }
-                } else {
-                    materialNumber = -1 * (materialNumber + 1);
-                    if (nonTexturedMaterials[materialNumber] == null) {
-                        nonTexturedMaterials[materialNumber] = materialHelper.getNonTexturedMaterial(materials[materialNumber],
-                                TextureHelper.TEX_IMAGE);
-                    }
-                    material = nonTexturedMaterials[materialNumber];
-                }
-                geometry.setMaterial(material);
-                if (material.isTransparent()) {
-                    geometry.setQueueBucket(Bucket.Transparent);
-                }
-            } else {
-                geometry.setMaterial(blenderContext.getDefaultMaterial());
-            }
             if (properties != null && properties.getValue() != null) {
                 geometry.setUserData("properties", properties);
             }
             geometries.add(geometry);
         }
-
-        //applying uvCoordinates for all the meshes
-        if (uvCoordsBuffer != null) {
-            for (Geometry geom : geometries) {
-                geom.getMesh().setBuffer(uvCoordsBuffer);
-            }
-        } else {
-            Map<Material, List<Geometry>> materialMap = new HashMap<Material, List<Geometry>>();
-            for (Geometry geom : geometries) {
-                Material material = geom.getMaterial();
-                List<Geometry> geomsWithCommonMaterial = materialMap.get(material);
-                if (geomsWithCommonMaterial == null) {
-                    geomsWithCommonMaterial = new ArrayList<Geometry>();
-                    materialMap.put(material, geomsWithCommonMaterial);
-                }
-                geomsWithCommonMaterial.add(geom);
-
-            }
-            for (Entry<Material, List<Geometry>> entry : materialMap.entrySet()) {
-                MaterialContext materialContext = blenderContext.getMaterialContext(entry.getKey());
-                if (materialContext != null && materialContext.getTexturesCount() > 0) {
-                    VertexBuffer coords = UVCoordinatesGenerator.generateUVCoordinates(materialContext.getUvCoordinatesType(),
-                            materialContext.getProjectionType(), materialContext.getTextureDimension(),
-                            materialContext.getProjection(0), entry.getValue());
-                    //setting the coordinates inside the mesh context
-                    for (Geometry geometry : entry.getValue()) {
-                        meshContext.addUVCoordinates(geometry, coords);
-                    }
+        
+        //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 = meshToMAterialMap.get(geometry.getMesh()).intValue();
+                boolean noTextures = false;
+                if(materialNumber < 0) {
+                	materialNumber = -1 * (materialNumber + 1);
+                	noTextures = true;
                 }
-            }
+                MaterialContext materialContext = materials[materialNumber];
+                materialContext.applyMaterial(geometry, structure.getOldMemoryAddress(), noTextures, uvCoordinates.get(Integer.valueOf(materialNumber)), blenderContext);
+        	}
+        } else {
+        	for(Geometry geometry : geometries) {
+        		geometry.setMaterial(blenderContext.getDefaultMaterial());
+        	}
         }
         
         // if there are multiple materials used, extract the shared
@@ -420,8 +374,6 @@ public class MeshHelper extends AbstractBlenderHelper {
             }
         }
 
-        blenderContext.addLoadedFeatures(structure.getOldMemoryAddress(), structure.getName(), structure, geometries);
-        blenderContext.setMeshContext(structure.getOldMemoryAddress(), meshContext);
         return geometries;
     }
 

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

@@ -0,0 +1,315 @@
+package com.jme3.scene.plugins.blender.textures;
+
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import com.jme3.math.FastMath;
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
+import com.jme3.scene.plugins.blender.file.DynamicArray;
+import com.jme3.scene.plugins.blender.file.Pointer;
+import com.jme3.scene.plugins.blender.file.Structure;
+
+/**
+ * A class constaining the colorband data.
+ * 
+ * @author Marcin Roguski (Kaelthas)
+ */
+public class ColorBand {
+	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;
+
+	private int					cursorsAmount, ipoType;
+	private ColorBandData[]		data;
+
+	/**
+	 * Constructor. Loads the data from the given structure.
+	 * 
+	 * @param cbdataStructure
+	 *            the colorband structure
+	 */
+	@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 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[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);
+						}
+
+						float[] ipoFactors = new float[4];
+						float f;
+
+						ColorBandData data0 = cbDataMap.get(currentCursor - 2);
+						ColorBandData data1 = cbDataMap.get(currentCursor - 1);
+						ColorBandData data2 = cbDataMap.get(currentCursor);
+						ColorBandData data3 = cbDataMap.get(currentCursor + 1);
+
+						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;
+							}
+
+							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;
+	}
+
+	/**
+	 * 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;
+
+		/**
+		 * 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 String toString() {
+			return "P: " + this.pos + " [" + this.r + ", " + this.g + ", " + this.b + ", " + this.a + "]";
+		}
+	}
+}

+ 367 - 0
engine/src/blender/com/jme3/scene/plugins/blender/textures/CombinedTexture.java

@@ -0,0 +1,367 @@
+package com.jme3.scene.plugins.blender.textures;
+
+import java.awt.Graphics2D;
+import java.awt.RenderingHints;
+import java.awt.image.BufferedImage;
+import java.util.ArrayList;
+import java.util.List;
+
+import jme3tools.converters.ImageToAwt;
+
+import com.jme3.math.Vector2f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Mesh;
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.BlenderContext.LoadedFeatureDataType;
+import com.jme3.scene.plugins.blender.file.Structure;
+import com.jme3.scene.plugins.blender.textures.UVCoordinatesGenerator.UVCoordinatesType;
+import com.jme3.scene.plugins.blender.textures.UVProjectionGenerator.UVProjectionType;
+import com.jme3.scene.plugins.blender.textures.blending.TextureBlender;
+import com.jme3.scene.plugins.blender.textures.blending.TextureBlenderFactory;
+import com.jme3.scene.plugins.blender.textures.io.PixelIOFactory;
+import com.jme3.scene.plugins.blender.textures.io.PixelInputOutput;
+import com.jme3.texture.Image;
+import com.jme3.texture.Texture;
+import com.jme3.texture.Texture.MagFilter;
+import com.jme3.texture.Texture.MinFilter;
+import com.jme3.texture.Texture.WrapMode;
+import com.jme3.texture.Texture2D;
+
+/**
+ * This class represents a texture that is defined for the material. It can be
+ * made of several textures (both 2D and 3D) that are merged together and
+ * returned as a single texture.
+ * 
+ * @author Marcin Roguski (Kaelthas)
+ */
+public class CombinedTexture {
+	/** The data for each of the textures. */
+	private List<TextureData>	textureDatas	= new ArrayList<TextureData>();
+
+	/** The result texture. */
+	private Texture				resultTexture;
+	/** The UV values for the result texture. */
+	private List<Vector2f>		resultUVS;
+
+	/**
+	 * 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()));
+		}
+		TextureData textureData = new TextureData();
+		textureData.texture = texture;
+		textureData.textureBlender = textureBlender;
+		textureData.uvCoordinatesType = UVCoordinatesType.valueOf(uvCoordinatesType);
+		textureData.projectionType = UVProjectionType.valueOf(projectionType);
+		textureData.textureStructure = textureStructure;
+
+		if (this.isWithoutAlpha(textureData, blenderContext)) {
+			textureDatas.clear();// clear previous textures, they will be
+									// covered anyway
+		}
+		textureDatas.add(textureData);
+	}
+
+	/**
+	 * 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 (userDefinedUVCoordinates == null || userDefinedUVCoordinates.size() == 0) {
+						List<Geometry> geometries = (List<Geometry>) blenderContext.getLoadedFeature(geometriesOMA, LoadedFeatureDataType.LOADED_FEATURE);
+						resultUVS = UVCoordinatesGenerator.generateUVCoordinatesFor2DTexture(mesh, textureData.uvCoordinatesType, textureData.projectionType, geometries);
+					} else {
+						resultUVS = userDefinedUVCoordinates;
+					}
+				}
+				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) {
+							List<Geometry> geometries = (List<Geometry>) blenderContext.getLoadedFeature(geometriesOMA, LoadedFeatureDataType.LOADED_FEATURE);
+							textureUVS = UVCoordinatesGenerator.generateUVCoordinatesFor2DTexture(mesh, textureData.uvCoordinatesType, textureData.projectionType, geometries);
+						} else {
+							textureUVS = userDefinedUVCoordinates;
+						}
+						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) {
+			resultUVS = ((TriangulatedTexture) resultTexture).getResultUVS();
+			resultTexture = ((TriangulatedTexture) resultTexture).getResultTexture();
+		}
+
+		// 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!");
+		}
+	}
+
+	/**
+	 * @return the result texture
+	 */
+	public Texture getResultTexture() {
+		return resultTexture;
+	}
+
+	/**
+	 * @return the result UV coordinates
+	 */
+	public List<Vector2f> getResultUVS() {
+		return resultUVS;
+	}
+
+	/**
+	 * 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) {
+		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();
+
+		for (int x = 0; x < sourceImage.getWidth(); ++x) {
+			for (int y = 0; y < sourceImage.getHeight(); ++y) {
+				sourceIO.read(sourceImage, sourcePixel, x, y);
+				targetIO.read(targetImage, targetPixel, x, y);
+				targetPixel.merge(sourcePixel);
+				targetIO.write(targetImage, 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();
+						for (int x = 0; x < image.getWidth(); ++x) {
+							for (int y = 0; y < image.getHeight(); ++y) {
+								pixelInputOutput.read(image, 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;
+	}
+}

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

@@ -0,0 +1,131 @@
+package com.jme3.scene.plugins.blender.textures;
+
+import com.jme3.math.FastMath;
+
+/**
+ * The data that helps in bytes calculations for the result image.
+ * 
+ * @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					pixelWidth;
+	/** The height of the image in pixels. */
+	private int					pixelHeight;
+	/** The total texel count. */
+	private int					xTexelCount;
+
+	/**
+	 * Constructor. Allocates the required memory. Initializes variables.
+	 * 
+	 * @param textelsCount
+	 *            the total count of the texels
+	 * @param pixelWidth
+	 *            the width of the image in pixels
+	 * @param pixelHeight
+	 *            the height of the image in pixels
+	 * @param isAlpha
+	 *            indicates if the memory for alpha values should be
+	 *            allocated
+	 */
+	public DDSTexelData(int textelsCount, int pixelWidth, int pixelHeight, boolean isAlpha) {
+		textelsCount = pixelWidth * pixelHeight >> 4;
+		this.colors = new TexturePixel[textelsCount][];
+		this.indexes = new long[textelsCount];
+		this.xTexelCount = pixelWidth >> 2;
+		this.yCounter = (pixelHeight >> 2) - 1;// xCounter is 0 for now
+		this.pixelHeight = pixelHeight;
+		this.pixelWidth = pixelWidth;
+		if (isAlpha) {
+			this.alphas = new float[textelsCount][];
+			this.alphaIndexes = new long[textelsCount];
+		}
+	}
+
+	/**
+	 * 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 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
+	 */
+	public void getRGBA8(int x, int y, byte[] result) {
+		int xTexetlIndex = x % pixelWidth / 4;
+		int yTexelIndex = y % pixelHeight / 4;
+
+		int texelIndex = yTexelIndex * xTexelCount + xTexetlIndex;
+		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)
+
+		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);
+	}
+}

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

@@ -0,0 +1,151 @@
+package com.jme3.scene.plugins.blender.textures;
+
+import java.util.Comparator;
+import java.util.List;
+import java.util.Set;
+import java.util.TreeSet;
+
+import com.jme3.bounding.BoundingBox;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Mesh;
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.BlenderContext.LoadedFeatureDataType;
+import com.jme3.scene.plugins.blender.file.Structure;
+import com.jme3.scene.plugins.blender.textures.TriangulatedTexture.TriangleTextureElement;
+import com.jme3.scene.plugins.blender.textures.UVCoordinatesGenerator.UVCoordinatesType;
+import com.jme3.scene.plugins.blender.textures.generating.TextureGenerator;
+import com.jme3.texture.Image;
+import com.jme3.texture.Texture;
+
+/**
+ * The generated texture loaded from blender file. The texture is not generated
+ * after being read. This class rather stores all required data and can compute
+ * a pixel in the required 3D space position.
+ * 
+ * @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;
+
+	/** 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()));
+	}
+
+	/**
+	 * 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);
+
+		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>() {
+			@Override
+			public int compare(TriangleTextureElement o1, TriangleTextureElement o2) {
+				return o1.faceIndex - o2.faceIndex;
+			}
+		});
+		for (int i = 0; i < mesh.getTriangleCount(); ++i) {
+			triangleTextureElements.add(new TriangleTextureElement(i, boundingBox, this, uvsArray, blenderContext));
+		}
+		return new TriangulatedTexture(triangleTextureElements, blenderContext);
+	}
+
+	@Override
+	public void setWrap(WrapAxis axis, WrapMode mode) {
+	}
+
+	@Override
+	public void setWrap(WrapMode mode) {
+	}
+
+	@Override
+	public WrapMode getWrap(WrapAxis axis) {
+		return null;
+	}
+
+	@Override
+	public Type getType() {
+		return Type.ThreeDimensional;
+	}
+
+	@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;
+		}
+	}
+}

+ 0 - 416
engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGenerator.java

@@ -1,416 +0,0 @@
-/*
- * Copyright (c) 2009-2010 jMonkeyEngine
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * * Redistributions of source code must retain the above copyright
- *   notice, this list of conditions and the following disclaimer.
- *
- * * Redistributions in binary form must reproduce the above copyright
- *   notice, this list of conditions and the following disclaimer in the
- *   documentation and/or other materials provided with the distribution.
- *
- * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
- *   may be used to endorse or promote products derived from this software
- *   without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
- * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-package com.jme3.scene.plugins.blender.textures;
-
-import com.jme3.math.FastMath;
-import com.jme3.scene.plugins.blender.BlenderContext;
-import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
-import com.jme3.scene.plugins.blender.file.DynamicArray;
-import com.jme3.scene.plugins.blender.file.Pointer;
-import com.jme3.scene.plugins.blender.file.Structure;
-import com.jme3.texture.Texture;
-import java.util.Map;
-import java.util.TreeMap;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-/**
- * This class is a base class for texture generators.
- * @author Marcin Roguski (Kaelthas)
- */
-/* package */abstract class TextureGenerator {
-	private static final Logger	LOGGER	= Logger.getLogger(TextureGenerator.class.getName());
-
-	protected NoiseGenerator	noiseGenerator;
-	
-	public TextureGenerator(NoiseGenerator noiseGenerator) {
-		this.noiseGenerator = noiseGenerator;
-	}
-
-	/**
-	 * This method generates the texture.
-	 * @param tex
-	 *        texture's structure
-	 * @param width
-	 *        the width of the result texture
-	 * @param height
-	 *        the height of the result texture
-	 * @param depth
-	 *        the depth of the texture
-	 * @param blenderContext
-	 *        the blender context
-	 * @return newly generated texture
-	 */
-	protected abstract Texture generate(Structure tex, int width, int height, int depth, BlenderContext blenderContext);
-
-	/**
-	 * This method reads the colorband data from the given texture structure.
-	 * 
-	 * @param tex
-	 *        the texture structure
-	 * @param blenderContext
-	 *        the blender context
-	 * @return read colorband or null if not present
-	 */
-	private ColorBand readColorband(Structure tex, BlenderContext blenderContext) {
-		ColorBand result = null;
-		int flag = ((Number) tex.getFieldValue("flag")).intValue();
-		if ((flag & NoiseGenerator.TEX_COLORBAND) != 0) {
-			Pointer pColorband = (Pointer) tex.getFieldValue("coba");
-			Structure colorbandStructure;
-			try {
-				colorbandStructure = pColorband.fetchData(blenderContext.getInputStream()).get(0);
-				result = new ColorBand(colorbandStructure);
-			} catch (BlenderFileException e) {
-				LOGGER.log(Level.WARNING, "Cannot fetch the colorband structure. The reason: {0}", e.getLocalizedMessage());
-			}
-		}
-		return result;
-	}
-	
-	protected float[][] computeColorband(Structure tex, BlenderContext blenderContext) {
-		ColorBand colorBand = this.readColorband(tex, blenderContext);
-		float[][] result = null;
-		if(colorBand!=null) {
-			result = new float[1001][4];//1001 - amount of possible cursor positions; 4 = [r, g, b, a]
-			ColorBandData[] dataArray = colorBand.data;
-			
-			if(dataArray.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] = dataArray[0].r;
-					result[i][1] = dataArray[0].g;
-					result[i][2] = dataArray[0].b;
-					result[i][3] = dataArray[0].a;
-				}
-			} else {
-				int currentCursor = 0;
-				ColorBandData currentData = dataArray[0];
-				ColorBandData nextData = dataArray[0];
-				switch(colorBand.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 = dataArray[currentCursor++];
-								if(currentCursor < dataArray.length) {
-									nextData = dataArray[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<colorBand.data.length;++i) {
-							cbDataMap.put(Integer.valueOf(i), colorBand.data[i]);
-						}
-						
-						if(colorBand.data[0].pos==0) {
-							cbDataMap.put(Integer.valueOf(-1), colorBand.data[0]);
-						} else {
-							ColorBandData cbData = colorBand.data[0].clone();
-							cbData.pos = 0;
-							cbDataMap.put(Integer.valueOf(-1), cbData);
-							cbDataMap.put(Integer.valueOf(-2), cbData);
-						}
-						
-						if(colorBand.data[colorBand.data.length - 1].pos==1000) {
-							cbDataMap.put(Integer.valueOf(colorBand.data.length), colorBand.data[colorBand.data.length - 1]);
-						} else {
-							ColorBandData cbData = colorBand.data[colorBand.data.length - 1].clone();
-							cbData.pos = 1000;
-							cbDataMap.put(Integer.valueOf(colorBand.data.length), cbData);
-							cbDataMap.put(Integer.valueOf(colorBand.data.length + 1), cbData);
-						}
-						
-						float[] ipoFactors = new float[4];
-						float f;
-						
-						ColorBandData data0 = cbDataMap.get(currentCursor - 2);
-						ColorBandData data1 = cbDataMap.get(currentCursor - 1);
-						ColorBandData data2 = cbDataMap.get(currentCursor);
-						ColorBandData data3 = cbDataMap.get(currentCursor + 1);
-						
-						for(int i=0;i<result.length;++i) {
-							if (data2.pos != data1.pos) {
-		                        f = (i - data2.pos) / (float)(data1.pos - data2.pos);
-		                    } else {
-		                        f = 0.0f;
-		                    }
-							
-							f = FastMath.clamp(f, 0.0f, 1.0f);
-							
-							this.getIpoData(colorBand, 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;
-							}
-							
-							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 = dataArray[currentCursor++];
-								if(currentCursor < dataArray.length) {
-									nextData = dataArray[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 = dataArray[currentCursor++];
-								if(currentCursor < dataArray.length) {
-									nextData = dataArray[currentCursor];
-								}
-							}
-						}
-						break;
-					default:
-						throw new IllegalStateException("Unknown interpolation type: " + colorBand.ipoType);
-				}
-			}
-		}
-		return result;
-	}
-	
-	/**
-	 * This method returns the data for either B-spline of Cardinal interpolation.
-	 * @param colorBand the color band
-	 * @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(ColorBand colorBand, float d, float[] ipoFactors) {
-		float d2 = d * d;
-		float d3 = d2 * d;
-		if(colorBand.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(colorBand.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 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;
-        if (texres.green < 0.0f) {
-            texres.green = 0.0f;
-        }
-        texres.blue = (texres.blue - 0.5f) * bacd.contrast + bacd.brightness;
-        if (texres.blue < 0.0f) {
-            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) {
-        texres.intensity = (texres.intensity - 0.5f) * contrast + brightness;
-        if (texres.intensity < 0.0f) {
-            texres.intensity = 0.0f;
-        } else if (texres.intensity > 1.0f) {
-            texres.intensity = 1.0f;
-        }
-    }
-	
-	/**
-	 * A class constaining the colorband data.
-	 * 
-	 * @author Marcin Roguski (Kaelthas)
-	 */
-	protected static class ColorBand {
-		//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;
-
-		public int		cursorsAmount, ipoType;
-		public ColorBandData[]	data;
-
-		/**
-		 * Constructor. Loads the data from the given structure.
-		 * 
-		 * @param cbdataStructure
-		 *        the colorband structure
-		 */
-		@SuppressWarnings("unchecked")
-		public ColorBand(Structure colorbandStructure) {
-			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));
-			}
-		}
-	}
-
-	/**
-	 * Class to store the single colorband cursor data.
-	 * 
-	 * @author Marcin Roguski (Kaelthas)
-	 */
-	protected static class ColorBandData implements Cloneable {
-		public final float	r, g, b, a;
-		public int 	pos;
-
-		/**
-		 * 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 String toString() {
-			return "P: " + this.pos + " [" + this.r+", "+this.g+", "+this.b+", "+this.a+"]";
-		}
-	}
-	
-	/**
-	 * 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
-         */
-		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();
-		}
-	}
-}

+ 0 - 131
engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorClouds.java

@@ -1,131 +0,0 @@
-/*
- * Copyright (c) 2009-2010 jMonkeyEngine
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * * Redistributions of source code must retain the above copyright
- *   notice, this list of conditions and the following disclaimer.
- *
- * * Redistributions in binary form must reproduce the above copyright
- *   notice, this list of conditions and the following disclaimer in the
- *   documentation and/or other materials provided with the distribution.
- *
- * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
- *   may be used to endorse or promote products derived from this software
- *   without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
- * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-package com.jme3.scene.plugins.blender.textures;
-
-import com.jme3.math.FastMath;
-import com.jme3.scene.plugins.blender.BlenderContext;
-import com.jme3.scene.plugins.blender.file.Structure;
-import com.jme3.texture.Image;
-import com.jme3.texture.Image.Format;
-import com.jme3.texture.Texture;
-import com.jme3.texture.Texture3D;
-import com.jme3.util.BufferUtils;
-import java.nio.ByteBuffer;
-import java.util.ArrayList;
-
-/**
- * This class generates the 'clouds' texture.
- * @author Marcin Roguski (Kaelthas)
- */
-public class TextureGeneratorClouds extends TextureGenerator {
-	// tex->noisetype
-    protected static final int TEX_NOISESOFT = 0;
-    protected static final int TEX_NOISEPERL = 1;
-    
-    // tex->stype
-    protected static final int TEX_DEFAULT = 0;
-    protected static final int TEX_COLOR = 1;
-    
-	/**
-	 * Constructor stores the given noise generator.
-	 * @param noiseGenerator
-	 *        the noise generator
-	 */
-	public TextureGeneratorClouds(NoiseGenerator noiseGenerator) {
-		super(noiseGenerator);
-	}
-
-	@Override
-	protected Texture generate(Structure tex, int width, int height, int depth, BlenderContext blenderContext) {
-		float[] texvec = new float[] { 0, 0, 0 };
-		TexturePixel texres = new TexturePixel();
-
-		// reading the data from the texture structure
-		float noisesize = ((Number) tex.getFieldValue("noisesize")).floatValue();
-		int noiseDepth = ((Number) tex.getFieldValue("noisedepth")).intValue();
-		int noiseBasis = ((Number) tex.getFieldValue("noisebasis")).intValue();
-		int noiseType = ((Number) tex.getFieldValue("noisetype")).intValue();
-		boolean isHard = noiseType != TEX_NOISESOFT;
-		int sType = ((Number) tex.getFieldValue("stype")).intValue();
-		int halfW = width >> 1, halfH = height >> 1, halfD = depth >> 1, index = 0;
-		float wDelta = 1.0f / halfW, hDelta = 1.0f / halfH, dDelta = 1.0f / halfD;
-		float[][] colorBand = this.computeColorband(tex, blenderContext);
-		Format format = sType == TEX_COLOR || colorBand != null ? Format.RGBA8 : Format.Luminance8;
-		int bytesPerPixel = sType == TEX_COLOR || colorBand != null ? 4 : 1;
-		BrightnessAndContrastData bacd = new BrightnessAndContrastData(tex);
-		
-		byte[] data = new byte[width * height * depth * bytesPerPixel];
-		for (int i = -halfW; i < halfW; ++i) {
-			texvec[0] = wDelta * i;
-			for (int j = -halfH; j < halfH; ++j) {
-				texvec[1] = hDelta * j;
-				for (int k = -halfD; k < halfD; ++k) {
-					texvec[2] = dDelta * k;
-					texres.intensity = NoiseGenerator.NoiseFunctions.turbulence(texvec[0], texvec[1], texvec[2], noisesize, noiseDepth, noiseBasis, isHard);
-					texres.intensity = FastMath.clamp(texres.intensity, 0.0f, 1.0f);
-					if (colorBand != null) {
-						int colorbandIndex = (int) (texres.intensity * 1000.0f);
-						texres.red = colorBand[colorbandIndex][0];
-						texres.green = colorBand[colorbandIndex][1];
-						texres.blue = colorBand[colorbandIndex][2];
-						
-						this.applyBrightnessAndContrast(bacd, texres);
-						data[index++] = (byte) (texres.red * 255.0f);
-						data[index++] = (byte) (texres.green * 255.0f);
-						data[index++] = (byte) (texres.blue * 255.0f);
-						data[index++] = (byte) (colorBand[colorbandIndex][3] * 255.0f);
-					} else if (sType == TEX_COLOR) {
-						texres.red = texres.intensity;
-						texres.green = NoiseGenerator.NoiseFunctions.turbulence(texvec[1], texvec[0], texvec[2], noisesize, noiseDepth, noiseBasis, isHard);
-						texres.blue = NoiseGenerator.NoiseFunctions.turbulence(texvec[1], texvec[2], texvec[0], noisesize, noiseDepth, noiseBasis, isHard);
-						
-						texres.green = FastMath.clamp(texres.green, 0.0f, 1.0f);
-						texres.blue = FastMath.clamp(texres.blue, 0.0f, 1.0f);
-						
-						this.applyBrightnessAndContrast(bacd, texres);
-						data[index++] = (byte) (texres.red * 255.0f);
-						data[index++] = (byte) (texres.green * 255.0f);
-						data[index++] = (byte) (texres.blue * 255.0f);
-						data[index++] = (byte) (255);//1.0f * 255.0f
-					} else {
-						this.applyBrightnessAndContrast(texres, bacd.contrast, bacd.brightness);
-						data[index++] = (byte) (texres.intensity * 255.0f);
-					}
-				}
-			}
-		}
-		
-		ArrayList<ByteBuffer> dataArray = new ArrayList<ByteBuffer>(1);
-		dataArray.add(BufferUtils.createByteBuffer(data));
-		return new Texture3D(new Image(format, width, height, depth, dataArray));
-	}
-}

+ 0 - 106
engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorNoise.java

@@ -1,106 +0,0 @@
-/*
- * Copyright (c) 2009-2010 jMonkeyEngine
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * * Redistributions of source code must retain the above copyright
- *   notice, this list of conditions and the following disclaimer.
- *
- * * Redistributions in binary form must reproduce the above copyright
- *   notice, this list of conditions and the following disclaimer in the
- *   documentation and/or other materials provided with the distribution.
- *
- * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
- *   may be used to endorse or promote products derived from this software
- *   without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
- * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-package com.jme3.scene.plugins.blender.textures;
-
-import com.jme3.math.FastMath;
-import com.jme3.scene.plugins.blender.BlenderContext;
-import com.jme3.scene.plugins.blender.file.Structure;
-import com.jme3.texture.Image;
-import com.jme3.texture.Image.Format;
-import com.jme3.texture.Texture;
-import com.jme3.texture.Texture3D;
-import com.jme3.util.BufferUtils;
-import java.nio.ByteBuffer;
-import java.util.ArrayList;
-
-/**
- * This class generates the 'noise' texture.
- * @author Marcin Roguski (Kaelthas)
- */
-public class TextureGeneratorNoise extends TextureGenerator {
-
-	/**
-	 * Constructor stores the given noise generator.
-	 * @param noiseGenerator
-	 *        the noise generator
-	 */
-	public TextureGeneratorNoise(NoiseGenerator noiseGenerator) {
-		super(noiseGenerator);
-	}
-
-	@Override
-	protected Texture generate(Structure tex, int width, int height, int depth, BlenderContext blenderContext) {
-		int val, random, loop;
-		int noisedepth = ((Number) tex.getFieldValue("noisedepth")).intValue();
-		TexturePixel texres = new TexturePixel();
-		int halfW = width >> 1, halfH = height >> 1, halfD = depth >> 1, index = 0;
-		float[][] colorBand = this.computeColorband(tex, blenderContext);
-		Format format = colorBand != null ? Format.RGBA8 : Format.Luminance8;
-		int bytesPerPixel = colorBand != null ? 4 : 1;
-		BrightnessAndContrastData bacd = new BrightnessAndContrastData(tex);
-		
-		byte[] data = new byte[width * height * depth * bytesPerPixel];
-		for (int i = -halfW; i < halfW; ++i) {
-			for (int j = -halfH; j < halfH; ++j) {
-				for (int k = -halfD; k < halfD; ++k) {
-					random = FastMath.rand.nextInt();
-					val = random & 3;
-
-					loop = noisedepth;
-					while (loop-- != 0) {
-						random >>= 2;
-						val *= random & 3;
-					}
-					texres.intensity = FastMath.clamp(val, 0.0f, 1.0f);
-					if (colorBand != null) {
-						int colorbandIndex = (int) (texres.intensity * 1000.0f);
-						texres.red = colorBand[colorbandIndex][0];
-						texres.green = colorBand[colorbandIndex][1];
-						texres.blue = colorBand[colorbandIndex][2];
-						
-						this.applyBrightnessAndContrast(bacd, texres);
-						data[index++] = (byte) (texres.red * 255.0f);
-						data[index++] = (byte) (texres.green * 255.0f);
-						data[index++] = (byte) (texres.blue * 255.0f);
-						data[index++] = (byte) (colorBand[colorbandIndex][3] * 255.0f);
-					} else {
-						this.applyBrightnessAndContrast(texres, bacd.contrast, bacd.brightness);
-						data[index++] = (byte) (texres.intensity * 255.0f);
-					}
-				}
-			}
-		}
-		ArrayList<ByteBuffer> dataArray = new ArrayList<ByteBuffer>(1);
-		dataArray.add(BufferUtils.createByteBuffer(data));
-		return new Texture3D(new Image(format, width, height, depth, dataArray));
-	}
-}

+ 0 - 125
engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorStucci.java

@@ -1,125 +0,0 @@
-/*
- * Copyright (c) 2009-2010 jMonkeyEngine
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * * Redistributions of source code must retain the above copyright
- *   notice, this list of conditions and the following disclaimer.
- *
- * * Redistributions in binary form must reproduce the above copyright
- *   notice, this list of conditions and the following disclaimer in the
- *   documentation and/or other materials provided with the distribution.
- *
- * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
- *   may be used to endorse or promote products derived from this software
- *   without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
- * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-package com.jme3.scene.plugins.blender.textures;
-
-import com.jme3.scene.plugins.blender.BlenderContext;
-import com.jme3.scene.plugins.blender.file.Structure;
-import com.jme3.texture.Image;
-import com.jme3.texture.Image.Format;
-import com.jme3.texture.Texture;
-import com.jme3.texture.Texture3D;
-import com.jme3.util.BufferUtils;
-import java.nio.ByteBuffer;
-import java.util.ArrayList;
-
-/**
- * This class generates the 'stucci' texture.
- * @author Marcin Roguski (Kaelthas)
- */
-public class TextureGeneratorStucci extends TextureGenerator {
-	protected static final int TEX_NOISESOFT = 0;
-	
-	/**
-	 * Constructor stores the given noise generator.
-	 * @param noiseGenerator
-	 *        the noise generator
-	 */
-	public TextureGeneratorStucci(NoiseGenerator noiseGenerator) {
-		super(noiseGenerator);
-	}
-
-	@Override
-	protected Texture generate(Structure tex, int width, int height, int depth, BlenderContext blenderContext) {
-		float noisesize = ((Number) tex.getFieldValue("noisesize")).floatValue();
-		int noisebasis = ((Number) tex.getFieldValue("noisebasis")).intValue();
-		int noisetype = ((Number) tex.getFieldValue("noisetype")).intValue();
-		float turbul = ((Number) tex.getFieldValue("turbul")).floatValue();
-		boolean isHard = noisetype != TEX_NOISESOFT;
-		int stype = ((Number) tex.getFieldValue("stype")).intValue();
-
-		if(noisesize<=0.001f) {//the texture goes black if this value is lower than 0.001f
-			noisesize = 0.001f;
-		}
-		
-		float[] texvec = new float[] { 0, 0, 0 };
-		TexturePixel texres = new TexturePixel();
-		int halfW = width >> 1, halfH = height >> 1, halfD = depth >> 1, index = 0;
-		float wDelta = 1.0f / halfW, hDelta = 1.0f / halfH, dDelta = 1.0f / halfD, noiseValue, ofs;;
-		float[][] colorBand = this.computeColorband(tex, blenderContext);
-		Format format = colorBand != null ? Format.RGBA8 : Format.Luminance8;
-		int bytesPerPixel = colorBand != null ? 4 : 1;
-
-		byte[] data = new byte[width * height * depth * bytesPerPixel];
-		for (int i = -halfW; i < halfW; ++i) {
-			texvec[0] = wDelta * i;
-			for (int j = -halfH; j < halfH; ++j) {
-				texvec[1] = hDelta * j;
-				for (int k = -halfD; k < halfD; ++k) {
-					texvec[2] = dDelta * k;
-					noiseValue = NoiseGenerator.NoiseFunctions.noise(texvec[0], texvec[1], texvec[2], noisesize, 0, noisebasis, isHard);
-					ofs = turbul / 200.0f;
-					if (stype != 0) {
-						ofs *= noiseValue * noiseValue;
-					}
-
-					texres.intensity = NoiseGenerator.NoiseFunctions.noise(texvec[0], texvec[1], texvec[2] + ofs, noisesize, 0, noisebasis, isHard);
-					if (colorBand != null) {
-						int colorbandIndex = (int) (texres.intensity * 1000.0f);
-						texres.red = colorBand[colorbandIndex][0];
-						texres.green = colorBand[colorbandIndex][1];
-						texres.blue = colorBand[colorbandIndex][2];
-						texres.alpha = colorBand[colorbandIndex][3];
-					}
-
-					if (stype == NoiseGenerator.TEX_WALLOUT) {
-						texres.intensity = 1.0f - texres.intensity;
-					}
-					if (texres.intensity < 0.0f) {
-						texres.intensity = 0.0f;
-					}
-					//no brightness and contrast needed for stucci (it doesn't affect the texture)
-					if (colorBand != null) {
-						data[index++] = (byte) (texres.red * 255.0f);
-						data[index++] = (byte) (texres.green * 255.0f);
-						data[index++] = (byte) (texres.blue * 255.0f);
-						data[index++] = (byte) (texres.alpha * 255.0f);
-					} else {
-						data[index++] = (byte) (texres.intensity * 255.0f);
-					}
-				}
-			}
-		}
-		ArrayList<ByteBuffer> dataArray = new ArrayList<ByteBuffer>(1);
-		dataArray.add(BufferUtils.createByteBuffer(data));
-		return new Texture3D(new Image(format, width, height, depth, dataArray));
-	}
-}

+ 0 - 171
engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorVoronoi.java

@@ -1,171 +0,0 @@
-/*
- * Copyright (c) 2009-2010 jMonkeyEngine
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * * Redistributions of source code must retain the above copyright
- *   notice, this list of conditions and the following disclaimer.
- *
- * * Redistributions in binary form must reproduce the above copyright
- *   notice, this list of conditions and the following disclaimer in the
- *   documentation and/or other materials provided with the distribution.
- *
- * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
- *   may be used to endorse or promote products derived from this software
- *   without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
- * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-package com.jme3.scene.plugins.blender.textures;
-
-import com.jme3.math.FastMath;
-import com.jme3.scene.plugins.blender.BlenderContext;
-import com.jme3.scene.plugins.blender.file.Structure;
-import com.jme3.scene.plugins.blender.textures.NoiseGenerator.NoiseMath;
-import com.jme3.texture.Image;
-import com.jme3.texture.Image.Format;
-import com.jme3.texture.Texture;
-import com.jme3.texture.Texture3D;
-import com.jme3.util.BufferUtils;
-import java.nio.ByteBuffer;
-import java.util.ArrayList;
-
-/**
- * This class generates the 'voronoi' texture.
- * @author Marcin Roguski (Kaelthas)
- */
-public class TextureGeneratorVoronoi extends TextureGenerator {
-
-	/**
-	 * Constructor stores the given noise generator.
-	 * @param noiseGenerator
-	 *        the noise generator
-	 */
-	public TextureGeneratorVoronoi(NoiseGenerator noiseGenerator) {
-		super(noiseGenerator);
-	}
-
-	@Override
-	protected Texture generate(Structure tex, int width, int height, int depth, BlenderContext blenderContext) {
-		float voronoiWeight1 = ((Number) tex.getFieldValue("vn_w1")).floatValue();
-		float voronoiWeight2 = ((Number) tex.getFieldValue("vn_w2")).floatValue();
-		float voronoiWeight3 = ((Number) tex.getFieldValue("vn_w3")).floatValue();
-		float voronoiWeight4 = ((Number) tex.getFieldValue("vn_w4")).floatValue();
-		float noisesize = ((Number) tex.getFieldValue("noisesize")).floatValue();
-		float outscale = ((Number) tex.getFieldValue("ns_outscale")).floatValue();
-		float mexp = ((Number) tex.getFieldValue("vn_mexp")).floatValue();
-		int distm = ((Number) tex.getFieldValue("vn_distm")).intValue();
-		int voronoiColorType = ((Number) tex.getFieldValue("vn_coltype")).intValue();
-
-		TexturePixel texres = new TexturePixel();
-		float[] texvec = new float[] { 0, 0, 0 };
-		int halfW = width >> 1, halfH = height >> 1, halfD = depth >> 1, index = 0;
-		float wDelta = 1.0f / halfW, hDelta = 1.0f / halfH, dDelta = 1.0f / halfD;
-		
-		float[][] colorBand = this.computeColorband(tex, blenderContext);
-		Format format = voronoiColorType != 0 || colorBand != null ? Format.RGBA8 : Format.Luminance8;
-		int bytesPerPixel = voronoiColorType != 0 || colorBand != null ? 4 : 1;
-		BrightnessAndContrastData bacd = new BrightnessAndContrastData(tex);
-		
-		float[] da = new float[4], pa = new float[12];
-		float[] hashPoint = voronoiColorType != 0 ? new float[3] : null;
-		float[] voronoiWeights = new float[] {FastMath.abs(voronoiWeight1), FastMath.abs(voronoiWeight2), 
-											  FastMath.abs(voronoiWeight3), FastMath.abs(voronoiWeight4)};
-		float weight;
-		float sc = voronoiWeights[0] + voronoiWeights[1] + voronoiWeights[2] + voronoiWeights[3];
-		if (sc != 0.0f) {
-			sc = outscale / sc;
-		}
-
-		byte[] data = new byte[width * height * depth * bytesPerPixel];
-		for (int i = -halfW; i < halfW; ++i) {
-			texvec[0] = wDelta * i / noisesize;
-			for (int j = -halfH; j < halfH; ++j) {
-				texvec[1] = hDelta * j / noisesize;
-				for (int k = -halfD; k < halfD; ++k) {
-					texvec[2] = dDelta * k;
-					NoiseGenerator.NoiseFunctions.voronoi(texvec[0], texvec[1], texvec[2], da, pa, mexp, distm);
-					texres.intensity = sc * FastMath.abs(voronoiWeight1 * da[0] + voronoiWeight2 * da[1] + voronoiWeight3 * da[2] + voronoiWeight4 * da[3]);
-					if(texres.intensity>1.0f) {
-						texres.intensity = 1.0f;
-					} else if(texres.intensity<0.0f) {
-						texres.intensity = 0.0f;
-					}
-					
-					if (colorBand != null) {//colorband ALWAYS goes first and covers the color (if set)
-						int colorbandIndex = (int) (texres.intensity * 1000.0f);
-						texres.red = colorBand[colorbandIndex][0];
-						texres.green = colorBand[colorbandIndex][1];
-						texres.blue = colorBand[colorbandIndex][2];
-						texres.alpha = colorBand[colorbandIndex][3];
-					} else if (voronoiColorType != 0) {
-						texres.red = texres.green = texres.blue = 0.0f;
-						texres.alpha = 1.0f;
-						for(int m=0; m<12; m+=3) {
-							weight = voronoiWeights[m/3];
-							this.cellNoiseV(pa[m], pa[m + 1], pa[m + 2], hashPoint);
-							texres.red += weight * hashPoint[0];
-							texres.green += weight * hashPoint[1];
-							texres.blue += weight * hashPoint[2];
-						}
-						if (voronoiColorType >= 2) {
-							float t1 = (da[1] - da[0]) * 10.0f;
-							if (t1 > 1.0f) {
-								t1 = 1.0f;
-							}
-							if (voronoiColorType == 3) {
-								t1 *= texres.intensity;
-							} else {
-								t1 *= sc;
-							}
-							texres.red *= t1;
-							texres.green *= t1;
-							texres.blue *= t1;
-						} else {
-							texres.red *= sc;
-							texres.green *= sc;
-							texres.blue *= sc;
-						}
-					}
-
-					if (voronoiColorType != 0 || colorBand != null) {
-						this.applyBrightnessAndContrast(bacd, texres);
-						data[index++] = (byte) (texres.red * 255.0f);
-						data[index++] = (byte) (texres.green * 255.0f);
-						data[index++] = (byte) (texres.blue * 255.0f);
-						data[index++] = (byte) (texres.alpha * 255.0f);
-					} else {
-						this.applyBrightnessAndContrast(texres, bacd.contrast, bacd.brightness);
-						data[index++] = (byte) (texres.intensity * 255.0f);
-					}
-				}
-			}
-		}
-		ArrayList<ByteBuffer> dataArray = new ArrayList<ByteBuffer>(1);
-		dataArray.add(BufferUtils.createByteBuffer(data));
-		return new Texture3D(new Image(format, width, height, depth, dataArray));
-	}
-	
-	/**
-     * Returns a vector/point/color in ca, using point hasharray directly
-     */
-    private void cellNoiseV(float x, float y, float z, float[] hashPoint) {
-        int xi = (int) Math.floor(x);
-        int yi = (int) Math.floor(y);
-        int zi = (int) Math.floor(z);
-        NoiseMath.hash(xi, yi, zi, hashPoint);
-    }
-}

+ 327 - 218
engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureHelper.java

@@ -36,13 +36,12 @@ import java.awt.image.BufferedImage;
 import java.awt.image.ColorConvertOp;
 import java.nio.ByteBuffer;
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
 import jme3tools.converters.ImageToAwt;
+import jme3tools.converters.RGB565;
 
 import com.jme3.asset.AssetManager;
 import com.jme3.asset.AssetNotFoundException;
@@ -50,7 +49,6 @@ import com.jme3.asset.BlenderKey;
 import com.jme3.asset.BlenderKey.FeaturesToLoad;
 import com.jme3.asset.GeneratedTextureKey;
 import com.jme3.asset.TextureKey;
-import com.jme3.math.ColorRGBA;
 import com.jme3.math.Vector3f;
 import com.jme3.scene.plugins.blender.AbstractBlenderHelper;
 import com.jme3.scene.plugins.blender.BlenderContext;
@@ -59,14 +57,15 @@ import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
 import com.jme3.scene.plugins.blender.file.FileBlockHeader;
 import com.jme3.scene.plugins.blender.file.Pointer;
 import com.jme3.scene.plugins.blender.file.Structure;
-import com.jme3.scene.plugins.blender.materials.MaterialContext;
+import com.jme3.scene.plugins.blender.textures.generating.TextureGeneratorFactory;
+import com.jme3.scene.plugins.blender.textures.io.PixelIOFactory;
+import com.jme3.scene.plugins.blender.textures.io.PixelInputOutput;
 import com.jme3.texture.Image;
 import com.jme3.texture.Image.Format;
 import com.jme3.texture.Texture;
 import com.jme3.texture.Texture.MinFilter;
 import com.jme3.texture.Texture.WrapMode;
 import com.jme3.texture.Texture2D;
-import com.jme3.texture.Texture3D;
 import com.jme3.util.BufferUtils;
 
 /**
@@ -75,228 +74,120 @@ import com.jme3.util.BufferUtils;
  * @author Marcin Roguski
  */
 public class TextureHelper extends AbstractBlenderHelper {
-	private static final Logger	LOGGER				= Logger.getLogger(TextureHelper.class.getName());
+	private static final Logger		LOGGER				= Logger.getLogger(TextureHelper.class.getName());
 
 	// texture types
-	public static final int		TEX_NONE			= 0;
-	public static final int		TEX_CLOUDS			= 1;
-	public static final int		TEX_WOOD			= 2;
-	public static final int		TEX_MARBLE			= 3;
-	public static final int		TEX_MAGIC			= 4;
-	public static final int		TEX_BLEND			= 5;
-	public static final int		TEX_STUCCI			= 6;
-	public static final int		TEX_NOISE			= 7;
-	public static final int		TEX_IMAGE			= 8;
-	public static final int		TEX_PLUGIN			= 9;
-	public static final int		TEX_ENVMAP			= 10;
-	public static final int		TEX_MUSGRAVE		= 11;
-	public static final int		TEX_VORONOI			= 12;
-	public static final int		TEX_DISTNOISE		= 13;
-	public static final int 	TEX_POINTDENSITY 	= 14;//v. 25+
-	public static final int 	TEX_VOXELDATA 		= 15;//v. 25+
-
-	// mapto
-	public static final int		MAP_COL				= 1;
-	public static final int		MAP_NORM			= 2;
-	public static final int		MAP_COLSPEC			= 4;
-	public static final int		MAP_COLMIR			= 8;
-	public static final int		MAP_VARS			= 0xFFF0;
-	public static final int		MAP_REF				= 16;
-	public static final int		MAP_SPEC			= 32;
-	public static final int		MAP_EMIT			= 64;
-	public static final int		MAP_ALPHA			= 128;
-	public static final int		MAP_HAR				= 256;
-	public static final int		MAP_RAYMIRR			= 512;
-	public static final int		MAP_TRANSLU			= 1024;
-	public static final int		MAP_AMB				= 2048;
-	public static final int		MAP_DISPLACE		= 4096;
-	public static final int		MAP_WARP			= 8192;
-	public static final int		MAP_LAYER			= 16384;
-
-	protected NoiseGenerator noiseGenerator;
-	private Map<Integer, TextureGenerator> textureGenerators = new HashMap<Integer, TextureGenerator>();
+	public static final int			TEX_NONE			= 0;
+	public static final int			TEX_CLOUDS			= 1;
+	public static final int			TEX_WOOD			= 2;
+	public static final int			TEX_MARBLE			= 3;
+	public static final int			TEX_MAGIC			= 4;
+	public static final int			TEX_BLEND			= 5;
+	public static final int			TEX_STUCCI			= 6;
+	public static final int			TEX_NOISE			= 7;
+	public static final int			TEX_IMAGE			= 8;
+	public static final int			TEX_PLUGIN			= 9;
+	public static final int			TEX_ENVMAP			= 10;
+	public static final int			TEX_MUSGRAVE		= 11;
+	public static final int			TEX_VORONOI			= 12;
+	public static final int			TEX_DISTNOISE		= 13;
+	public static final int			TEX_POINTDENSITY	= 14;												// v.
+																											// 25+
+	public static final int			TEX_VOXELDATA		= 15;												// v.
+																											// 25+
+
+	private TextureGeneratorFactory	textureGeneratorFactory;
 
 	/**
 	 * This constructor parses the given blender version and stores the result.
 	 * It creates noise generator and texture generators.
 	 * 
 	 * @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 TextureHelper(String blenderVersion, boolean fixUpAxis) {
 		super(blenderVersion, false);
-		noiseGenerator = new NoiseGenerator(blenderVersion);
-		textureGenerators.put(Integer.valueOf(TEX_BLEND), new TextureGeneratorBlend(noiseGenerator));
-		textureGenerators.put(Integer.valueOf(TEX_CLOUDS), new TextureGeneratorClouds(noiseGenerator));
-		textureGenerators.put(Integer.valueOf(TEX_DISTNOISE), new TextureGeneratorDistnoise(noiseGenerator));
-		textureGenerators.put(Integer.valueOf(TEX_MAGIC), new TextureGeneratorMagic(noiseGenerator));
-		textureGenerators.put(Integer.valueOf(TEX_MARBLE), new TextureGeneratorMarble(noiseGenerator));
-		textureGenerators.put(Integer.valueOf(TEX_MUSGRAVE), new TextureGeneratorMusgrave(noiseGenerator));
-		textureGenerators.put(Integer.valueOf(TEX_NOISE), new TextureGeneratorNoise(noiseGenerator));
-		textureGenerators.put(Integer.valueOf(TEX_STUCCI), new TextureGeneratorStucci(noiseGenerator));
-		textureGenerators.put(Integer.valueOf(TEX_VORONOI), new TextureGeneratorVoronoi(noiseGenerator));
-		textureGenerators.put(Integer.valueOf(TEX_WOOD), new TextureGeneratorWood(noiseGenerator));
+		textureGeneratorFactory = new TextureGeneratorFactory(blenderVersion);
 	}
 
 	/**
-	 * This class returns a texture read from the file or from packed blender data. The returned texture has the name set to the value of
-	 * its blender type.
+	 * This class returns a texture read from the file or from packed blender
+	 * data. The returned texture has the name set to the value of its blender
+	 * type.
 	 * 
 	 * @param tex
-	 *        texture structure filled with data
+	 *            texture structure filled with data
 	 * @param blenderContext
-	 *        the blender context
+	 *            the blender context
 	 * @return the texture that can be used by JME engine
 	 * @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 Texture getTexture(Structure tex, BlenderContext blenderContext) throws BlenderFileException {
+	public Texture getTexture(Structure tex, Structure mTex, BlenderContext blenderContext) throws BlenderFileException {
 		Texture result = (Texture) blenderContext.getLoadedFeature(tex.getOldMemoryAddress(), LoadedFeatureDataType.LOADED_FEATURE);
 		if (result != null) {
 			return result;
 		}
 		int type = ((Number) tex.getFieldValue("type")).intValue();
-		int width = blenderContext.getBlenderKey().getGeneratedTextureWidth();
-		int height = blenderContext.getBlenderKey().getGeneratedTextureHeight();
-		int depth = blenderContext.getBlenderKey().getGeneratedTextureDepth();
 
 		switch (type) {
-		case TEX_IMAGE:// (it is first because probably this will be most commonly used)
-			Pointer pImage = (Pointer) tex.getFieldValue("ima");
-			if (pImage.isNotNull()){
-				Structure image = pImage.fetchData(blenderContext.getInputStream()).get(0);
-				result = this.getTextureFromImage(image, blenderContext);
-			}
-			break;
-		case TEX_CLOUDS:
-		case TEX_WOOD:
-		case TEX_MARBLE:
-		case TEX_MAGIC:
-		case TEX_BLEND:
-		case TEX_STUCCI:
-		case TEX_NOISE:
-		case TEX_MUSGRAVE:
-		case TEX_VORONOI:
-		case TEX_DISTNOISE:
-			TextureGenerator textureGenerator = textureGenerators.get(Integer.valueOf(type));
-			result = textureGenerator.generate(tex, width, height, depth, blenderContext);
-			break;
-		case TEX_NONE:// No texture, do nothing
-			break;
-		case TEX_POINTDENSITY:
-			LOGGER.warning("Point density texture loading currently not supported!");
-			break;
-		case TEX_VOXELDATA:
-			LOGGER.warning("Voxel data texture loading currently not supported!");
-			break;
-		case TEX_PLUGIN:
-		case TEX_ENVMAP:// TODO: implement envmap texture
-			LOGGER.log(Level.WARNING, "Unsupported texture type: {0} for texture: {1}", new Object[]{type, tex.getName()});
-			break;
-		default:
-			throw new BlenderFileException("Unknown texture type: " + type + " for texture: " + tex.getName());
+			case TEX_IMAGE:// (it is first because probably this will be most commonly used)
+				Pointer pImage = (Pointer) tex.getFieldValue("ima");
+				if (pImage.isNotNull()) {
+					Structure image = pImage.fetchData(blenderContext.getInputStream()).get(0);
+					result = this.getTextureFromImage(image, blenderContext);
+					this.applyColorbandAndColorFactors(tex, result.getImage(), blenderContext);
+				}
+				break;
+			case TEX_CLOUDS:
+			case TEX_WOOD:
+			case TEX_MARBLE:
+			case TEX_MAGIC:
+			case TEX_BLEND:
+			case TEX_STUCCI:
+			case TEX_NOISE:
+			case TEX_MUSGRAVE:
+			case TEX_VORONOI:
+			case TEX_DISTNOISE:
+				result = new GeneratedTexture(tex, mTex, textureGeneratorFactory.createTextureGenerator(type), blenderContext);
+				break;
+			case TEX_NONE:// No texture, do nothing
+				break;
+			case TEX_POINTDENSITY:
+				LOGGER.warning("Point density texture loading currently not supported!");
+				break;
+			case TEX_VOXELDATA:
+				LOGGER.warning("Voxel data texture loading currently not supported!");
+				break;
+			case TEX_PLUGIN:
+			case TEX_ENVMAP:
+				LOGGER.log(Level.WARNING, "Unsupported texture type: {0} for texture: {1}", new Object[] { type, tex.getName() });
+				break;
+			default:
+				throw new BlenderFileException("Unknown texture type: " + type + " for texture: " + tex.getName());
 		}
 		if (result != null) {
 			result.setName(tex.getName());
 			result.setWrap(WrapMode.Repeat);
 			// NOTE: Enable mipmaps FOR ALL TEXTURES EVER
 			result.setMinFilter(MinFilter.Trilinear);
-			if(type != TEX_IMAGE) {//only generated textures should have this key
+			if (type != TEX_IMAGE) {// only generated textures should have this key
 				result.setKey(new GeneratedTextureKey(tex.getName()));
 			}
 		}
 		return result;
 	}
 
-	/**
-	 * This method merges the given textures. The result texture has no alpha
-	 * factor (is always opaque).
-	 * 
-	 * @param sources
-	 *            the textures to be merged
-	 * @param materialContext
-	 *            the context of the material
-	 * @return merged textures
-	 */
-	public Texture mergeTextures(List<Texture> sources, MaterialContext materialContext) {
-		Texture result = null;
-		if(sources!=null && sources.size()>0) {
-			if(sources.size() == 1) {
-				return sources.get(0);//just return the texture
-			}
-			//checking the sizes of the textures (tehy should perfectly match)
-			int lastTextureWithoutAlphaIndex = 0;
-			int width = sources.get(0).getImage().getWidth();
-			int height = sources.get(0).getImage().getHeight();
-			int depth = sources.get(0).getImage().getDepth();
-			
-			for(Texture source : sources) {
-				if(source.getImage().getWidth() != width) {
-					throw new IllegalArgumentException("The texture " + source.getName() + " has invalid width! It should be: " + width + '!');
-				}
-				if(source.getImage().getHeight() != height) {
-					throw new IllegalArgumentException("The texture " + source.getName() + " has invalid height! It should be: " + height + '!');
-				}
-				if(source.getImage().getDepth() != depth) {
-					throw new IllegalArgumentException("The texture " + source.getName() + " has invalid depth! It should be: " + depth + '!');
-				}
-				//support for more formats is not necessary at the moment
-				if(source.getImage().getFormat()!=Format.RGB8 && source.getImage().getFormat()!=Format.BGR8) {
-					++lastTextureWithoutAlphaIndex;
-				}
-			}
-			if(depth==0) {
-				depth = 1;
-			}
-			
-			//remove textures before the one without alpha (they will be covered anyway)
-			if(lastTextureWithoutAlphaIndex > 0 && lastTextureWithoutAlphaIndex<sources.size()-1) {
-				sources = sources.subList(lastTextureWithoutAlphaIndex, sources.size()-1);
-			}
-			int pixelsAmount = width * height * depth;
-			
-			ByteBuffer data = BufferUtils.createByteBuffer(pixelsAmount * 3);
-			TexturePixel resultPixel = new TexturePixel();
-			TexturePixel sourcePixel = new TexturePixel();
-			ColorRGBA diffuseColor = materialContext.getDiffuseColor();
-			for (int i = 0; i < pixelsAmount; ++i) {
-				for (int j = 0; j < sources.size(); ++j) {
-					Image image = sources.get(j).getImage();
-					ByteBuffer sourceData = image.getData(0);
-					if(j==0) {
-						resultPixel.fromColor(diffuseColor);
-						sourcePixel.fromImage(image.getFormat(), sourceData, i);
-						resultPixel.merge(sourcePixel);
-					} else {
-						sourcePixel.fromImage(image.getFormat(), sourceData, i);
-						resultPixel.merge(sourcePixel);
-					}
-				}
-				data.put((byte)(255 * resultPixel.red));
-				data.put((byte)(255 * resultPixel.green));
-				data.put((byte)(255 * resultPixel.blue));
-				resultPixel.clear();
-			}
-			
-			if(depth==1) {
-				result = new Texture2D(new Image(Format.RGB8, width, height, data));
-			} else {
-				ArrayList<ByteBuffer> arrayData = new ArrayList<ByteBuffer>(1);
-				arrayData.add(data);
-				result = new Texture3D(new Image(Format.RGB8, width, height, depth, arrayData));
-			}
-		}
-		return result;
-	}
-
 	/**
 	 * This method converts the given texture into normal-map texture.
+	 * 
 	 * @param source
-	 *        the source texture
+	 *            the source texture
 	 * @param strengthFactor
-	 *        the normal strength factor
+	 *            the normal strength factor
 	 * @return normal-map texture
 	 */
 	public Texture convertToNormalMapTexture(Texture source, float strengthFactor) {
@@ -337,14 +228,172 @@ public class TextureHelper extends AbstractBlenderHelper {
 	}
 
 	/**
-	 * This method returns the height represented by the specified pixel in the given texture.
-	 * The given texture should be a height-map.
+	 * This method decompresses the given image. If the given image is already
+	 * decompressed nothing happens and it is simply returned.
+	 * 
 	 * @param image
-	 *        the height-map texture
+	 *            the image to decompress
+	 * @return the decompressed image
+	 */
+	public Image decompress(Image image) {
+		byte[] bytes = null;
+		TexturePixel[] colors = null;
+		ByteBuffer data = image.getData(0);// TODO: support decompression of all data 'layers'
+		data.rewind();
+		Format format = image.getFormat();
+
+		DDSTexelData texelData = new DDSTexelData(data.remaining() / (format.getBitsPerPixel() * 2), image.getWidth(), image.getHeight(), format != Format.DXT1);
+		switch (format) {// TODO: DXT1A
+			case DXT1:// BC1
+				bytes = new byte[image.getWidth() * image.getHeight() * 4];
+				colors = new TexturePixel[] { new TexturePixel(), new TexturePixel(), new TexturePixel(), new TexturePixel() };
+				while (data.hasRemaining()) {
+					short c0 = data.getShort();
+					short c1 = data.getShort();
+					int col0 = RGB565.RGB565_to_ARGB8(c0);
+					int col1 = RGB565.RGB565_to_ARGB8(c1);
+					colors[0].fromARGB8(col0);
+					colors[1].fromARGB8(col1);
+
+					if (col0 > col1) {
+						// creating color2 = 2/3color0 + 1/3color1
+						colors[2].fromPixel(colors[0]);
+						colors[2].mult(2);
+						colors[2].add(colors[1]);
+						colors[2].divide(3);
+
+						// creating color3 = 1/3color0 + 2/3color1;
+						colors[3].fromPixel(colors[1]);
+						colors[3].mult(2);
+						colors[3].add(colors[0]);
+						colors[3].divide(3);
+					} else {
+						// creating color2 = 1/2color0 + 1/2color1
+						colors[2].fromPixel(colors[0]);
+						colors[2].add(colors[1]);
+						colors[2].mult(0.5f);
+
+						colors[3].fromARGB8(0);
+					}
+					int indexes = data.getInt();// 4-byte table with color indexes in decompressed table
+					texelData.add(colors, indexes);
+				}
+				break;
+			case DXT3:// BC2
+				bytes = new byte[image.getWidth() * image.getHeight() * 4];
+				colors = new TexturePixel[] { new TexturePixel(), new TexturePixel(), new TexturePixel(), new TexturePixel() };
+				while (data.hasRemaining()) {
+					long alpha = data.getLong();
+					float[] alphas = new float[16];
+					long alphasIndex = 0;
+					for (int i = 0; i < 16; ++i) {
+						alphasIndex |= i << i * 4;
+						byte a = (byte) ((alpha >> i * 4 & 0x0F) << 4);
+						alphas[i] = a >= 0 ? a / 255.0f : 1.0f - ~a / 255.0f;
+					}
+
+					short c0 = data.getShort();
+					short c1 = data.getShort();
+					int col0 = RGB565.RGB565_to_ARGB8(c0);
+					int col1 = RGB565.RGB565_to_ARGB8(c1);
+					colors[0].fromARGB8(col0);
+					colors[1].fromARGB8(col1);
+
+					// creating color2 = 2/3color0 + 1/3color1
+					colors[2].fromPixel(colors[0]);
+					colors[2].mult(2);
+					colors[2].add(colors[1]);
+					colors[2].divide(3);
+
+					// creating color3 = 1/3color0 + 2/3color1;
+					colors[3].fromPixel(colors[1]);
+					colors[3].mult(2);
+					colors[3].add(colors[0]);
+					colors[3].divide(3);
+
+					int indexes = data.getInt();// 4-byte table with color indexes in decompressed table
+					texelData.add(colors, indexes, alphas, alphasIndex);
+				}
+				break;
+			case DXT5:// BC3
+				bytes = new byte[image.getWidth() * image.getHeight() * 4];
+				colors = new TexturePixel[] { new TexturePixel(), new TexturePixel(), new TexturePixel(), new TexturePixel() };
+				float[] alphas = new float[8];
+				while (data.hasRemaining()) {
+					alphas[0] = data.get() * 255.0f;
+					alphas[1] = data.get() * 255.0f;
+					long alphaIndices = data.get() | data.get() << 8 | data.get() << 16 | data.get() << 24 | data.get() << 32 | data.get() << 40;
+					if (alphas[0] > alphas[1]) {// 6 interpolated alpha values.
+						alphas[2] = (6 * alphas[0] + alphas[1]) / 7;
+						alphas[3] = (5 * alphas[0] + 2 * alphas[1]) / 7;
+						alphas[4] = (4 * alphas[0] + 3 * alphas[1]) / 7;
+						alphas[5] = (3 * alphas[0] + 4 * alphas[1]) / 7;
+						alphas[6] = (2 * alphas[0] + 5 * alphas[1]) / 7;
+						alphas[7] = (alphas[0] + 6 * alphas[1]) / 7;
+					} else {
+						alphas[2] = (4 * alphas[0] + alphas[1]) * 0.2f;
+						alphas[3] = (3 * alphas[0] + 2 * alphas[1]) * 0.2f;
+						alphas[4] = (2 * alphas[0] + 3 * alphas[1]) * 0.2f;
+						alphas[5] = (alphas[0] + 4 * alphas[1]) * 0.2f;
+						alphas[6] = 0;
+						alphas[7] = 1;
+					}
+
+					short c0 = data.getShort();
+					short c1 = data.getShort();
+					int col0 = RGB565.RGB565_to_ARGB8(c0);
+					int col1 = RGB565.RGB565_to_ARGB8(c1);
+					colors[0].fromARGB8(col0);
+					colors[1].fromARGB8(col1);
+
+					// creating color2 = 2/3color0 + 1/3color1
+					colors[2].fromPixel(colors[0]);
+					colors[2].mult(2);
+					colors[2].add(colors[1]);
+					colors[2].divide(3);
+
+					// creating color3 = 1/3color0 + 2/3color1;
+					colors[3].fromPixel(colors[1]);
+					colors[3].mult(2);
+					colors[3].add(colors[0]);
+					colors[3].divide(3);
+
+					int indexes = data.getInt();// 4-byte table with color indexes in decompressed table
+					texelData.add(colors, indexes, alphas, alphaIndices);
+				}
+				break;
+			default:
+				LOGGER.fine("Unsupported decompression format.");
+		}
+
+		if (bytes != null) {// writing the data to the result table
+			byte[] pixelBytes = new byte[4];
+			for (int i = 0; i < image.getWidth(); ++i) {
+				for (int j = 0; j < image.getHeight(); ++j) {
+					texelData.getRGBA8(i, j, pixelBytes);
+					bytes[(j * image.getWidth() + i) * 4] = pixelBytes[0];
+					bytes[(j * image.getWidth() + i) * 4 + 1] = pixelBytes[1];
+					bytes[(j * image.getWidth() + i) * 4 + 2] = pixelBytes[2];
+					bytes[(j * image.getWidth() + i) * 4 + 3] = pixelBytes[3];
+				}
+			}
+			// TODO: think of other image formats (ie. RGB if the texture has no
+			// alpha values)
+			return new Image(Format.RGBA8, image.getWidth(), image.getHeight(), BufferUtils.createByteBuffer(bytes));
+		}
+		return image;
+	}
+
+	/**
+	 * This method returns the height represented by the specified pixel in the
+	 * given texture. The given texture should be a height-map.
+	 * 
+	 * @param image
+	 *            the height-map texture
 	 * @param x
-	 *        pixel's X coordinate
+	 *            pixel's X coordinate
 	 * @param y
-	 *        pixel's Y coordinate
+	 *            pixel's Y coordinate
 	 * @return height reprezented by the given texture in the specified location
 	 */
 	protected int getHeight(BufferedImage image, int x, int y) {
@@ -362,10 +411,15 @@ public class TextureHelper extends AbstractBlenderHelper {
 	}
 
 	/**
-	 * This method transforms given vector's coordinates into ARGB color (A is always = 255).
-	 * @param x X factor of the vector
-	 * @param y Y factor of the vector
-	 * @param z Z factor of the vector
+	 * This method transforms given vector's coordinates into ARGB color (A is
+	 * always = 255).
+	 * 
+	 * @param x
+	 *            X factor of the vector
+	 * @param y
+	 *            Y factor of the vector
+	 * @param z
+	 *            Z factor of the vector
 	 * @return color representation of the given vector
 	 */
 	protected int vectorToColor(float x, float y, float z) {
@@ -376,15 +430,17 @@ public class TextureHelper extends AbstractBlenderHelper {
 	}
 
 	/**
-	 * This class returns a texture read from the file or from packed blender data.
+	 * This class returns a texture read from the file or from packed blender
+	 * data.
 	 * 
 	 * @param image
-	 *        image structure filled with data
+	 *            image structure filled with data
 	 * @param blenderContext
-	 *        the blender context
+	 *            the blender context
 	 * @return the texture that can be used by JME engine
 	 * @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 Texture getTextureFromImage(Structure image, BlenderContext blenderContext) throws BlenderFileException {
 		LOGGER.log(Level.FINE, "Fetching texture with OMA = {0}", image.getOldMemoryAddress());
@@ -412,8 +468,8 @@ public class TextureHelper extends AbstractBlenderHelper {
 			if (result != null) {
 				result.setName(texturePath);
 				result.setWrap(Texture.WrapMode.Repeat);
-				if(LOGGER.isLoggable(Level.FINE)) {
-					LOGGER.log(Level.FINE, "Adding texture {0} to the loaded features with OMA = {1}", new Object[] {texturePath, image.getOldMemoryAddress()});
+				if (LOGGER.isLoggable(Level.FINE)) {
+					LOGGER.log(Level.FINE, "Adding texture {0} to the loaded features with OMA = {1}", new Object[] { texturePath, image.getOldMemoryAddress() });
 				}
 				blenderContext.addLoadedFeatures(image.getOldMemoryAddress(), image.getName(), image, result);
 			}
@@ -421,20 +477,71 @@ public class TextureHelper extends AbstractBlenderHelper {
 		return result;
 	}
 
+	/**
+	 * This method applies the colorband and color factors to image type
+	 * textures. If there is no colorband defined for the texture or the color
+	 * factors are all equal to 1.0f then no changes are made.
+	 * 
+	 * @param tex
+	 *            the texture structure
+	 * @param image
+	 *            the image that will be altered if necessary
+	 * @param blenderContext
+	 *            the blender context
+	 */
+	private void applyColorbandAndColorFactors(Structure tex, Image image, BlenderContext blenderContext) {
+		float rfac = ((Number) tex.getFieldValue("rfac")).floatValue();
+		float gfac = ((Number) tex.getFieldValue("gfac")).floatValue();
+		float bfac = ((Number) tex.getFieldValue("bfac")).floatValue();
+		float[][] colorBand = new ColorBand(tex, blenderContext).computeValues();
+
+		if (colorBand != null) {
+			TexturePixel pixel = new TexturePixel();
+			PixelInputOutput imageIO = PixelIOFactory.getPixelIO(image.getFormat());
+			for (int x = 0; x < image.getWidth(); ++x) {
+				for (int y = 0; y < image.getHeight(); ++y) {
+					imageIO.read(image, pixel, x, y);
+
+					int colorbandIndex = (int) (pixel.alpha * 1000.0f);
+					pixel.red = colorBand[colorbandIndex][0] * rfac;
+					pixel.green = colorBand[colorbandIndex][1] * gfac;
+					pixel.blue = colorBand[colorbandIndex][2] * bfac;
+					pixel.alpha = colorBand[colorbandIndex][3];
+
+					imageIO.write(image, pixel, x, y);
+				}
+			}
+		} else if (rfac != 1.0f || gfac != 1.0f || bfac != 1.0f) {
+			TexturePixel pixel = new TexturePixel();
+			PixelInputOutput imageIO = PixelIOFactory.getPixelIO(image.getFormat());
+			for (int x = 0; x < image.getWidth(); ++x) {
+				for (int y = 0; y < image.getHeight(); ++y) {
+					imageIO.read(image, pixel, x, y);
+
+					pixel.red *= rfac;
+					pixel.green *= gfac;
+					pixel.blue *= bfac;
+
+					imageIO.write(image, pixel, x, y);
+				}
+			}
+		}
+	}
+
 	/**
 	 * This method loads the textre from outside the blend file.
 	 * 
 	 * @param name
-	 *        the path to the image
+	 *            the path to the image
 	 * @param blenderContext
-	 *        the blender context
+	 *            the blender context
 	 * @return the loaded image or null if the image cannot be found
 	 */
 	protected Texture loadTextureFromFile(String name, BlenderContext blenderContext) {
-                if (!name.contains(".")){
-                    return null; // no extension means not a valid image
-                }
-                
+		if (!name.contains(".")) {
+			return null; // no extension means not a valid image
+		}
+
 		AssetManager assetManager = blenderContext.getAssetManager();
 		name = name.replaceAll("\\\\", "\\/");
 		Texture result = null;
@@ -442,32 +549,34 @@ public class TextureHelper extends AbstractBlenderHelper {
 		List<String> assetNames = new ArrayList<String>();
 		if (name.startsWith("//")) {
 			String relativePath = name.substring(2);
-			//augument the path with blender key path
+			// augument the path with blender key path
 			BlenderKey blenderKey = blenderContext.getBlenderKey();
-            int idx = blenderKey.getName().lastIndexOf('/');
+			int idx = blenderKey.getName().lastIndexOf('/');
 			String blenderAssetFolder = blenderKey.getName().substring(0, idx != -1 ? idx : 0);
-			assetNames.add(blenderAssetFolder+'/'+relativePath);
-		} else {//use every path from the asset name to the root (absolute path)
+			assetNames.add(blenderAssetFolder + '/' + relativePath);
+		} else {// use every path from the asset name to the root (absolute
+				// path)
 			String[] paths = name.split("\\/");
-			StringBuilder sb = new StringBuilder(paths[paths.length-1]);//the asset name
-			assetNames.add(paths[paths.length-1]);
+			StringBuilder sb = new StringBuilder(paths[paths.length - 1]);// the asset name
+			assetNames.add(paths[paths.length - 1]);
 
-			for(int i=paths.length-2;i>=0;--i) {
+			for (int i = paths.length - 2; i >= 0; --i) {
 				sb.insert(0, '/');
 				sb.insert(0, paths[i]);
 				assetNames.add(0, sb.toString());
 			}
 		}
 
-		//now try to locate the asset
-		for(String assetName : assetNames) {
+		// now try to locate the asset
+		for (String assetName : assetNames) {
 			try {
-                TextureKey key = new TextureKey(assetName);
-                key.setGenerateMips(true);
-                key.setAsCube(false);
+				TextureKey key = new TextureKey(assetName);
+				key.setGenerateMips(true);
+				key.setAsCube(false);
 				result = assetManager.loadTexture(key);
-				break;//if no exception is thrown then accept the located asset and break the loop
-			} catch(AssetNotFoundException e) {
+				break;// if no exception is thrown then accept the located asset
+						// and break the loop
+			} catch (AssetNotFoundException e) {
 				LOGGER.fine(e.getLocalizedMessage());
 			}
 		}

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

@@ -2,10 +2,6 @@ package com.jme3.scene.plugins.blender.textures;
 
 import com.jme3.math.ColorRGBA;
 import com.jme3.math.FastMath;
-import com.jme3.texture.Image.Format;
-import java.nio.ByteBuffer;
-import java.util.logging.Level;
-import java.util.logging.Logger;
 
 /**
  * The class that stores the pixel values of a texture.
@@ -13,8 +9,6 @@ import java.util.logging.Logger;
  * @author Marcin Roguski (Kaelthas)
  */
 public class TexturePixel implements Cloneable {
-	private static final Logger	LOGGER	= Logger.getLogger(TexturePixel.class.getName());
-
 	/** The pixel data. */
 	public float				intensity, red, green, blue, alpha;
 
@@ -64,6 +58,35 @@ public class TexturePixel implements Cloneable {
 		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 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 values from the given integer that stores the ARGB8 data.
 	 * 
@@ -72,81 +95,15 @@ public class TexturePixel implements Cloneable {
 	 */
 	public void fromARGB8(int argb8) {
 		byte pixelValue = (byte) ((argb8 & 0xFF000000) >> 24);
-		this.alpha = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
+		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;
+		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;
+		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;
+		this.blue = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - ~pixelValue / 255.0f;
 	}
-
-	/**
-	 * Copies the data from the given image.
-	 * 
-	 * @param imageFormat
-	 *            the image format
-	 * @param data
-	 *            the image data
-	 * @param pixelIndex
-	 *            the index of the required pixel
-	 */
-	public void fromImage(Format imageFormat, ByteBuffer data, int pixelIndex) {
-		int firstByteIndex;
-		byte pixelValue;
-		switch (imageFormat) {
-			case ABGR8:
-				firstByteIndex = pixelIndex << 2;
-				pixelValue = data.get(firstByteIndex);
-				this.alpha = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
-				pixelValue = data.get(firstByteIndex + 1);
-				this.blue = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
-				pixelValue = data.get(firstByteIndex + 2);
-				this.green = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
-				pixelValue = data.get(firstByteIndex + 3);
-				this.red = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
-				break;
-			case RGBA8:
-				firstByteIndex = pixelIndex << 2;
-				pixelValue = data.get(firstByteIndex);
-				this.red = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
-				pixelValue = data.get(firstByteIndex + 1);
-				this.green = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
-				pixelValue = data.get(firstByteIndex + 2);
-				this.blue = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
-				pixelValue = data.get(firstByteIndex + 3);
-				this.alpha = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
-				break;
-			case BGR8:
-				firstByteIndex = pixelIndex * 3;
-				pixelValue = data.get(firstByteIndex);
-				this.blue = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
-				pixelValue = data.get(firstByteIndex + 1);
-				this.green = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
-				pixelValue = data.get(firstByteIndex + 2);
-				this.red = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
-				this.alpha = 1.0f;
-				break;
-			case RGB8:
-				firstByteIndex = pixelIndex * 3;
-				pixelValue = data.get(firstByteIndex);
-				this.red = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
-				pixelValue = data.get(firstByteIndex + 1);
-				this.green = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
-				pixelValue = data.get(firstByteIndex + 2);
-				this.blue = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
-				this.alpha = 1.0f;
-				break;
-			case Luminance8:
-				pixelValue = data.get(pixelIndex);
-				this.intensity = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
-				break;
-			default:
-				LOGGER.log(Level.FINEST, "Unknown type of texture: {0}. Black pixel used!", imageFormat);
-				this.intensity = this.blue = this.red = this.green = this.alpha = 0.0f;
-		}
-	}
-
+	
 	/**
 	 * Stores RGBA values in the given array.
 	 * 
@@ -191,6 +148,41 @@ public class TexturePixel implements Cloneable {
 		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);
+	}
+	
 	/**
 	 * Merges two pixels (adds the values of each color).
 	 * 
@@ -202,7 +194,7 @@ public class TexturePixel implements Cloneable {
 		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;
-		// alpha should be always 1.0f as a result
+		this.alpha = (this.alpha + pixel.alpha) * 0.5f;
 	}
 
 	/**

+ 721 - 0
engine/src/blender/com/jme3/scene/plugins/blender/textures/TriangulatedTexture.java

@@ -0,0 +1,721 @@
+package com.jme3.scene.plugins.blender.textures;
+
+import java.awt.Graphics2D;
+import java.awt.RenderingHints;
+import java.awt.geom.AffineTransform;
+import java.awt.image.BufferedImage;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.TreeSet;
+
+import jme3tools.converters.ImageToAwt;
+
+import com.jme3.bounding.BoundingBox;
+import com.jme3.math.FastMath;
+import com.jme3.math.Vector2f;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.textures.blending.TextureBlender;
+import com.jme3.scene.plugins.blender.textures.io.PixelIOFactory;
+import com.jme3.scene.plugins.blender.textures.io.PixelInputOutput;
+import com.jme3.texture.Image;
+import com.jme3.texture.Image.Format;
+import com.jme3.texture.Texture;
+import com.jme3.texture.Texture2D;
+import com.jme3.util.BufferUtils;
+
+/**
+ * This texture holds a set of images for each face in the specified mesh. It
+ * helps to flatten 3D texture, merge 3D and 2D textures and merge 2D textures
+ * with different UV coordinates.
+ * 
+ * @author Marcin Roguski (Kaelthas)
+ */
+/* package */class TriangulatedTexture extends Texture {
+	/** 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;
+
+	/** 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>() {
+			@Override
+			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, uvs));
+		}
+		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();
+		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 = this.createTransform(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, sourcePixel, x, y);
+					targetIO.read(targetImage, targetPixel, x, y);
+					targetPixel.merge(sourcePixel);
+					targetIO.write(targetImage, 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>() {
+				@Override
+				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);
+
+				// 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]);
+				}
+			}
+		}
+		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
+	 */
+	private 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);
+	}
+
+	/**
+	 * 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, pixel, x, y);
+				targetIO.write(target, pixel, targetXPos + x, targetYPos + y);
+			}
+		}
+	}
+
+	/**
+	 * This method creates the affine transform that is used to transform a
+	 * triangle defined by one UV coordinates into a triangle defined by
+	 * different UV's.
+	 * 
+	 * @param source
+	 *            source UV coordinates
+	 * @param dest
+	 *            target UV coordinates
+	 * @param sourceSize
+	 *            the width and height of the source image
+	 * @param targetSize
+	 *            the width and height of the target image
+	 * @return affine transform to transform one triangle to another
+	 */
+	private AffineTransform createTransform(Vector2f[] source, Vector2f[] dest, int[] sourceSize, int[] targetSize) {
+		float x11 = source[0].getX() * sourceSize[0];
+		float x12 = source[0].getY() * sourceSize[1];
+		float x21 = source[1].getX() * sourceSize[0];
+		float x22 = source[1].getY() * sourceSize[1];
+		float x31 = source[2].getX() * sourceSize[0];
+		float x32 = source[2].getY() * sourceSize[1];
+		float y11 = dest[0].getX() * targetSize[0];
+		float y12 = dest[0].getY() * targetSize[1];
+		float y21 = dest[1].getX() * targetSize[0];
+		float y22 = dest[1].getY() * targetSize[1];
+		float y31 = dest[2].getX() * targetSize[0];
+		float y32 = dest[2].getY() * targetSize[1];
+
+		float a1 = ((y11 - y21) * (x12 - x32) - (y11 - y31) * (x12 - x22)) / ((x11 - x21) * (x12 - x32) - (x11 - x31) * (x12 - x22));
+		float a2 = ((y11 - y21) * (x11 - x31) - (y11 - y31) * (x11 - x21)) / ((x12 - x22) * (x11 - x31) - (x12 - x32) * (x11 - x21));
+		float a3 = y11 - a1 * x11 - a2 * x12;
+		float a4 = ((y12 - y22) * (x12 - x32) - (y12 - y32) * (x12 - x22)) / ((x11 - x21) * (x12 - x32) - (x11 - x31) * (x12 - x22));
+		float a5 = ((y12 - y22) * (x11 - x31) - (y12 - y32) * (x11 - x21)) / ((x12 - x22) * (x11 - x31) - (x12 - x32) * (x11 - x21));
+		float a6 = y12 - a4 * x11 - a5 * x12;
+		return new AffineTransform(a1, a4, a2, a5, a3, a6);
+	}
+
+	/**
+	 * 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 texture
+		 *            the source texture
+		 * @param uvCoordinates
+		 *            the UV coordinates that define the image
+		 */
+		public TriangleTextureElement(int faceIndex, Texture2D texture, List<Vector2f> uvCoordinates) {
+			this.faceIndex = faceIndex;
+			Image sourceImage = texture.getImage();
+
+			uv = new Vector2f[] { uvCoordinates.get(faceIndex * 3).clone(), uvCoordinates.get(faceIndex * 3 + 1).clone(), uvCoordinates.get(faceIndex * 3 + 2).clone() };
+
+			float pixelWidth = 1 / (float) sourceImage.getWidth();
+			float pixelHeight = 1 / (float) sourceImage.getHeight();
+
+			// 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) {
+				float x = uv[i].x * sourceImage.getWidth();
+				float y = uv[i].y * sourceImage.getHeight();
+				// here is where errors may occur
+				texturePosition[i][0] = (int) x;
+				texturePosition[i][1] = (int) y;
+				// here is where we repair errors :)
+				if (Math.abs(texturePosition[i][0] - x) > pixelWidth) {
+					++texturePosition[i][0];
+				}
+				if (Math.abs(texturePosition[i][1] - y) > pixelHeight) {
+					++texturePosition[i][1];
+				}
+			}
+
+			// 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, 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, 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;
+
+			int uvIndex = faceIndex * 3;
+			Vector3f min = boundingBox.getMin(null);
+			Vector3f v1 = min.add(uv[uvIndex].x * width, uv[uvIndex].y * height, uv[uvIndex].z * depth);
+			Vector3f v2 = min.add(uv[uvIndex + 1].x * width, uv[uvIndex + 1].y * height, uv[uvIndex + 1].z * depth);
+			Vector3f v3 = min.add(uv[uvIndex + 2].x * width, uv[uvIndex + 2].y * height, uv[uvIndex + 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());
+			int imageHeight = (int) (envelope.height * blenderContext.getBlenderKey().getGeneratedTexturePPU());
+			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, 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());
+		}
+
+		/**
+		 * 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 void setWrap(WrapAxis axis, WrapMode mode) {
+	}
+
+	@Override
+	public void setWrap(WrapMode mode) {
+	}
+
+	@Override
+	public WrapMode getWrap(WrapAxis axis) {
+		return null;
+	}
+
+	@Override
+	public Type getType() {
+		return Type.TwoDimensional;
+	}
+
+	@Override
+	public Texture createSimpleClone() {
+		return null;
+	}
+}

+ 168 - 115
engine/src/blender/com/jme3/scene/plugins/blender/textures/UVCoordinatesGenerator.java

@@ -31,6 +31,11 @@
  */
 package com.jme3.scene.plugins.blender.textures;
 
+import java.nio.FloatBuffer;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.logging.Logger;
+
 import com.jme3.bounding.BoundingBox;
 import com.jme3.bounding.BoundingSphere;
 import com.jme3.bounding.BoundingVolume;
@@ -39,69 +44,144 @@ import com.jme3.math.Vector3f;
 import com.jme3.scene.Geometry;
 import com.jme3.scene.Mesh;
 import com.jme3.scene.VertexBuffer;
-import com.jme3.scene.VertexBuffer.Format;
-import com.jme3.scene.VertexBuffer.Usage;
+import com.jme3.scene.plugins.blender.textures.UVProjectionGenerator.UVProjectionType;
 import com.jme3.util.BufferUtils;
-import java.nio.FloatBuffer;
-import java.util.List;
-import java.util.logging.Logger;
 
 /**
  * This class is used for UV coordinates generation.
+ * 
  * @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());
 
-	// texture UV coordinates types
-	public static final int		TEXCO_ORCO					= 1;
-	public static final int		TEXCO_REFL					= 2;
-	public static final int		TEXCO_NORM					= 4;
-	public static final int		TEXCO_GLOB					= 8;
-	public static final int		TEXCO_UV					= 16;
-	public static final int		TEXCO_OBJECT				= 32;
-	public static final int		TEXCO_LAVECTOR				= 64;
-	public static final int		TEXCO_VIEW					= 128;
-	public static final int		TEXCO_STICKY				= 256;
-	public static final int		TEXCO_OSA					= 512;
-	public static final int		TEXCO_WINDOW				= 1024;
-	public static final int		NEED_UV						= 2048;
-	public static final int		TEXCO_TANGENT				= 4096;
-	// still stored in vertex->accum, 1 D
-	public static final int		TEXCO_PARTICLE_OR_STRAND	= 8192;													// strand is used
-	public static final int		TEXCO_STRESS				= 16384;
-	public static final int		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);
 
-	// 2D texture mapping (projection)
-	public static final int		PROJECTION_FLAT				= 0;
-	public static final int		PROJECTION_CUBE				= 1;
-	public static final int		PROJECTION_TUBE				= 2;
-	public static final int		PROJECTION_SPHERE			= 3;
+		public final int	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;
+		}
+	}
 
 	/**
-	 * This method generates UV coordinates for the given mesh.
-	 * IMPORTANT! This method assumes that all geometries represent one node.
-	 * Each containing mesh with separate material.
-	 * So all meshes have the same reference to vertex table which stores all their vertices.
+	 * Generates a UV coordinates for 2D texture.
+	 * 
+	 * @param mesh
+	 *            the mesh we generate UV's for
 	 * @param texco
-	 *        texture coordinates type
+	 *            UV coordinates type
 	 * @param projection
-	 *        the projection type for 2D textures
-	 * @param textureDimension
-	 *        the dimension of the texture (only 2D and 3D)
-	 * @param coordinatesSwappingIndexes
-	 *        an array that tells how UV-coordinates need to be swapped
+	 *            projection type
 	 * @param geometries
-	 *        a list of geometries the UV coordinates will be applied to
-	 * @return created UV-coordinates buffer
+	 *            the geometris the given mesh belongs to (required to compute
+	 *            bounding box)
+	 * @return UV coordinates for the given mesh
 	 */
-	public static VertexBuffer generateUVCoordinates(int texco, int projection, int textureDimension, int[] coordinatesSwappingIndexes, List<Geometry> geometries) {
-		if (textureDimension != 2 && textureDimension != 3) {
-			throw new IllegalStateException("Unsupported texture dimension: " + textureDimension);
+	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);
 		}
 
-		VertexBuffer result = new VertexBuffer(VertexBuffer.Type.TexCoord);
-		Mesh mesh = geometries.get(0).getMesh();
+		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.
 
@@ -110,17 +190,11 @@ public class UVCoordinatesGenerator {
 				inputData = BufferUtils.getFloatArray(mesh.getFloatBuffer(VertexBuffer.Type.Position));
 				break;
 			case TEXCO_UV:
-				FloatBuffer uvCoordinatesBuffer = BufferUtils.createFloatBuffer(mesh.getVertexCount() * textureDimension);
 				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];
-					uvCoordinatesBuffer.put(uv.x);
-					uvCoordinatesBuffer.put(uv.y);
-					if(textureDimension == 3) {
-						uvCoordinatesBuffer.put(0);
-					}
+					result.add(new Vector3f(uv.x, uv.y, 0));
 				}
-				result.setupData(Usage.Static, textureDimension, Format.Float, uvCoordinatesBuffer);
 				break;
 			case TEXCO_NORM:
 				inputData = BufferUtils.getFloatArray(mesh.getFloatBuffer(VertexBuffer.Type.Normal));
@@ -144,62 +218,34 @@ public class UVCoordinatesGenerator {
 		}
 
 		if (inputData != null) {// make calculations
-			if (textureDimension == 2) {
-				switch (projection) {
-					case PROJECTION_FLAT:
-						inputData = UVProjectionGenerator.flatProjection(mesh, bb);
-						break;
-					case PROJECTION_CUBE:
-						inputData = UVProjectionGenerator.cubeProjection(mesh, bb);
-						break;
-					case PROJECTION_TUBE:
-						BoundingTube bt = UVCoordinatesGenerator.getBoundingTube(geometries);
-						inputData = UVProjectionGenerator.tubeProjection(mesh, bt);
-						break;
-					case PROJECTION_SPHERE:
-						BoundingSphere bs = UVCoordinatesGenerator.getBoundingSphere(geometries);
-						inputData = UVProjectionGenerator.sphereProjection(mesh, bs);
-						break;
-					default:
-						throw new IllegalStateException("Unknown projection type: " + projection);
-				}
-			} else {
-				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 };
-
-				// 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];
-					
-					
-					inputData[i] = uvCoordsResults[coordinatesSwappingIndexes[0]];
-					inputData[i + 1] = uvCoordsResults[coordinatesSwappingIndexes[1]];
-					inputData[i + 2] = uvCoordsResults[coordinatesSwappingIndexes[2]];
+			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;
 				}
 			}
-			result.setupData(Usage.Static, textureDimension, Format.Float, BufferUtils.createFloatBuffer(inputData));
-		}
-
-		// each mesh will have the same coordinates
-		for (Geometry geometry : geometries) {
-			mesh = geometry.getMesh();
-			mesh.clearBuffer(VertexBuffer.Type.TexCoord);// in case there are coordinates already set
-			mesh.setBuffer(result);
+			// 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
+	 *            the list of geometries
 	 * @return bounding box of the given geometries
 	 */
-	/* package */static BoundingBox getBoundingBox(List<Geometry> geometries) {
+	public static BoundingBox getBoundingBox(List<Geometry> geometries) {
 		BoundingBox result = null;
 		for (Geometry geometry : geometries) {
 			BoundingBox bb = UVCoordinatesGenerator.getBoundingBox(geometry.getMesh());
@@ -214,8 +260,9 @@ public class UVCoordinatesGenerator {
 
 	/**
 	 * This method returns the bounding box of the given mesh.
+	 * 
 	 * @param mesh
-	 *        the mesh
+	 *            the mesh
 	 * @return bounding box of the given mesh
 	 */
 	/* package */static BoundingBox getBoundingBox(Mesh mesh) {
@@ -234,8 +281,9 @@ public class UVCoordinatesGenerator {
 
 	/**
 	 * This method returns the bounding sphere of the given geometries.
+	 * 
 	 * @param geometries
-	 *        the list of geometries
+	 *            the list of geometries
 	 * @return bounding sphere of the given geometries
 	 */
 	/* package */static BoundingSphere getBoundingSphere(List<Geometry> geometries) {
@@ -253,8 +301,9 @@ public class UVCoordinatesGenerator {
 
 	/**
 	 * This method returns the bounding sphere of the given mesh.
+	 * 
 	 * @param mesh
-	 *        the mesh
+	 *            the mesh
 	 * @return bounding sphere of the given mesh
 	 */
 	/* package */static BoundingSphere getBoundingSphere(Mesh mesh) {
@@ -274,8 +323,9 @@ public class UVCoordinatesGenerator {
 
 	/**
 	 * This method returns the bounding tube of the given mesh.
+	 * 
 	 * @param mesh
-	 *        the mesh
+	 *            the mesh
 	 * @return bounding tube of the given mesh
 	 */
 	/* package */static BoundingTube getBoundingTube(Mesh mesh) {
@@ -303,11 +353,12 @@ public class UVCoordinatesGenerator {
 		float radius = Math.max(maxx - minx, maxy - miny) * 0.5f;
 		return new BoundingTube(radius, maxz - minz, center);
 	}
-	
+
 	/**
 	 * This method returns the bounding tube of the given geometries.
+	 * 
 	 * @param geometries
-	 *        the list of geometries
+	 *            the list of geometries
 	 * @return bounding tube of the given geometries
 	 */
 	/* package */static BoundingTube getBoundingTube(List<Geometry> geometries) {
@@ -324,9 +375,11 @@ public class UVCoordinatesGenerator {
 	}
 
 	/**
-	 * 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.
+	 * 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 {
@@ -336,12 +389,13 @@ public class UVCoordinatesGenerator {
 
 		/**
 		 * Constructor creates the tube with the given params.
+		 * 
 		 * @param radius
-		 *        the radius of the tube
+		 *            the radius of the tube
 		 * @param height
-		 *        the height of the tube
+		 *            the height of the tube
 		 * @param center
-		 *        the center of the tube
+		 *            the center of the tube
 		 */
 		public BoundingTube(float radius, float height, Vector3f center) {
 			this.radius = radius;
@@ -351,8 +405,9 @@ public class UVCoordinatesGenerator {
 
 		/**
 		 * This method merges two bounding tubes.
+		 * 
 		 * @param boundingTube
-		 *        bounding tube to be merged woth the current one
+		 *            bounding tube to be merged woth the current one
 		 * @return new instance of bounding tube representing the tubes' merge
 		 */
 		public BoundingTube merge(BoundingTube boundingTube) {
@@ -375,14 +430,14 @@ public class UVCoordinatesGenerator {
 			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 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 returns the radius of the tube.
 		 * @return the radius of the tube
 		 */
 		public float getRadius() {
@@ -390,7 +445,6 @@ public class UVCoordinatesGenerator {
 		}
 
 		/**
-		 * This method returns the height of the tube.
 		 * @return the height of the tube
 		 */
 		public float getHeight() {
@@ -398,7 +452,6 @@ public class UVCoordinatesGenerator {
 		}
 
 		/**
-		 * This method returns the center of the tube.
 		 * @return the center of the tube
 		 */
 		public Vector3f getCenter() {

+ 76 - 55
engine/src/blender/com/jme3/scene/plugins/blender/textures/UVProjectionGenerator.java

@@ -5,10 +5,7 @@ import com.jme3.bounding.BoundingSphere;
 import com.jme3.math.FastMath;
 import com.jme3.math.Triangle;
 import com.jme3.math.Vector3f;
-import com.jme3.scene.Mesh;
-import com.jme3.scene.VertexBuffer;
 import com.jme3.scene.plugins.blender.textures.UVCoordinatesGenerator.BoundingTube;
-import java.nio.FloatBuffer;
 
 /**
  * This class helps with projection calculations.
@@ -16,6 +13,32 @@ import java.nio.FloatBuffer;
  * @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.
 	 * 
@@ -25,18 +48,14 @@ import java.nio.FloatBuffer;
 	 *            the bounding box for projecting
 	 * @return UV coordinates after the projection
 	 */
-	public static float[] flatProjection(Mesh mesh, BoundingBox bb) {
-		if (bb == null) {
-			bb = UVCoordinatesGenerator.getBoundingBox(mesh);
-		}
+	public static float[] flatProjection(float[] positions, BoundingBox bb) {
 		Vector3f min = bb.getMin(null);
-		float[] ext = new float[] { bb.getXExtent() * 2.0f, bb.getYExtent() * 2.0f };
-		FloatBuffer positions = mesh.getFloatBuffer(VertexBuffer.Type.Position);
-		float[] uvCoordinates = new float[positions.limit() / 3 * 2];
-		for (int i = 0, j = 0; i < positions.limit(); i += 3, j += 2) {
-			uvCoordinates[j] = (positions.get(i) - min.x) / ext[0];
-			uvCoordinates[j + 1] = (positions.get(i + 1) - min.y) / ext[1];
-			// skip the Z-coordinate
+		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;
 	}
@@ -44,13 +63,13 @@ import java.nio.FloatBuffer;
 	/**
 	 * Cube projection for 2D textures.
 	 * 
-	 * @param mesh
-	 *            mesh that is to be projected
+	 * @param positions
+	 *            points to be projected
 	 * @param bb
 	 *            the bounding box for projecting
 	 * @return UV coordinates after the projection
 	 */
-	public static float[] cubeProjection(Mesh mesh, BoundingBox bb) {
+	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);
@@ -58,10 +77,12 @@ import java.nio.FloatBuffer;
 		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[mesh.getTriangleCount() * 6];// 6 == 3 * 2
+		float[] uvCoordinates = new float[positions.length/3*2];
 		float borderAngle = (float) Math.sqrt(2.0f) / 2.0f;
-		for (int i = 0, pointIndex = 0; i < mesh.getTriangleCount(); ++i) {
-			mesh.getTriangle(i, triangle);
+		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));
@@ -107,23 +128,22 @@ import java.nio.FloatBuffer;
 	/**
 	 * Tube projection for 2D textures.
 	 * 
-	 * @param mesh
-	 *            mesh that is to be projected
+	 * @param positions
+	 *            points to be projected
 	 * @param bt
 	 *            the bounding tube for projecting
 	 * @return UV coordinates after the projection
 	 */
-	public static float[] tubeProjection(Mesh mesh, BoundingTube bt) {
-		FloatBuffer positions = mesh.getFloatBuffer(VertexBuffer.Type.Position);
-		float[] uvCoordinates = new float[positions.limit() / 3 * 2];
+	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, cy = bt.getCenter().y;
-		Vector3f uBase = new Vector3f(0, -1, 0);
+		float cx = bt.getCenter().x, cz = bt.getCenter().z;
+		Vector3f uBase = new Vector3f(0, 0, -1);
 		
-		float vBase = bt.getCenter().z - bt.getHeight() * 0.5f;
-		for (int i = 0, j = 0; i < positions.limit(); i += 3, j += 2) {
+		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.get(i)-cx, positions.get(i + 1)-cy, 0);
+			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
@@ -132,31 +152,32 @@ import java.nio.FloatBuffer;
 			uvCoordinates[j] = angle / FastMath.TWO_PI;
 
 			// calculating V
-			float z = positions.get(i + 2);
-			uvCoordinates[j + 1] = (z - vBase) / bt.getHeight();
+			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<mesh.getTriangleCount();++i) {
-			mesh.getTriangle(i, 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);
+			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
-				//indexOfUcoord = (indexOfTriangle*3 + indexOfTrianglesVertex)*2
 				if(sgn1==1.0f) {
-					uvCoordinates[i*3*2] += 1.0f;
+					uvCoordinates[i/3*2] += 1.0f;
 				}
 				if(sgn2==1.0f) {
-					uvCoordinates[(i*3+1)*2] += 1.0f;
+					uvCoordinates[(i/3+1)*2] += 1.0f;
 				}
 				if(sgn3==1.0f) {
-					uvCoordinates[(i*3+2)*2] += 1.0f;
+					uvCoordinates[(i/3+2)*2] += 1.0f;
 				}
 			}
 		}
@@ -166,23 +187,22 @@ import java.nio.FloatBuffer;
 	/**
 	 * Sphere projection for 2D textures.
 	 * 
-	 * @param mesh
-	 *            mesh that is to be projected
+	 * @param positions
+	 *            points to be projected
 	 * @param bb
 	 *            the bounding box for projecting
 	 * @return UV coordinates after the projection
 	 */
-	public static float[] sphereProjection(Mesh mesh, BoundingSphere bs) {
-		FloatBuffer positions = mesh.getFloatBuffer(VertexBuffer.Type.Position);
-		float[] uvCoordinates = new float[positions.limit() / 3 * 2];
+	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.limit(); i += 3, j += 2) {
+		for (int i = 0, j = 0; i < positions.length; i += 3, j += 2) {
 			// calculating U
-			v.set(positions.get(i)-cx, positions.get(i + 1)-cy, 0);
+			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
@@ -191,7 +211,7 @@ import java.nio.FloatBuffer;
 			uvCoordinates[j] = angle / FastMath.TWO_PI;
 
 			// calculating V
-			v.set(positions.get(i)-cx, positions.get(i + 1)-cy, positions.get(i + 2)-cz);
+			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;
@@ -199,8 +219,10 @@ import java.nio.FloatBuffer;
 		
 		//looking for splitted triangles
 		Triangle triangle = new Triangle();
-		for(int i=0;i<mesh.getTriangleCount();++i) {
-			mesh.getTriangle(i, 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);
@@ -209,15 +231,14 @@ import java.nio.FloatBuffer;
 					   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
-				//indexOfUcoord = (indexOfTriangle*3 + indexOfTrianglesVertex)*2
 				if(sgn1==1.0f) {
-					uvCoordinates[i*3*2] += 1.0f;
+					uvCoordinates[i/3*2] += 1.0f;
 				}
 				if(sgn2==1.0f) {
-					uvCoordinates[(i*3+1)*2] += 1.0f;
+					uvCoordinates[(i/3+1)*2] += 1.0f;
 				}
 				if(sgn3==1.0f) {
-					uvCoordinates[(i*3+2)*2] += 1.0f;
+					uvCoordinates[(i/3+2)*2] += 1.0f;
 				}
 			}
 		}

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

@@ -1,6 +1,7 @@
 package com.jme3.scene.plugins.blender.textures.blending;
 
-import com.jme3.math.FastMath;
+import java.util.logging.Logger;
+
 import com.jme3.scene.plugins.blender.BlenderContext;
 import com.jme3.scene.plugins.blender.materials.MaterialHelper;
 
@@ -11,113 +12,22 @@ import com.jme3.scene.plugins.blender.materials.MaterialHelper;
  * @author Marcin Roguski (Kaelthas)
  */
 /* package */abstract class AbstractTextureBlender implements TextureBlender {
-	/**
-	 * 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 blendFactor
-	 *            the blending factor
-	 * @param blendtype
-	 *            the blending type
-	 * @param blenderContext
-	 *            the blender context
-	 */
-	protected void blendPixel(float[] result, float[] materialColor, float[] pixelColor, float blendFactor, int blendtype, BlenderContext blenderContext) {
-		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);
-		}
+	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;
 	}
 
 	/**
@@ -139,8 +49,7 @@ import com.jme3.scene.plugins.blender.materials.MaterialHelper;
 		MaterialHelper materialHelper = blenderContext.getHelper(MaterialHelper.class);
 
 		switch (type) {
-			case MTEX_BLEND_HUE: {// FIXME: not working well for image textures
-									// (works fine for generated textures)
+			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) {
@@ -161,7 +70,7 @@ import com.jme3.scene.plugins.blender.materials.MaterialHelper;
 				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);
+					materialHelper.hsvToRgb(h, oneMinusFactor * s + fac * colorTransformResult[1], v, materialRGB);
 				}
 				break;
 			}
@@ -170,12 +79,10 @@ import com.jme3.scene.plugins.blender.materials.MaterialHelper;
 				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);
+				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)
+			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);
@@ -192,4 +99,18 @@ import com.jme3.scene.plugins.blender.materials.MaterialHelper;
 				throw new IllegalStateException("Unknown ramp type: " + type);
 		}
 	}
+	
+	@Override
+	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());
+		}
+	}
 }

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

@@ -1,7 +1,7 @@
 package com.jme3.scene.plugins.blender.textures.blending;
 
 import com.jme3.scene.plugins.blender.BlenderContext;
-import com.jme3.texture.Texture;
+import com.jme3.texture.Image;
 
 /**
  * An interface for texture blending classes (the classes that mix the texture
@@ -32,20 +32,22 @@ public interface TextureBlender {
 	 * color in 'map to' panel. As a result of this method a new texture is
 	 * created. The input texture is NOT.
 	 * 
-	 * @param materialColor
-	 *            the material diffuse color
-	 * @param texture
-	 *            the texture we use in blending
-	 * @param color
-	 *            the color defined for the texture
-	 * @param affectFactor
-	 *            the factor that the color affects the texture (value form 0.0
-	 *            to 1.0)
-	 * @param blendType
-	 *            the blending type
+	 * @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 texture that was created after the blending
+	 * @return new image that was created after the blending
 	 */
-	Texture blend(float[] materialColor, Texture texture, float[] color, float affectFactor, int blendType, boolean neg, BlenderContext blenderContext);
+	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);
 }

+ 149 - 99
engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderAWT.java

@@ -2,14 +2,13 @@ package com.jme3.scene.plugins.blender.textures.blending;
 
 import java.nio.ByteBuffer;
 import java.util.ArrayList;
-import java.util.logging.Level;
-import java.util.logging.Logger;
 
+import com.jme3.math.FastMath;
 import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.textures.TexturePixel;
+import com.jme3.scene.plugins.blender.textures.io.PixelIOFactory;
+import com.jme3.scene.plugins.blender.textures.io.PixelInputOutput;
 import com.jme3.texture.Image;
-import com.jme3.texture.Texture;
-import com.jme3.texture.Texture2D;
-import com.jme3.texture.Texture3D;
 import com.jme3.texture.Image.Format;
 import com.jme3.util.BufferUtils;
 
@@ -36,127 +35,178 @@ import com.jme3.util.BufferUtils;
  * @author Marcin Roguski (Kaelthas)
  */
 public class TextureBlenderAWT extends AbstractTextureBlender {
-	private static final Logger	LOGGER	= Logger.getLogger(TextureBlenderAWT.class.getName());
-
+	public TextureBlenderAWT(int flag, boolean negateTexture, int blendType, float[] materialColor, float[] color, float blendFactor) {
+		super(flag, negateTexture, blendType, materialColor, color, blendFactor);
+	}
+	
 	@Override
-	public Texture blend(float[] materialColor, Texture texture, float[] color, float affectFactor, int blendType, boolean neg, BlenderContext blenderContext) {
+	public Image blend(Image image, Image baseImage, BlenderContext blenderContext) {
 		float[] pixelColor = new float[] { color[0], color[1], color[2], 1.0f };
-		Format format = texture.getImage().getFormat();
-		ByteBuffer data = texture.getImage().getData(0);
-		data.rewind();
-
-		int width = texture.getImage().getWidth();
-		int height = texture.getImage().getHeight();
-		int depth = texture.getImage().getDepth();
+		Format format = image.getFormat();
+		ByteBuffer data = image.getData(0);
+		
+		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;
 		}
 		ByteBuffer newData = BufferUtils.createByteBuffer(width * height * depth * 4);
-
+		
 		float[] resultPixel = new float[4];
-		int dataIndex = 0;
-		while (data.hasRemaining()) {
-			this.setupMaterialColor(data, format, neg, pixelColor);
-			this.blendPixel(resultPixel, materialColor, pixelColor, affectFactor, blendType, blenderContext);
+		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, basePixel, x, y);
+				basePixel.toRGBA(materialColor);
+			}			
+			
+			//reading the current texture's pixel
+			pixelReader.read(image, 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;
+			}
 		}
-		if (texture.getType() == Texture.Type.TwoDimensional) {
-			return new Texture2D(new Image(Format.RGBA8, width, height, newData));
-		} else {
+		if(depth > 1) {
 			ArrayList<ByteBuffer> dataArray = new ArrayList<ByteBuffer>(1);
 			dataArray.add(newData);
-			return new Texture3D(new Image(Format.RGBA8, width, height, depth, dataArray));
+			return new Image(Format.RGBA8, width, height, depth, dataArray);
+		} else {
+			return new Image(Format.RGBA8, width, height, newData);
 		}
 	}
 
 	/**
-	 * This method alters the material color in a way dependent on the type of
-	 * the image. For example the color remains untouched if the texture is of
-	 * Luminance type. The luminance defines the interaction between the
-	 * material color and color defined for texture blending. If the type has 3
-	 * or more color channels then the material color is replaced with the
-	 * texture's color and later blended with the defined blend color. All alpha
-	 * values (if present) are ignored and not used during blending.
+	 * This method blends the single pixel depending on the blending type.
 	 * 
-	 * @param data
-	 *            the image data
-	 * @param imageFormat
-	 *            the format of the image
-	 * @param neg
-	 *            defines it the result color should be nagated
+	 * @param result
+	 *            the result pixel
 	 * @param materialColor
-	 *            the material's color (value may be changed)
-	 * @return texture intensity for the current pixel
+	 *            the material color
+	 * @param pixelColor
+	 *            the pixel color
+	 * @param blendFactor
+	 *            the blending factor
+	 * @param blendtype
+	 *            the blending type
+	 * @param blenderContext
+	 *            the blender context
 	 */
-	protected float setupMaterialColor(ByteBuffer data, Format imageFormat, boolean neg, float[] materialColor) {
-		float tin = 0.0f;
-		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 RGBA8:
-				materialColor[0] = firstPixelValue;
-				pixelValue = data.get();
-				materialColor[1] = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
-				pixelValue = data.get();
-				materialColor[2] = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
-				pixelValue = data.get();
-				materialColor[3] = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
+	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 ABGR8:
-				materialColor[3] = firstPixelValue;
-				pixelValue = data.get();
-				materialColor[2] = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
-				pixelValue = data.get();
-				materialColor[1] = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
-				pixelValue = data.get();
-				materialColor[0] = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
+			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 BGR8:
-				materialColor[2] = firstPixelValue;
-				pixelValue = data.get();
-				materialColor[1] = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
-				pixelValue = data.get();
-				materialColor[0] = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
-				materialColor[3] = 1.0f;
+			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 RGB8:
-				materialColor[0] = firstPixelValue;
-				pixelValue = data.get();
-				materialColor[1] = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
-				pixelValue = data.get();
-				materialColor[2] = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
-				materialColor[3] = 1.0f;
+			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 ARGB4444:
-			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:// TODO: implement these textures
-				LOGGER.log(Level.WARNING, "Image type not yet supported for blending: {0}", imageFormat);
+			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("Invalid image format type for AWT texture blender: " + imageFormat);
-		}
-		if (neg) {
-			materialColor[0] = 1.0f - materialColor[0];
-			materialColor[1] = 1.0f - materialColor[1];
-			materialColor[2] = 1.0f - materialColor[2];
+				throw new IllegalStateException("Unknown blend type: " + blendType);
 		}
-		// Blender formula for texture intensity calculation:
-		// 0.35*texres.tr+0.45*texres.tg+0.2*texres.tb
-		tin = 0.35f * materialColor[0] + 0.45f * materialColor[1] + 0.2f * materialColor[2];
-		return tin;
 	}
 }

+ 20 - 19
engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderDDS.java

@@ -11,9 +11,6 @@ import com.jme3.scene.plugins.blender.BlenderContext;
 import com.jme3.scene.plugins.blender.textures.TexturePixel;
 import com.jme3.texture.Image;
 import com.jme3.texture.Image.Format;
-import com.jme3.texture.Texture;
-import com.jme3.texture.Texture2D;
-import com.jme3.texture.Texture3D;
 import com.jme3.util.BufferUtils;
 
 /**
@@ -25,18 +22,23 @@ import com.jme3.util.BufferUtils;
  * <li> DXT1A:
  * @author Marcin Roguski (Kaelthas)
  */
-public class TextureBlenderDDS extends AbstractTextureBlender {
+public class TextureBlenderDDS extends TextureBlenderAWT {
 	private static final Logger	LOGGER	= Logger.getLogger(TextureBlenderDDS.class.getName());
 
+	public TextureBlenderDDS(int flag, boolean negateTexture, int blendType, float[] materialColor, float[] color, float blendFactor) {
+		super(flag, negateTexture, blendType, materialColor, color, blendFactor);
+	}
+	
+	//TODO: implement using base texture
 	@Override
-	public Texture blend(float[] materialColor, Texture texture, float[] color, float affectFactor, int blendType, boolean neg, BlenderContext blenderContext) {
-		Format format = texture.getImage().getFormat();
-		ByteBuffer data = texture.getImage().getData(0);
+	public Image blend(Image image, Image baseImage, BlenderContext blenderContext) {
+		Format format = image.getFormat();
+		ByteBuffer data = image.getData(0);
 		data.rewind();
 
-		int width = texture.getImage().getWidth();
-		int height = texture.getImage().getHeight();
-		int depth = texture.getImage().getDepth();
+		int width = image.getWidth();
+		int height = image.getHeight();
+		int depth = image.getDepth();
 		if (depth == 0) {
 			depth = 1;
 		}
@@ -50,9 +52,7 @@ public class TextureBlenderDDS extends AbstractTextureBlender {
 			switch (format) {
 				case DXT3:
 				case DXT5:
-					newData.putLong(dataIndex, data.getLong());// just copy the
-																// 8 bytes of
-																// alphas
+					newData.putLong(dataIndex, data.getLong());// just copy the 8 bytes of alphas
 					dataIndex += 8;
 				case DXT1:
 					int col0 = RGB565.RGB565_to_ARGB8(data.getShort());
@@ -69,11 +69,11 @@ public class TextureBlenderDDS extends AbstractTextureBlender {
 
 			// blending colors
 			for (int i = 0; i < colors.length; ++i) {
-				if (neg) {
+				if (negateTexture) {
 					colors[i].negate();
 				}
 				colors[i].toRGBA(pixelColor);
-				this.blendPixel(resultPixel, materialColor, pixelColor, affectFactor, blendType, blenderContext);
+				this.blendPixel(resultPixel, materialColor, pixelColor, blenderContext);
 				colors[i].fromARGB8(1, resultPixel[0], resultPixel[1], resultPixel[2]);
 				int argb8 = colors[i].toARGB8();
 				short rgb565 = RGB565.ARGB8_to_RGB565(argb8);
@@ -85,12 +85,13 @@ public class TextureBlenderDDS extends AbstractTextureBlender {
 			newData.putInt(dataIndex, data.getInt());
 			dataIndex += 4;
 		}
-		if (texture.getType() == Texture.Type.TwoDimensional) {
-			return new Texture2D(new Image(format, width, height, newData));
-		} else {
+		
+		if(depth > 1) {
 			ArrayList<ByteBuffer> dataArray = new ArrayList<ByteBuffer>(1);
 			dataArray.add(newData);
-			return new Texture3D(new Image(format, width, height, depth, dataArray));
+			return new Image(format, width, height, depth, dataArray);
+		} else {
+			return new Image(format, width, height, newData);
 		}
 	}
 }

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

@@ -35,7 +35,7 @@ import java.util.logging.Level;
 import java.util.logging.Logger;
 
 import com.jme3.scene.plugins.blender.BlenderContext;
-import com.jme3.texture.Texture;
+import com.jme3.texture.Image;
 import com.jme3.texture.Image.Format;
 
 /**
@@ -53,7 +53,7 @@ public class TextureBlenderFactory {
 	 *            the texture format
 	 * @return texture blending class
 	 */
-	public static TextureBlender createTextureBlender(Format format) {
+	public static TextureBlender createTextureBlender(Format format, int flag, boolean negate, int blendType, float[] materialColor, float[] color, float colfac) {
 		switch (format) {
 			case Luminance8:
 			case Luminance8Alpha8:
@@ -62,7 +62,7 @@ public class TextureBlenderFactory {
 			case Luminance16F:
 			case Luminance16FAlpha16F:
 			case Luminance32F:
-				return new TextureBlenderLuminance();
+				return new TextureBlenderLuminance(flag, negate, blendType, materialColor, color, colfac);
 			case RGBA8:
 			case ABGR8:
 			case BGR8:
@@ -80,12 +80,12 @@ public class TextureBlenderFactory {
 			case RGBA16:
 			case RGBA16F:
 			case RGBA32F:
-				return new TextureBlenderAWT();
+				return new TextureBlenderAWT(flag, negate, blendType, materialColor, color, colfac);
 			case DXT1:
 			case DXT1A:
 			case DXT3:
 			case DXT5:
-				return new TextureBlenderDDS();
+				return new TextureBlenderDDS(flag, negate, blendType, materialColor, color, colfac);
 			case Alpha16:
 			case Alpha8:
 			case ARGB4444:
@@ -101,12 +101,31 @@ public class TextureBlenderFactory {
 				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() {
 					@Override
-					public Texture blend(float[] materialColor, Texture texture, float[] color, float affectFactor, int blendType, boolean neg, BlenderContext blenderContext) {
-						return texture;
+					public Image blend(Image image, Image baseImage, BlenderContext blenderContext) {
+						return image;
+					}
+
+					@Override
+					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;
+	}
 }

+ 45 - 19
engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderLuminance.java

@@ -7,10 +7,10 @@ import java.util.logging.Logger;
 
 import com.jme3.math.FastMath;
 import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.textures.TexturePixel;
+import com.jme3.scene.plugins.blender.textures.io.PixelIOFactory;
+import com.jme3.scene.plugins.blender.textures.io.PixelInputOutput;
 import com.jme3.texture.Image;
-import com.jme3.texture.Texture;
-import com.jme3.texture.Texture2D;
-import com.jme3.texture.Texture3D;
 import com.jme3.texture.Image.Format;
 import com.jme3.util.BufferUtils;
 
@@ -29,15 +29,28 @@ import com.jme3.util.BufferUtils;
 public class TextureBlenderLuminance extends AbstractTextureBlender {
 	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);
+	}
+	
 	@Override
-	public Texture blend(float[] materialColor, Texture texture, float[] color, float affectFactor, int blendType, boolean neg, BlenderContext blenderContext) {
-		Format format = texture.getImage().getFormat();
-		ByteBuffer data = texture.getImage().getData(0);
+	public Image blend(Image image, Image baseImage, BlenderContext blenderContext) {
+		Format format = image.getFormat();
+		ByteBuffer data = image.getData(0);
 		data.rewind();
 
-		int width = texture.getImage().getWidth();
-		int height = texture.getImage().getHeight();
-		int depth = texture.getImage().getDepth();
+		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;
 		}
@@ -45,21 +58,34 @@ public class TextureBlenderLuminance extends AbstractTextureBlender {
 
 		float[] resultPixel = new float[4];
 		float[] tinAndAlpha = new float[2];
-		int dataIndex = 0;
+		int dataIndex = 0, x = 0, y = 0;
 		while (data.hasRemaining()) {
-			this.getTinAndAlpha(data, format, neg, tinAndAlpha);
-			this.blendPixel(resultPixel, materialColor, color, tinAndAlpha[0], affectFactor, blendType, blenderContext);
+			//getting the proper material color if the base texture is applied
+			if(basePixelIO != null) {
+				basePixelIO.read(baseImage, 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;
+			}
 		}
-		if (texture.getType() == Texture.Type.TwoDimensional) {
-			return new Texture2D(new Image(Format.RGBA8, width, height, newData));
-		} else {
+		
+		if(depth > 1) {
 			ArrayList<ByteBuffer> dataArray = new ArrayList<ByteBuffer>(1);
 			dataArray.add(newData);
-			return new Texture3D(new Image(Format.RGBA8, width, height, depth, dataArray));
+			return new Image(Format.RGBA8, width, height, depth, dataArray);
+		} else {
+			return new Image(Format.RGBA8, width, height, newData);
 		}
 	}
 
@@ -77,7 +103,7 @@ public class TextureBlenderLuminance extends AbstractTextureBlender {
 	 */
 	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;
+		float firstPixelValue = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - ~pixelValue / 255.0f;
 		switch (imageFormat) {
 			case Luminance8:
 				result[0] = neg ? 1.0f - firstPixelValue : firstPixelValue;
@@ -86,7 +112,7 @@ public class TextureBlenderLuminance extends AbstractTextureBlender {
 			case Luminance8Alpha8:
 				result[0] = neg ? 1.0f - firstPixelValue : firstPixelValue;
 				pixelValue = data.get();
-				result[1] = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
+				result[1] = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - ~pixelValue / 255.0f;
 				break;
 			case Luminance16:
 			case Luminance16Alpha16:
@@ -96,7 +122,7 @@ public class TextureBlenderLuminance extends AbstractTextureBlender {
 				LOGGER.log(Level.WARNING, "Image type not yet supported for blending: {0}", imageFormat);
 				break;
 			default:
-				throw new IllegalStateException("Invalid image format type for DDS texture blender: " + imageFormat);
+				throw new IllegalStateException("Invalid image format type for Luminance texture blender: " + imageFormat);
 		}
 	}
 

+ 4 - 15
engine/src/blender/com/jme3/scene/plugins/blender/textures/NoiseGenerator.java → engine/src/blender/com/jme3/scene/plugins/blender/textures/generating/NoiseGenerator.java

@@ -29,13 +29,14 @@
  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
-package com.jme3.scene.plugins.blender.textures;
+package com.jme3.scene.plugins.blender.textures.generating;
 
 import com.jme3.math.FastMath;
 import com.jme3.scene.plugins.blender.AbstractBlenderHelper;
 import com.jme3.scene.plugins.blender.BlenderContext;
 import com.jme3.scene.plugins.blender.file.Structure;
-import com.jme3.scene.plugins.blender.textures.TextureGeneratorMusgrave.MusgraveData;
+import com.jme3.scene.plugins.blender.textures.generating.TextureGeneratorMusgrave.MusgraveData;
+
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.ObjectInputStream;
@@ -54,18 +55,6 @@ import java.util.logging.Logger;
 /*package*/ class NoiseGenerator extends AbstractBlenderHelper {
     private static final Logger LOGGER = Logger.getLogger(NoiseGenerator.class.getName());
     
-    // flag
-    protected static final int TEX_COLORBAND = 1;
-    protected static final int TEX_FLIPBLEND = 2;
-    protected static final int TEX_NEGALPHA = 4;
-    protected static final int TEX_CHECKER_ODD = 8;
-    protected static final int TEX_CHECKER_EVEN = 16;
-    protected static final int TEX_PRV_ALPHA = 32;
-    protected static final int TEX_PRV_NOR = 64;
-    protected static final int TEX_REPEAT_XMIR = 128;
-    protected static final int TEX_REPEAT_YMIR = 256;
-    protected static final int TEX_FLAG_MASK = TEX_COLORBAND | TEX_FLIPBLEND | TEX_NEGALPHA | TEX_CHECKER_ODD | TEX_CHECKER_EVEN | TEX_PRV_ALPHA | TEX_PRV_NOR | TEX_REPEAT_XMIR | TEX_REPEAT_YMIR;
-
     // tex->stype
     protected static final int TEX_PLASTIC = 0;
     protected static final int TEX_WALLIN = 1;
@@ -575,7 +564,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] = 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) {

+ 129 - 0
engine/src/blender/com/jme3/scene/plugins/blender/textures/generating/TextureGenerator.java

@@ -0,0 +1,129 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.scene.plugins.blender.textures.generating;
+
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.file.Structure;
+import com.jme3.scene.plugins.blender.textures.ColorBand;
+import com.jme3.scene.plugins.blender.textures.TexturePixel;
+import com.jme3.texture.Image.Format;
+
+/**
+ * This class is a base class for texture generators.
+ * @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;
+	}
+
+	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;
+        if (texres.green < 0.0f) {
+            texres.green = 0.0f;
+        }
+        texres.blue = (texres.blue - 0.5f) * bacd.contrast + bacd.brightness;
+        if (texres.blue < 0.0f) {
+            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) {
+        texres.intensity = (texres.intensity - 0.5f) * contrast + brightness;
+        if (texres.intensity < 0.0f) {
+            texres.intensity = 0.0f;
+        } else if (texres.intensity > 1.0f) {
+            texres.intensity = 1.0f;
+        }
+    }
+	
+	/**
+	 * 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
+         */
+		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();
+		}
+	}
+}

+ 24 - 54
engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorBlend.java → engine/src/blender/com/jme3/scene/plugins/blender/textures/generating/TextureGeneratorBlend.java

@@ -29,18 +29,13 @@
  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
-package com.jme3.scene.plugins.blender.textures;
+package com.jme3.scene.plugins.blender.textures.generating;
 
 import com.jme3.math.FastMath;
 import com.jme3.scene.plugins.blender.BlenderContext;
 import com.jme3.scene.plugins.blender.file.Structure;
-import com.jme3.texture.Image;
+import com.jme3.scene.plugins.blender.textures.TexturePixel;
 import com.jme3.texture.Image.Format;
-import com.jme3.texture.Texture;
-import com.jme3.texture.Texture3D;
-import com.jme3.util.BufferUtils;
-import java.nio.ByteBuffer;
-import java.util.ArrayList;
 
 /**
  * This class generates the 'blend' texture.
@@ -110,58 +105,33 @@ public final class TextureGeneratorBlend extends TextureGenerator {
 	 *        the noise generator
 	 */
 	public TextureGeneratorBlend(NoiseGenerator noiseGenerator) {
-		super(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
-	protected Texture generate(Structure tex, int width, int height, int depth, BlenderContext blenderContext) {
-		int flag = ((Number) tex.getFieldValue("flag")).intValue();
-		int stype = ((Number) tex.getFieldValue("stype")).intValue();
-		TexturePixel texres = new TexturePixel();
-		int halfW = width >> 1, halfH = height >> 1, halfD = depth >> 1, index = 0;
-		float wDelta = 1.0f / halfW, hDelta = 1.0f / halfH, dDelta = 1.0f / halfD, x, y;
-		float[][] colorBand = this.computeColorband(tex, blenderContext);
-		BrightnessAndContrastData bacd = new BrightnessAndContrastData(tex);
-		Format format = colorBand != null ? Format.RGBA8 : Format.Luminance8;
-		int bytesPerPixel = colorBand != null ? 4 : 1;
-		boolean flipped = (flag & NoiseGenerator.TEX_FLIPBLEND) != 0;
+	public void getPixel(TexturePixel pixel, float x, float y, float z) {
+		pixel.intensity = INTENSITY_FUNCTION[stype].getIntensity(x, y, z);
 		
-		byte[] data = new byte[width * height * depth * bytesPerPixel];
-		for (int i = -halfW; i < halfW; ++i) {
-			x = wDelta * i;
-			for (int j = -halfH; j < halfH; ++j) {
-				if (flipped) {
-					y = x;
-					x = hDelta * j;
-				} else {
-					y = hDelta * j;
-				}
-				for (int k = -halfD; k < halfD; ++k) {
-					texres.intensity = INTENSITY_FUNCTION[stype].getIntensity(x, y, dDelta * k);
-					
-					if (colorBand != null) {
-						int colorbandIndex = (int) (texres.intensity * 1000.0f);
-						texres.red = colorBand[colorbandIndex][0];
-						texres.green = colorBand[colorbandIndex][1];
-						texres.blue = colorBand[colorbandIndex][2];
-						
-						this.applyBrightnessAndContrast(bacd, texres);
-						data[index++] = (byte) (texres.red * 255.0f);
-						data[index++] = (byte) (texres.green * 255.0f);
-						data[index++] = (byte) (texres.blue * 255.0f);
-						data[index++] = (byte) (colorBand[colorbandIndex][3] * 255.0f);
-					} else {
-						this.applyBrightnessAndContrast(texres, bacd.contrast, bacd.brightness);
-						data[index++] = (byte) (texres.intensity * 255.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);
 		}
-		ArrayList<ByteBuffer> dataArray = new ArrayList<ByteBuffer>(1);
-		dataArray.add(BufferUtils.createByteBuffer(data));
-		return new Texture3D(new Image(format, width, height, depth, dataArray));
 	}
-	
+
 	private static interface IntensityFunction {
 		float getIntensity(float x, float y, float z);
 	}

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

@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.scene.plugins.blender.textures.generating;
+
+import com.jme3.math.FastMath;
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.file.Structure;
+import com.jme3.scene.plugins.blender.textures.TexturePixel;
+import com.jme3.texture.Image.Format;
+
+/**
+ * This class generates the 'clouds' texture.
+ * @author Marcin Roguski (Kaelthas)
+ */
+public class TextureGeneratorClouds extends TextureGenerator {
+	// 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);
+		}
+	}
+}

+ 31 - 55
engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorDistnoise.java → engine/src/blender/com/jme3/scene/plugins/blender/textures/generating/TextureGeneratorDistnoise.java

@@ -29,83 +29,59 @@
  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
-package com.jme3.scene.plugins.blender.textures;
+package com.jme3.scene.plugins.blender.textures.generating;
 
 import com.jme3.math.FastMath;
 import com.jme3.scene.plugins.blender.BlenderContext;
 import com.jme3.scene.plugins.blender.file.Structure;
-import com.jme3.scene.plugins.blender.textures.NoiseGenerator.NoiseFunction;
-import com.jme3.texture.Image;
+import com.jme3.scene.plugins.blender.textures.TexturePixel;
+import com.jme3.scene.plugins.blender.textures.generating.NoiseGenerator.NoiseFunction;
 import com.jme3.texture.Image.Format;
-import com.jme3.texture.Texture;
-import com.jme3.texture.Texture3D;
-import com.jme3.util.BufferUtils;
-import java.nio.ByteBuffer;
-import java.util.ArrayList;
 
 /**
  * This class generates the 'distorted noise' texture.
  * @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);
+		super(noiseGenerator, Format.Luminance8);
 	}
-
+	
 	@Override
-	protected Texture generate(Structure tex, int width, int height, int depth, BlenderContext blenderContext) {
-		float noisesize = ((Number) tex.getFieldValue("noisesize")).floatValue();
-		float distAmount = ((Number) tex.getFieldValue("dist_amount")).floatValue();
-		int noisebasis = ((Number) tex.getFieldValue("noisebasis")).intValue();
-		int noisebasis2 = ((Number) tex.getFieldValue("noisebasis2")).intValue();
-
-		TexturePixel texres = new TexturePixel();
-		float[] texvec = new float[] { 0, 0, 0 };
-		int halfW = width >> 1, halfH = height >> 1, halfD = depth >> 1, index = 0;
-		float wDelta = 1.0f / halfW, hDelta = 1.0f / halfH, dDelta = 1.0f / halfD;
-		float[][] colorBand = this.computeColorband(tex, blenderContext);
-		Format format = colorBand != null ? Format.RGBA8 : Format.Luminance8;
-		int bytesPerPixel = colorBand != null ? 4 : 1;
-		BrightnessAndContrastData bacd = new BrightnessAndContrastData(tex);
-		
-		byte[] data = new byte[width * height * depth * bytesPerPixel];
-		for (int i = -halfW; i < halfW; ++i) {
-			texvec[0] = wDelta * i / noisesize;
-			for (int j = -halfH; j < halfH; ++j) {
-				texvec[1] = hDelta * j / noisesize;
-				for (int k = -halfD; k < halfD; ++k) {
-					texvec[2] = dDelta * k;
-					texres.intensity = this.musgraveVariableLunacrityNoise(texvec[0], texvec[1], texvec[2], distAmount, noisebasis, noisebasis2);
-					texres.intensity = FastMath.clamp(texres.intensity, 0.0f, 1.0f);
-					if (colorBand != null) {
-						int colorbandIndex = (int) (texres.intensity * 1000.0f);
-						texres.red = colorBand[colorbandIndex][0];
-						texres.green = colorBand[colorbandIndex][1];
-						texres.blue = colorBand[colorbandIndex][2];
+	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, texres);
-						data[index++] = (byte) (texres.red * 255.0f);
-						data[index++] = (byte) (texres.green * 255.0f);
-						data[index++] = (byte) (texres.blue * 255.0f);
-						data[index++] = (byte) (colorBand[colorbandIndex][3] * 255.0f);
-					} else {
-						this.applyBrightnessAndContrast(texres, bacd.contrast, bacd.brightness);
-						data[index++] = (byte) (texres.intensity * 255.0f);
-					}
-				}
-			}
+			this.applyBrightnessAndContrast(bacd, pixel);
+		} else {
+			this.applyBrightnessAndContrast(pixel, bacd.contrast, bacd.brightness);
 		}
-		ArrayList<ByteBuffer> dataArray = new ArrayList<ByteBuffer>(1);
-		dataArray.add(BufferUtils.createByteBuffer(data));
-		return new Texture3D(new Image(format, width, height, depth, dataArray));
 	}
-	
+
 	/**
      * "Variable Lacunarity Noise" A distorted variety of Perlin noise. This method is used to calculate distorted noise
      * texture.

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

@@ -0,0 +1,39 @@
+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);
+		}
+	}
+}

+ 46 - 67
engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorMagic.java → engine/src/blender/com/jme3/scene/plugins/blender/textures/generating/TextureGeneratorMagic.java

@@ -29,18 +29,13 @@
  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
-package com.jme3.scene.plugins.blender.textures;
+package com.jme3.scene.plugins.blender.textures.generating;
 
 import com.jme3.math.FastMath;
 import com.jme3.scene.plugins.blender.BlenderContext;
 import com.jme3.scene.plugins.blender.file.Structure;
-import com.jme3.texture.Image;
+import com.jme3.scene.plugins.blender.textures.TexturePixel;
 import com.jme3.texture.Image.Format;
-import com.jme3.texture.Texture;
-import com.jme3.texture.Texture3D;
-import com.jme3.util.BufferUtils;
-import java.nio.ByteBuffer;
-import java.util.ArrayList;
 
 /**
  * This class generates the 'magic' texture.
@@ -111,78 +106,62 @@ public class TextureGeneratorMagic extends TextureGenerator {
 		};
 	}
 	
+	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);
+		super(noiseGenerator, Format.RGBA8);
 	}
-
+	
 	@Override
-	protected Texture generate(Structure tex, int width, int height, int depth, BlenderContext blenderContext) {
-		float xyz[] = new float[3], turb;
-		int noisedepth = ((Number) tex.getFieldValue("noisedepth")).intValue();
-		float turbul = ((Number) tex.getFieldValue("turbul")).floatValue() / 5.0f;
-		float[] texvec = new float[] { 0, 0, 0 };
-		TexturePixel texres = new TexturePixel();
-		int halfW = width >> 1, halfH = height >> 1, halfD = depth >> 1, index = 0;
-		float wDelta = 1.0f / halfW, hDelta = 1.0f / halfH, dDelta = 1.0f / halfD;
-		float[][] colorBand = this.computeColorband(tex, blenderContext);
-		BrightnessAndContrastData bacd = new BrightnessAndContrastData(tex);
-		
-		byte[] data = new byte[width * height * depth * 4];
-		for (int i = -halfW; i < halfW; ++i) {
-			texvec[0] = wDelta * i;
-			for (int j = -halfH; j < halfH; ++j) {
-				texvec[1] = hDelta * j;
-				for (int k = -halfD; k < halfD; ++k) {
-					turb = turbul;
-					texvec[2] = dDelta * k;
-					xyz[0] = (float) Math.sin((texvec[0] + texvec[1] + texvec[2]) * 5.0f);
-					xyz[1] = (float) Math.cos((-texvec[0] + texvec[1] - texvec[2]) * 5.0f);
-					xyz[2] = -(float) Math.cos((-texvec[0] - texvec[1] + texvec[2]) * 5.0f);
-
-					if (colorBand != null) {
-						texres.intensity = FastMath.clamp(0.3333f * (xyz[0] + xyz[1] + xyz[2]), 0.0f, 1.0f);
-						int colorbandIndex = (int) (texres.intensity * 1000.0f);
-						texres.red = colorBand[colorbandIndex][0];
-						texres.green = colorBand[colorbandIndex][1];
-						texres.blue = colorBand[colorbandIndex][2];
-						texres.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);
-							}
-						}
+	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 (turb != 0.0f) {
-							turb *= 2.0f;
-							xyz[0] /= turb;
-							xyz[1] /= turb;
-							xyz[2] /= turb;
-						}
-						texres.red = 0.5f - xyz[0];
-						texres.green = 0.5f - xyz[1];
-						texres.blue = 0.5f - xyz[2];
-						texres.alpha = 1.0f;
-					}
-					this.applyBrightnessAndContrast(bacd, texres);
-					data[index++] = (byte) (texres.red * 255.0f);
-					data[index++] = (byte) (texres.green * 255.0f);
-					data[index++] = (byte) (texres.blue * 255.0f);
-					data[index++] = (byte) (texres.alpha * 255.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;
 		}
-		ArrayList<ByteBuffer> dataArray = new ArrayList<ByteBuffer>(1);
-		dataArray.add(BufferUtils.createByteBuffer(data));
-		return new Texture3D(new Image(Format.RGBA8, width, height, depth, dataArray));
+		this.applyBrightnessAndContrast(bacd, pixel);
 	}
 	
 	private static interface NoiseDepthFunction {

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

@@ -29,17 +29,11 @@
  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
-package com.jme3.scene.plugins.blender.textures;
+package com.jme3.scene.plugins.blender.textures.generating;
 
 import com.jme3.scene.plugins.blender.BlenderContext;
 import com.jme3.scene.plugins.blender.file.Structure;
-import com.jme3.texture.Image;
-import com.jme3.texture.Image.Format;
-import com.jme3.texture.Texture;
-import com.jme3.texture.Texture3D;
-import com.jme3.util.BufferUtils;
-import java.nio.ByteBuffer;
-import java.util.ArrayList;
+import com.jme3.scene.plugins.blender.textures.TexturePixel;
 
 /**
  * This class generates the 'marble' texture.
@@ -51,6 +45,8 @@ public class TextureGeneratorMarble extends TextureGeneratorWood {
     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
@@ -59,50 +55,29 @@ public class TextureGeneratorMarble extends TextureGeneratorWood {
 	public TextureGeneratorMarble(NoiseGenerator noiseGenerator) {
 		super(noiseGenerator);
 	}
-
+	
 	@Override
-	protected Texture generate(Structure tex, int width, int height, int depth, BlenderContext blenderContext) {
-		float[] texvec = new float[] { 0, 0, 0 };
-		TexturePixel texres = new TexturePixel();
-		int halfW = width >> 1, halfH = height >> 1, halfD = depth >> 1, index = 0;
-		float wDelta = 1.0f / halfW, hDelta = 1.0f / halfH, dDelta = 1.0f / halfD;
-		float[][] colorBand = this.computeColorband(tex, blenderContext);
-		Format format = colorBand != null ? Format.RGBA8 : Format.Luminance8;
-		int bytesPerPixel = colorBand != null ? 4 : 1;
-		BrightnessAndContrastData bacd = new BrightnessAndContrastData(tex);
-		MarbleData marbleData = new MarbleData(tex);
-		
-		byte[] data = new byte[width * height * depth * bytesPerPixel];
-		for (int i = -halfW; i < halfW; ++i) {
-			texvec[0] = wDelta * i;
-			for (int j = -halfH; j < halfH; ++j) {
-				texvec[1] = hDelta * j;
-				for (int k = -halfD; k < halfD; ++k) {
-					texvec[2] = dDelta * k;
-					texres.intensity = this.marbleInt(marbleData, texvec[0], texvec[1], texvec[2]);
-					if (colorBand != null) {
-						int colorbandIndex = (int) (texres.intensity * 1000.0f);
-						texres.red = colorBand[colorbandIndex][0];
-						texres.green = colorBand[colorbandIndex][1];
-						texres.blue = colorBand[colorbandIndex][2];
-						
-						this.applyBrightnessAndContrast(bacd, texres);
-						data[index++] = (byte) (texres.red * 255.0f);
-						data[index++] = (byte) (texres.green * 255.0f);
-						data[index++] = (byte) (texres.blue * 255.0f);
-						data[index++] = (byte) (colorBand[colorbandIndex][3] * 255.0f);
-					} else {
-						this.applyBrightnessAndContrast(texres, bacd.contrast, bacd.brightness);
-						data[index++] = (byte) (texres.intensity * 255.0f);
-					}
-				}
-			}
-		}
-		ArrayList<ByteBuffer> dataArray = new ArrayList<ByteBuffer>(1);
-		dataArray.add(BufferUtils.createByteBuffer(data));
-		return new Texture3D(new Image(format, width, height, depth, dataArray));
+	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;
         if (marbleData.waveform > TEX_TRI || marbleData.waveform < TEX_SIN) {

+ 38 - 61
engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorMusgrave.java → engine/src/blender/com/jme3/scene/plugins/blender/textures/generating/TextureGeneratorMusgrave.java

@@ -29,88 +29,65 @@
  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
-package com.jme3.scene.plugins.blender.textures;
+package com.jme3.scene.plugins.blender.textures.generating;
 
 import com.jme3.scene.plugins.blender.BlenderContext;
 import com.jme3.scene.plugins.blender.file.Structure;
-import com.jme3.scene.plugins.blender.textures.NoiseGenerator.MusgraveFunction;
-import com.jme3.texture.Image;
+import com.jme3.scene.plugins.blender.textures.TexturePixel;
+import com.jme3.scene.plugins.blender.textures.generating.NoiseGenerator.MusgraveFunction;
 import com.jme3.texture.Image.Format;
-import com.jme3.texture.Texture;
-import com.jme3.texture.Texture3D;
-import com.jme3.util.BufferUtils;
-import java.nio.ByteBuffer;
-import java.util.ArrayList;
 
 /**
  * This class generates the 'musgrave' texture.
  * @author Marcin Roguski (Kaelthas)
  */
 public class TextureGeneratorMusgrave extends TextureGenerator {
-
+	protected MusgraveData musgraveData;
+	protected MusgraveFunction musgraveFunction;
+	protected int stype;
+	protected float noisesize;
+	
 	/**
 	 * Constructor stores the given noise generator.
 	 * @param noiseGenerator
 	 *        the noise generator
 	 */
 	public TextureGeneratorMusgrave(NoiseGenerator noiseGenerator) {
-		super(noiseGenerator);
+		super(noiseGenerator, Format.Luminance8);
+	}
+	
+	@Override
+	public void readData(Structure tex, BlenderContext blenderContext) {
+		super.readData(tex, blenderContext);
+		musgraveData = new MusgraveData(tex);
+		stype = ((Number) tex.getFieldValue("stype")).intValue();
+		noisesize = ((Number) tex.getFieldValue("noisesize")).floatValue();
+		musgraveFunction = NoiseGenerator.musgraveFunctions.get(Integer.valueOf(musgraveData.stype));
+		if(musgraveFunction==null) {
+			throw new IllegalStateException("Unknown type of musgrave texture: " + stype);
+		}
 	}
 
 	@Override
-	protected Texture generate(Structure tex, int width, int height, int depth, BlenderContext blenderContext) {
-		int stype = ((Number) tex.getFieldValue("stype")).intValue();
-		float noisesize = ((Number) tex.getFieldValue("noisesize")).floatValue();
-		TexturePixel texres = new TexturePixel();
-		float[] texvec = new float[] { 0, 0, 0 };
-		int halfW = width >> 1, halfH = height >> 1, halfD = depth >> 1, index = 0;
-		float wDelta = 1.0f / halfW, hDelta = 1.0f / halfH, dDelta = 1.0f / halfD;
-		float[][] colorBand = this.computeColorband(tex, blenderContext);
-		Format format = colorBand != null ? Format.RGBA8 : Format.Luminance8;
-		int bytesPerPixel = colorBand != null ? 4 : 1;
-		MusgraveData musgraveData = new MusgraveData(tex);
-		MusgraveFunction musgraveFunction;
-		BrightnessAndContrastData bacd = new BrightnessAndContrastData(tex);
+	public void getPixel(TexturePixel pixel, float x, float y, float z) {
+		pixel.intensity = musgraveData.outscale * musgraveFunction.execute(musgraveData, x, y, z);
+		if(pixel.intensity>1) {
+			pixel.intensity = 1.0f;
+		} else if(pixel.intensity < 0) {
+			pixel.intensity = 0.0f;
+		}
 		
-		byte[] data = new byte[width * height * depth * bytesPerPixel];
-		for (int i = -halfW; i < halfW; ++i) {
-			texvec[0] = wDelta * i / noisesize;
-			for (int j = -halfH; j < halfH; ++j) {
-				texvec[1] = hDelta * j / noisesize;
-				for (int k = -halfD; k < halfD; ++k) {
-					texvec[2] = dDelta * k / noisesize;
-					musgraveFunction = NoiseGenerator.musgraveFunctions.get(Integer.valueOf(musgraveData.stype));
-					if(musgraveFunction==null) {
-						throw new IllegalStateException("Unknown type of musgrave texture: " + stype);
-					}
-					texres.intensity = musgraveData.outscale * musgraveFunction.execute(musgraveData, texvec[0], texvec[1], texvec[2]);
-					if(texres.intensity>1) {
-						texres.intensity = 1.0f;
-					} else if(texres.intensity < 0) {
-						texres.intensity = 0.0f;
-					}
-					
-					if (colorBand != null) {
-						int colorbandIndex = (int) (texres.intensity * 1000.0f);
-						texres.red = colorBand[colorbandIndex][0];
-						texres.green = colorBand[colorbandIndex][1];
-						texres.blue = colorBand[colorbandIndex][2];
-						
-						this.applyBrightnessAndContrast(texres, bacd.contrast, bacd.brightness);
-						data[index++] = (byte) (texres.red * 255.0f);
-						data[index++] = (byte) (texres.green * 255.0f);
-						data[index++] = (byte) (texres.blue * 255.0f);
-						data[index++] = (byte) (colorBand[colorbandIndex][3] * 255.0f);
-					} else {
-						this.applyBrightnessAndContrast(bacd, texres);
-						data[index++] = (byte) (texres.intensity * 255.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(pixel, bacd.contrast, bacd.brightness);
+			pixel.alpha = colorBand[colorbandIndex][3];
+		} else {
+			this.applyBrightnessAndContrast(bacd, pixel);
 		}
-		ArrayList<ByteBuffer> dataArray = new ArrayList<ByteBuffer>(1);
-		dataArray.add(BufferUtils.createByteBuffer(data));
-		return new Texture3D(new Image(format, width, height, depth, dataArray));
 	}
 	
     protected static class MusgraveData {

+ 85 - 0
engine/src/blender/com/jme3/scene/plugins/blender/textures/generating/TextureGeneratorNoise.java

@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.scene.plugins.blender.textures.generating;
+
+import com.jme3.math.FastMath;
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.file.Structure;
+import com.jme3.scene.plugins.blender.textures.TexturePixel;
+import com.jme3.texture.Image.Format;
+
+/**
+ * This class generates the 'noise' texture.
+ * @author Marcin Roguski (Kaelthas)
+ */
+public class TextureGeneratorNoise extends TextureGenerator {
+	protected int noisedepth;
+	
+	/**
+	 * Constructor stores the given noise generator.
+	 * @param noiseGenerator
+	 *        the noise generator
+	 */
+	public TextureGeneratorNoise(NoiseGenerator noiseGenerator) {
+		super(noiseGenerator, Format.Luminance8);
+	}
+	
+	@Override
+	public void readData(Structure tex, BlenderContext blenderContext) {
+		super.readData(tex, blenderContext);
+		noisedepth = ((Number) tex.getFieldValue("noisedepth")).intValue();
+	}
+	
+	@Override
+	public void getPixel(TexturePixel pixel, float x, float y, float z) {
+		int random = FastMath.rand.nextInt();
+		int val = random & 3;
+
+		int loop = noisedepth;
+		while (loop-- != 0) {
+			random >>= 2;
+			val *= random & 3;
+		}
+		pixel.intensity = FastMath.clamp(val, 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);
+			pixel.alpha = colorBand[colorbandIndex][3];
+		} else {
+			this.applyBrightnessAndContrast(pixel, bacd.contrast, bacd.brightness);
+		}
+	}
+}

+ 101 - 0
engine/src/blender/com/jme3/scene/plugins/blender/textures/generating/TextureGeneratorStucci.java

@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.scene.plugins.blender.textures.generating;
+
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.file.Structure;
+import com.jme3.scene.plugins.blender.textures.TexturePixel;
+import com.jme3.texture.Image.Format;
+
+/**
+ * This class generates the 'stucci' texture.
+ * @author Marcin Roguski (Kaelthas)
+ */
+public class TextureGeneratorStucci extends TextureGenerator {
+	protected static final int TEX_NOISESOFT = 0;
+	
+	protected float noisesize;
+	protected int noisebasis;
+	protected int noisetype;
+	protected float turbul;
+	protected boolean isHard;
+	protected int stype;
+	
+	/**
+	 * Constructor stores the given noise generator.
+	 * @param noiseGenerator
+	 *        the noise generator
+	 */
+	public TextureGeneratorStucci(NoiseGenerator noiseGenerator) {
+		super(noiseGenerator, Format.Luminance8);
+	}
+
+	@Override
+	public void readData(Structure tex, BlenderContext blenderContext) {
+		super.readData(tex, blenderContext);
+		noisesize = ((Number) tex.getFieldValue("noisesize")).floatValue();
+		noisebasis = ((Number) tex.getFieldValue("noisebasis")).intValue();
+		noisetype = ((Number) tex.getFieldValue("noisetype")).intValue();
+		turbul = ((Number) tex.getFieldValue("turbul")).floatValue();
+		isHard = noisetype != TEX_NOISESOFT;
+		stype = ((Number) tex.getFieldValue("stype")).intValue();
+		if(noisesize<=0.001f) {//the texture goes black if this value is lower than 0.001f
+			noisesize = 0.001f;
+		}
+	}
+	
+	@Override
+	public void getPixel(TexturePixel pixel, float x, float y, float z) {
+		float noiseValue = NoiseGenerator.NoiseFunctions.noise(x, y, z, noisesize, 0, noisebasis, isHard);
+		float ofs = turbul / 200.0f;
+		if (stype != 0) {
+			ofs *= noiseValue * noiseValue;
+		}
+
+		pixel.intensity = NoiseGenerator.NoiseFunctions.noise(x, y, z + ofs, noisesize, 0, noisebasis, isHard);
+		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];
+		}
+
+		if (stype == NoiseGenerator.TEX_WALLOUT) {
+			pixel.intensity = 1.0f - pixel.intensity;
+		}
+		if (pixel.intensity < 0.0f) {
+			pixel.intensity = 0.0f;
+		}
+		//no brightness and contrast needed for stucci (it doesn't affect the texture)
+	}
+}

+ 141 - 0
engine/src/blender/com/jme3/scene/plugins/blender/textures/generating/TextureGeneratorVoronoi.java

@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.scene.plugins.blender.textures.generating;
+
+import com.jme3.math.FastMath;
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.file.Structure;
+import com.jme3.scene.plugins.blender.textures.TexturePixel;
+import com.jme3.scene.plugins.blender.textures.generating.NoiseGenerator.NoiseMath;
+import com.jme3.texture.Image.Format;
+
+/**
+ * This class generates the 'voronoi' texture.
+ * @author Marcin Roguski (Kaelthas)
+ */
+public class TextureGeneratorVoronoi extends TextureGenerator {
+	protected float noisesize;
+	protected float outscale;
+	protected float mexp;
+	protected int distanceType;
+	protected int voronoiColorType;
+	protected float[] da = new float[4], pa = new float[12];
+	protected float[] hashPoint;
+	protected float[] voronoiWeights;
+	protected float weightSum;
+	
+	/**
+	 * Constructor stores the given noise generator.
+	 * @param noiseGenerator
+	 *        the noise generator
+	 */
+	public TextureGeneratorVoronoi(NoiseGenerator noiseGenerator) {
+		super(noiseGenerator, Format.Luminance8);
+	}
+	
+	@Override
+	public void readData(Structure tex, BlenderContext blenderContext) {
+		super.readData(tex, blenderContext);
+		voronoiWeights = new float[4];
+		voronoiWeights[0] = ((Number) tex.getFieldValue("vn_w1")).floatValue();
+		voronoiWeights[1] = ((Number) tex.getFieldValue("vn_w2")).floatValue();
+		voronoiWeights[2] = ((Number) tex.getFieldValue("vn_w3")).floatValue();
+		voronoiWeights[3] = ((Number) tex.getFieldValue("vn_w4")).floatValue();
+		noisesize = ((Number) tex.getFieldValue("noisesize")).floatValue();
+		outscale = ((Number) tex.getFieldValue("ns_outscale")).floatValue();
+		mexp = ((Number) tex.getFieldValue("vn_mexp")).floatValue();
+		distanceType = ((Number) tex.getFieldValue("vn_distm")).intValue();
+		voronoiColorType = ((Number) tex.getFieldValue("vn_coltype")).intValue();
+		hashPoint = voronoiColorType != 0 ? new float[3] : null;
+		weightSum = voronoiWeights[0] + voronoiWeights[1] + voronoiWeights[2] + voronoiWeights[3];
+		if (weightSum != 0.0f) {
+			weightSum = outscale / weightSum;
+		}
+		if(voronoiColorType != 0 || colorBand != null) {
+			this.imageFormat = Format.RGBA8;
+		}
+	}
+	
+	@Override
+	public void getPixel(TexturePixel pixel, float x, float y, float z) {
+		//for voronoi we need to widen the range a little
+		NoiseGenerator.NoiseFunctions.voronoi(x * 4, y * 4, z * 4, da, pa, mexp, distanceType);
+		pixel.intensity = weightSum * FastMath.abs(voronoiWeights[0] * da[0] + voronoiWeights[1] * da[1] + voronoiWeights[2] * da[2] + voronoiWeights[3] * da[3]);
+		if(pixel.intensity>1.0f) {
+			pixel.intensity = 1.0f;
+		} else if(pixel.intensity<0.0f) {
+			pixel.intensity = 0.0f;
+		}
+		
+		if (colorBand != null) {//colorband ALWAYS goes first and covers the color (if set)
+			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 (voronoiColorType != 0) {
+			pixel.red = pixel.green = pixel.blue = 0.0f;
+			pixel.alpha = 1.0f;
+			for(int m=0; m<12; m+=3) {
+				float weight = voronoiWeights[m/3];
+				NoiseMath.hash((int)pa[m], (int)pa[m + 1], (int)pa[m + 2], hashPoint);
+				pixel.red += weight * hashPoint[0];
+				pixel.green += weight * hashPoint[1];
+				pixel.blue += weight * hashPoint[2];
+			}
+			if (voronoiColorType >= 2) {
+				float t1 = (da[1] - da[0]) * 10.0f;
+				if (t1 > 1.0f) {
+					t1 = 1.0f;
+				}
+				if (voronoiColorType == 3) {
+					t1 *= pixel.intensity;
+				} else {
+					t1 *= weightSum;
+				}
+				pixel.red *= t1;
+				pixel.green *= t1;
+				pixel.blue *= t1;
+			} else {
+				pixel.red *= weightSum;
+				pixel.green *= weightSum;
+				pixel.blue *= weightSum;
+			}
+		}
+
+		if (voronoiColorType != 0 || colorBand != null) {
+			this.applyBrightnessAndContrast(bacd, pixel);
+		} else {
+			this.applyBrightnessAndContrast(pixel, bacd.contrast, bacd.brightness);
+		}
+	}
+}

+ 24 - 54
engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorWood.java → engine/src/blender/com/jme3/scene/plugins/blender/textures/generating/TextureGeneratorWood.java

@@ -28,18 +28,13 @@
  * ***** END GPL LICENSE BLOCK *****
  *
  */
-package com.jme3.scene.plugins.blender.textures;
+package com.jme3.scene.plugins.blender.textures.generating;
 
 import com.jme3.math.FastMath;
 import com.jme3.scene.plugins.blender.BlenderContext;
 import com.jme3.scene.plugins.blender.file.Structure;
-import com.jme3.texture.Image;
+import com.jme3.scene.plugins.blender.textures.TexturePixel;
 import com.jme3.texture.Image.Format;
-import com.jme3.texture.Texture;
-import com.jme3.texture.Texture3D;
-import com.jme3.util.BufferUtils;
-import java.nio.ByteBuffer;
-import java.util.ArrayList;
 
 /**
  * This class generates the 'wood' texture.
@@ -61,62 +56,37 @@ public class TextureGeneratorWood extends TextureGenerator {
     protected static final int TEX_NOISESOFT = 0;
     protected static final int TEX_NOISEPERL = 1;
     
+    protected WoodIntensityData woodIntensityData;
+    
 	/**
 	 * Constructor stores the given noise generator.
 	 * @param noiseGenerator the noise generator
 	 */
 	public TextureGeneratorWood(NoiseGenerator noiseGenerator) {
-		super(noiseGenerator);
+		super(noiseGenerator, Format.Luminance8);
 	}
-
+	
 	@Override
-	protected Texture generate(Structure tex, int width, int height, int depth, BlenderContext blenderContext) {
-		float[] texvec = new float[] { 0, 0, 0 };
-		TexturePixel texres = new TexturePixel();
-		int halfW = width >> 1;
-		int halfH = height >> 1;
-		int halfD = depth >> 1;
-		float wDelta = 1.0f / halfW, hDelta = 1.0f / halfH, dDelta = 1.0f / halfD;
-		
-		float[][] colorBand = this.computeColorband(tex, blenderContext);
-		Format format = colorBand != null ? Format.RGBA8 : Format.Luminance8;
-		int bytesPerPixel = colorBand != null ? 4 : 1;
-		WoodIntensityData woodIntensityData = new WoodIntensityData(tex);
-		BrightnessAndContrastData bacd = new BrightnessAndContrastData(tex);
+	public void readData(Structure tex, BlenderContext blenderContext) {
+		super.readData(tex, blenderContext);
+		woodIntensityData = new WoodIntensityData(tex);
+	}
+	
+	@Override
+	public void getPixel(TexturePixel pixel, float x, float y, float z) {
+		pixel.intensity = this.woodIntensity(woodIntensityData, x, y, z);
 		
-		int index = 0;
-		byte[] data = new byte[width * height * depth * bytesPerPixel];
-		for (int i = -halfW; i < halfW; ++i) {
-			texvec[0] = wDelta * i;
-			for (int j = -halfH; j < halfH; ++j) {
-				texvec[1] = hDelta * j;
-				for(int k = -halfD; k < halfD; ++k) {
-					texvec[2] = dDelta * k;
-					texres.intensity = this.woodIntensity(woodIntensityData, texvec[0], texvec[1], texvec[2]);
-					
-					if (colorBand != null) {
-						int colorbandIndex = (int) (texres.intensity * 1000.0f);
-						texres.red = colorBand[colorbandIndex][0];
-						texres.green = colorBand[colorbandIndex][1];
-						texres.blue = colorBand[colorbandIndex][2];
-						
-						this.applyBrightnessAndContrast(bacd, texres);
-						
-						data[index++] = (byte) (texres.red * 255.0f);
-						data[index++] = (byte) (texres.green * 255.0f);
-						data[index++] = (byte) (texres.blue * 255.0f);
-						data[index++] = (byte) (colorBand[colorbandIndex][3] * 255.0f);
-					} else {
-						this.applyBrightnessAndContrast(texres, bacd.contrast, bacd.brightness);
-						data[index++] = (byte) (texres.intensity * 255.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);
+			pixel.alpha = colorBand[colorbandIndex][3];
+		} else {
+			this.applyBrightnessAndContrast(pixel, bacd.contrast, bacd.brightness);
 		}
-		
-		ArrayList<ByteBuffer> dataArray = new ArrayList<ByteBuffer>(1);
-		dataArray.add(BufferUtils.createByteBuffer(data));
-		return new Texture3D(new Image(format, width, height, depth, dataArray));
 	}
 	
     protected static WaveForm[] waveformFunctions = new WaveForm[3];

+ 0 - 0
engine/src/blender/com/jme3/scene/plugins/blender/textures/noiseconstants.dat → engine/src/blender/com/jme3/scene/plugins/blender/textures/generating/noiseconstants.dat


+ 75 - 0
engine/src/blender/com/jme3/scene/plugins/blender/textures/io/AWTPixelInputOutput.java

@@ -0,0 +1,75 @@
+package com.jme3.scene.plugins.blender.textures.io;
+
+import com.jme3.scene.plugins.blender.textures.TexturePixel;
+import com.jme3.texture.Image;
+
+/**
+ * Implemens read/write operations for AWT images.
+ * @author Marcin Roguski (Kaelthas)
+ */
+/*package*/ class AWTPixelInputOutput implements PixelInputOutput {
+	@Override
+	public void read(Image image, TexturePixel pixel, int index) {
+		byte r,g,b,a;
+		switch(image.getFormat()) {//TODO: add other formats
+			case RGBA8:
+				r = image.getData(0).get(index);
+				g = image.getData(0).get(index + 1);
+				b = image.getData(0).get(index + 2);
+				a = image.getData(0).get(index + 3);
+				break;
+			case ABGR8:
+				a = image.getData(0).get(index);
+				b = image.getData(0).get(index + 1);
+				g = image.getData(0).get(index + 2);
+				r = image.getData(0).get(index + 3);
+				break;
+			case BGR8:
+				b = image.getData(0).get(index);
+				g = image.getData(0).get(index + 1);
+				r = image.getData(0).get(index + 2);
+				a = (byte)0xFF;
+				break;
+			default:
+				throw new IllegalStateException("Unknown image format: " + image.getFormat());
+		}
+		pixel.fromARGB8(a, r, g, b);
+	}
+	
+	@Override
+	public void read(Image image, TexturePixel pixel, int x, int y) {
+		int index = (y * image.getWidth() + x) * (image.getFormat().getBitsPerPixel() >> 3);
+		this.read(image, pixel, index);
+	}
+
+	@Override
+	public void write(Image image, TexturePixel pixel, int index) {
+		switch(image.getFormat()) {
+			case RGBA8:
+				image.getData(0).put(index, pixel.getR8());
+				image.getData(0).put(index + 1, pixel.getG8());
+				image.getData(0).put(index + 2, pixel.getB8());
+				image.getData(0).put(index + 3, pixel.getA8());
+				break;
+			case ABGR8:
+				image.getData(0).put(index, pixel.getA8());
+				image.getData(0).put(index + 1, pixel.getB8());
+				image.getData(0).put(index + 2, pixel.getG8());
+				image.getData(0).put(index + 3, pixel.getR8());
+				break;
+			case BGR8:
+				image.getData(0).put(index, pixel.getB8());
+				image.getData(0).put(index + 1, pixel.getG8());
+				image.getData(0).put(index + 2, pixel.getR8());
+				break;
+			default:
+				throw new IllegalStateException("Unknown image format: " + image.getFormat());
+		}
+	}
+	
+	@Override
+	public void write(Image image, TexturePixel pixel, int x, int y) {
+		int index = (y * image.getWidth() + x) * (image.getFormat().getBitsPerPixel() >> 3);
+		this.write(image, pixel, index);
+	}
+}

+ 174 - 0
engine/src/blender/com/jme3/scene/plugins/blender/textures/io/DDSPixelInputOutput.java

@@ -0,0 +1,174 @@
+package com.jme3.scene.plugins.blender.textures.io;
+
+import java.nio.ByteBuffer;
+
+import jme3tools.converters.RGB565;
+
+import com.jme3.math.FastMath;
+import com.jme3.scene.plugins.blender.textures.TexturePixel;
+import com.jme3.texture.Image;
+
+/**
+ * Implemens read/write operations for DDS images.
+ * This class currently implements only read operation.
+ * @author Marcin Roguski (Kaelthas)
+ */
+/*package*/ class DDSPixelInputOutput implements PixelInputOutput {
+	@Override
+	public void read(Image image, TexturePixel pixel, int index) {
+		throw new UnsupportedOperationException("Cannot get the DXT pixel by index because not every index contains the pixel color!");
+	}
+
+	@Override
+	public void read(Image image, TexturePixel pixel, int x, int y) {
+		int xTexetlIndex = x % image.getWidth() >> 2;
+		int yTexelIndex = y % image.getHeight() >> 2;
+		int xTexelCount = image.getWidth() >> 2;
+		int texelIndex = yTexelIndex * xTexelCount + xTexetlIndex;
+		
+		TexturePixel[] colors = new TexturePixel[] { new TexturePixel(), new TexturePixel(), new TexturePixel(), new TexturePixel() };
+		int indexes = 0;
+		long alphaIndexes = 0;
+		float[] alphas = null;
+		ByteBuffer data = image.getData().get(0);
+		
+		switch (image.getFormat()) {
+			case DXT1: {// BC1
+				data.position(texelIndex * 8);
+				short c0 = data.getShort();
+				short c1 = data.getShort();
+				int col0 = RGB565.RGB565_to_ARGB8(c0);
+				int col1 = RGB565.RGB565_to_ARGB8(c1);
+				colors[0].fromARGB8(col0);
+				colors[1].fromARGB8(col1);
+
+				if (col0 > col1) {
+					// creating color2 = 2/3color0 + 1/3color1
+					colors[2].fromPixel(colors[0]);
+					colors[2].mult(2);
+					colors[2].add(colors[1]);
+					colors[2].divide(3);
+
+					// creating color3 = 1/3color0 + 2/3color1;
+					colors[3].fromPixel(colors[1]);
+					colors[3].mult(2);
+					colors[3].add(colors[0]);
+					colors[3].divide(3);
+				} else {
+					// creating color2 = 1/2color0 + 1/2color1
+					colors[2].fromPixel(colors[0]);
+					colors[2].add(colors[1]);
+					colors[2].mult(0.5f);
+
+					colors[3].fromARGB8(0);
+				}
+				indexes = data.getInt();// 4-byte table with color indexes in decompressed table
+				break;
+			}
+			case DXT3: {// BC2
+				data.position(texelIndex * 16);
+				long alpha = data.getLong();
+				alphas = new float[16];
+				for (int i = 0; i < 16; ++i) {
+					alphaIndexes |= i << i * 4;
+					byte a = (byte) ((alpha >> i * 4 & 0x0F) << 4);
+					alphas[i] = a >= 0 ? a / 255.0f : 1.0f - ~a / 255.0f;
+				}
+
+				short c0 = data.getShort();
+				short c1 = data.getShort();
+				int col0 = RGB565.RGB565_to_ARGB8(c0);
+				int col1 = RGB565.RGB565_to_ARGB8(c1);
+				colors[0].fromARGB8(col0);
+				colors[1].fromARGB8(col1);
+
+				// creating color2 = 2/3color0 + 1/3color1
+				colors[2].fromPixel(colors[0]);
+				colors[2].mult(2);
+				colors[2].add(colors[1]);
+				colors[2].divide(3);
+
+				// creating color3 = 1/3color0 + 2/3color1;
+				colors[3].fromPixel(colors[1]);
+				colors[3].mult(2);
+				colors[3].add(colors[0]);
+				colors[3].divide(3);
+
+				indexes = data.getInt();// 4-byte table with color indexes in decompressed table
+				break;
+			}
+			case DXT5: {// BC3
+				data.position(texelIndex * 16);
+				alphas = new float[8];
+				alphas[0] = data.get() * 255.0f;
+				alphas[1] = data.get() * 255.0f;
+				alphaIndexes = data.get() | data.get() << 8 | data.get() << 16 | data.get() << 24 | data.get() << 32 | data.get() << 40;
+				if (alphas[0] > alphas[1]) {// 6 interpolated alpha values.
+					alphas[2] = (6 * alphas[0] + alphas[1]) / 7;
+					alphas[3] = (5 * alphas[0] + 2 * alphas[1]) / 7;
+					alphas[4] = (4 * alphas[0] + 3 * alphas[1]) / 7;
+					alphas[5] = (3 * alphas[0] + 4 * alphas[1]) / 7;
+					alphas[6] = (2 * alphas[0] + 5 * alphas[1]) / 7;
+					alphas[7] = (alphas[0] + 6 * alphas[1]) / 7;
+				} else {
+					alphas[2] = (4 * alphas[0] + alphas[1]) * 0.2f;
+					alphas[3] = (3 * alphas[0] + 2 * alphas[1]) * 0.2f;
+					alphas[4] = (2 * alphas[0] + 3 * alphas[1]) * 0.2f;
+					alphas[5] = (alphas[0] + 4 * alphas[1]) * 0.2f;
+					alphas[6] = 0;
+					alphas[7] = 1;
+				}
+
+				short c0 = data.getShort();
+				short c1 = data.getShort();
+				int col0 = RGB565.RGB565_to_ARGB8(c0);
+				int col1 = RGB565.RGB565_to_ARGB8(c1);
+				colors[0].fromARGB8(col0);
+				colors[1].fromARGB8(col1);
+
+				// creating color2 = 2/3color0 + 1/3color1
+				colors[2].fromPixel(colors[0]);
+				colors[2].mult(2);
+				colors[2].add(colors[1]);
+				colors[2].divide(3);
+
+				// creating color3 = 1/3color0 + 2/3color1;
+				colors[3].fromPixel(colors[1]);
+				colors[3].mult(2);
+				colors[3].add(colors[0]);
+				colors[3].divide(3);
+
+				indexes = data.getInt();// 4-byte table with color indexes in decompressed table
+				break;
+			}
+			case DXT1A://TODO: implement
+				break;
+			default:
+				throw new IllegalStateException("Unsupported decompression format.");
+		}
+		
+		// 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;
+
+		// getting the pixel
+		int indexMask = colors.length - 1;
+		int colorIndex = indexes >> pixelIndexInTexel & indexMask;
+		float alpha = alphas != null ? alphas[(int) (alphaIndexes >> alphaIndexInTexel & 0x07)] : colors[colorIndex].alpha;
+		pixel.fromPixel(colors[colorIndex]);
+		pixel.alpha = alpha;
+	}
+
+	@Override
+	public void write(Image image, TexturePixel pixel, int index) {
+		throw new UnsupportedOperationException("Cannot put the DXT pixel by index because not every index contains the pixel color!");
+	}
+
+	@Override
+	public void write(Image image, TexturePixel pixel, int x, int y) {
+		throw new UnsupportedOperationException("Writing to DDS texture pixel by pixel is not yet supported!");
+	}
+}

+ 33 - 0
engine/src/blender/com/jme3/scene/plugins/blender/textures/io/LuminancePixelInputOutput.java

@@ -0,0 +1,33 @@
+package com.jme3.scene.plugins.blender.textures.io;
+
+import com.jme3.scene.plugins.blender.textures.TexturePixel;
+import com.jme3.texture.Image;
+
+/**
+ * Implemens read/write operations for luminance images.
+ * @author Marcin Roguski (Kaelthas)
+ */
+/*package*/ class LuminancePixelInputOutput implements PixelInputOutput {
+	@Override
+	public void read(Image image, TexturePixel pixel, int index) {
+		byte intensity = image.getData(0).get(index);
+		pixel.fromIntensity(intensity);
+	}
+	
+	@Override
+	public void read(Image image, TexturePixel pixel, int x, int y) {
+		int index = y * image.getWidth() + x;
+		this.read(image, pixel, index);
+	}
+	
+	@Override
+	public void write(Image image, TexturePixel pixel, int index) {
+		image.getData(0).put(index, pixel.getInt());
+	}
+
+	@Override
+	public void write(Image image, TexturePixel pixel, int x, int y) {
+		int index = y * image.getWidth() + x;
+		this.write(image, pixel, index);
+	}
+}

+ 50 - 0
engine/src/blender/com/jme3/scene/plugins/blender/textures/io/PixelIOFactory.java

@@ -0,0 +1,50 @@
+package com.jme3.scene.plugins.blender.textures.io;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import com.jme3.texture.Image.Format;
+
+/**
+ * This class creates a pixel IO object for the specified image format.
+ * 
+ * @author Marcin Roguski (Kaelthas)
+ */
+public class PixelIOFactory {
+	private static final Map<Format, PixelInputOutput>	PIXEL_INPUT_OUTPUT	= new HashMap<Format, PixelInputOutput>();
+
+	/**
+	 * This method returns pixel IO object for the specified format.
+	 * 
+	 * @param format
+	 *            the format of the image
+	 * @return pixel IO object
+	 */
+	public static PixelInputOutput getPixelIO(Format format) {
+		PixelInputOutput result = PIXEL_INPUT_OUTPUT.get(format);
+		if (result == null) {
+			switch (format) {
+				case ABGR8:
+				case RGBA8:
+				case BGR8:
+					result = new AWTPixelInputOutput();
+					break;
+				case Luminance8:
+					result = new LuminancePixelInputOutput();
+					break;
+				case DXT1:
+				case DXT1A:
+				case DXT3:
+				case DXT5:
+					result = new DDSPixelInputOutput();
+					break;
+				default:
+					throw new IllegalStateException("Unsupported image format for IO operations: " + format);
+			}
+			synchronized (PIXEL_INPUT_OUTPUT) {
+				PIXEL_INPUT_OUTPUT.put(format, result);
+			}
+		}
+		return result;
+	}
+}

+ 65 - 0
engine/src/blender/com/jme3/scene/plugins/blender/textures/io/PixelInputOutput.java

@@ -0,0 +1,65 @@
+package com.jme3.scene.plugins.blender.textures.io;
+
+import com.jme3.scene.plugins.blender.textures.TexturePixel;
+import com.jme3.texture.Image;
+
+/**
+ * Implemens read/write operations for images.
+ * 
+ * @author Marcin Roguski (Kaelthas)
+ */
+public interface PixelInputOutput {
+	/**
+	 * This method reads a pixel that starts at the given index.
+	 * 
+	 * @param image
+	 *            the image we read pixel from
+	 * @param pixel
+	 *            the pixel where the result is stored
+	 * @param index
+	 *            the index where the pixel begins in the image data
+	 */
+	void read(Image image, TexturePixel pixel, int index);
+
+	/**
+	 * This method reads a pixel that is located at the given position on the
+	 * image.
+	 * 
+	 * @param image
+	 *            the image we read pixel from
+	 * @param pixel
+	 *            the pixel where the result is stored
+	 * @param x
+	 *            the X coordinate of the pixel
+	 * @param y
+	 *            the Y coordinate of the pixel
+	 */
+	void read(Image image, TexturePixel pixel, int x, int y);
+
+	/**
+	 * This method writes a pixel that starts at the given index.
+	 * 
+	 * @param image
+	 *            the image we read pixel from
+	 * @param pixel
+	 *            the pixel where the result is stored
+	 * @param index
+	 *            the index where the pixel begins in the image data
+	 */
+	void write(Image image, TexturePixel pixel, int index);
+
+	/**
+	 * This method writes a pixel that is located at the given position on the
+	 * image.
+	 * 
+	 * @param image
+	 *            the image we read pixel from
+	 * @param pixel
+	 *            the pixel where the result is stored
+	 * @param x
+	 *            the X coordinate of the pixel
+	 * @param y
+	 *            the Y coordinate of the pixel
+	 */
+	void write(Image image, TexturePixel pixel, int x, int y);
+}