diff --git a/engine/src/blender/Common/MatDefs/Texture3D/tex3D.frag b/engine/src/blender/Common/MatDefs/Texture3D/tex3D.frag deleted file mode 100644 index 55862acf9..000000000 --- a/engine/src/blender/Common/MatDefs/Texture3D/tex3D.frag +++ /dev/null @@ -1,7 +0,0 @@ -uniform sampler3D m_Texture; - -varying vec3 texCoord; - -void main(){ - gl_FragColor= texture3D(m_Texture,texCoord); -} \ No newline at end of file diff --git a/engine/src/blender/Common/MatDefs/Texture3D/tex3D.j3md b/engine/src/blender/Common/MatDefs/Texture3D/tex3D.j3md deleted file mode 100644 index f76fd4148..000000000 --- a/engine/src/blender/Common/MatDefs/Texture3D/tex3D.j3md +++ /dev/null @@ -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 - } - } - -} diff --git a/engine/src/blender/Common/MatDefs/Texture3D/tex3D.vert b/engine/src/blender/Common/MatDefs/Texture3D/tex3D.vert deleted file mode 100644 index f91b7b309..000000000 --- a/engine/src/blender/Common/MatDefs/Texture3D/tex3D.vert +++ /dev/null @@ -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; -} \ No newline at end of file diff --git a/engine/src/blender/com/jme3/asset/BlenderKey.java b/engine/src/blender/com/jme3/asset/BlenderKey.java index 4e791656e..f7e67999e 100644 --- a/engine/src/blender/com/jme3/asset/BlenderKey.java +++ b/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) diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/AbstractBlenderLoader.java b/engine/src/blender/com/jme3/scene/plugins/blender/AbstractBlenderLoader.java index b42f76078..2cc45ce33 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/AbstractBlenderLoader.java +++ b/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 diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/BlenderContext.java b/engine/src/blender/com/jme3/scene/plugins/blender/BlenderContext.java index 1ed1c1cff..741533fa6 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/BlenderContext.java +++ b/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 meshContexts = new HashMap(); /** A map of bone contexts. */ protected Map boneContexts = new HashMap(); - /** A map of material contexts. */ - protected Map materialContexts = new HashMap(); /** A map og helpers that perform loading. */ private Map helpers = new HashMap(); @@ -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 null 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. * diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/BlenderLoader.java b/engine/src/blender/com/jme3/scene/plugins/blender/BlenderLoader.java index 3c81ba6d8..26032beee 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/BlenderLoader.java +++ b/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); diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/curves/CurvesHelper.java b/engine/src/blender/com/jme3/scene/plugins/blender/curves/CurvesHelper.java index af30f3bb3..d6d671e45 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/curves/CurvesHelper.java +++ b/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); } diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/materials/MaterialContext.java b/engine/src/blender/com/jme3/scene/plugins/blender/materials/MaterialContext.java index 2e6b0cf28..6799f4877 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/materials/MaterialContext.java +++ b/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 mTexs; - /* package */final List textures; - /* package */final Map loadedTextures; - /* package */final Map textureToMTexMap; - /* package */final int texturesCount; - /* package */final Type textureType; + /* package */final Map 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(); - textures = new ArrayList(); - DynamicArray mtexsArray = (DynamicArray) structure.getFieldValue("mtex"); int separatedTextures = ((Number) structure.getFieldValue("septex")).intValue(); - Type firstTextureType = null; + List texturesList = new ArrayList(); 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> sortedTextures = this.sortAndFilterTextures(); - loadedTextures = new HashMap(sortedTextures.size()); - textureToMTexMap = new HashMap(); + Map> textureDataMap = this.sortAndFilterTextures(texturesList); + loadedTextures = new HashMap(); float[] diffuseColorArray = new float[] {diffuseColor.r, diffuseColor.g, diffuseColor.b, diffuseColor.a}; TextureHelper textureHelper = blenderContext.getHelper(TextureHelper.class); - for(Entry> entry : sortedTextures.entrySet()) { + for(Entry> entry : textureDataMap.entrySet()) { if(entry.getValue().size()>0) { - List textures = new ArrayList(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 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 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> sortAndFilterTextures() { - Map> result = new HashMap>(); - 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 mtexs = result.get(mapto); - if(mtexs==null) { - mtexs = new ArrayList(); - 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> sortAndFilterTextures(List textures) { + Map> result = new HashMap>(); + for (TextureData data : textures) { + Number mapto = (Number) data.mtex.getFieldValue("mapto"); + List datas = result.get(mapto); + if(datas==null) { + datas = new ArrayList(); + 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 true if the texture has no alpha channel and false - * 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 true if the material is shadeless and false otherwise - */ - public boolean isShadeless() { - return shadeless; - } - - /** - * @return true if the material uses vertex color and false otherwise - */ - public boolean isVertexColor() { - return vertexColor; - } - - /** - * @return true if the material is transparent and false otherwise - */ - public boolean isTransparent() { - return transparent; - } - - /** - * @return true if the material uses tangents and false 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 null if none - * exists - */ - public Structure getMTex(Texture texture) { - return textureToMTexMap.get(texture); + private static class TextureData { + Structure mtex; + Structure textureStructure; + int uvCoordinatesType; + int projectionType; } } diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/materials/MaterialHelper.java b/engine/src/blender/com/jme3/scene/plugins/blender/materials/MaterialHelper.java index a25b7278a..f7291cbba 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/materials/MaterialHelper.java +++ b/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 texturesMap = new HashMap(); - for(Entry 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 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 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); } } } diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/meshes/MeshHelper.java b/engine/src/blender/com/jme3/scene/plugins/blender/meshes/MeshHelper.java index 7f9e8acf4..07a2bb86f 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/meshes/MeshHelper.java +++ b/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 uvCoordinates = null; + Map> uvCoordinates = new HashMap>();// List 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(); } // 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 uvs = null; boolean materialWithoutTextures = false; Pointer pImage = null; + + List uvCoordinatesList = uvCoordinates.get(Integer.valueOf(matNr)); + if(uvCoordinatesList == null) { + uvCoordinatesList = new ArrayList(); + 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) 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 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 meshToMAterialMap = new HashMap(meshesMap.size()); for (Entry> 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 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> materialMap = new HashMap>(); - for (Geometry geom : geometries) { - Material material = geom.getMaterial(); - List geomsWithCommonMaterial = materialMap.get(material); - if (geomsWithCommonMaterial == null) { - geomsWithCommonMaterial = new ArrayList(); - materialMap.put(material, geomsWithCommonMaterial); - } - geomsWithCommonMaterial.add(geom); - - } - for (Entry> 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; } diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/ColorBand.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/ColorBand.java new file mode 100644 index 000000000..eb64287af --- /dev/null +++ b/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 data = (DynamicArray) 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 true if the colorband has transparencies and false + * 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 cbDataMap = new TreeMap(); + 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 + "]"; + } + } +} diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/CombinedTexture.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/CombinedTexture.java new file mode 100644 index 000000000..c78019cb4 --- /dev/null +++ b/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 textureDatas = new ArrayList(); + + /** The result texture. */ + private Texture resultTexture; + /** The UV values for the result texture. */ + private List 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 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 geometries = (List) 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 textureUVS = null; + if (textureData.uvCoordinatesType != UVCoordinatesType.TEXCO_UV) { + List geometries = (List) 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 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 true if the texture has no alpha channel and false + * 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; + } +} diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/DDSTexelData.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/DDSTexelData.java new file mode 100644 index 000000000..288d687da --- /dev/null +++ b/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); + } +} diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/GeneratedTexture.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/GeneratedTexture.java new file mode 100644 index 000000000..18884793b --- /dev/null +++ b/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 geometries = (List) 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 uvs = UVCoordinatesGenerator.generateUVCoordinatesFor3DTexture(mesh, coordinatesType, coordinatesSwappingIndexes, geometries); + Vector3f[] uvsArray = uvs.toArray(new Vector3f[uvs.size()]); + BoundingBox boundingBox = UVCoordinatesGenerator.getBoundingBox(geometries); + Set triangleTextureElements = new TreeSet(new Comparator() { + @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; + } + } +} diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGenerator.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGenerator.java deleted file mode 100644 index a7272770e..000000000 --- a/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGenerator.java +++ /dev/null @@ -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 cbDataMap = new TreeMap(); - for(int i=0;i 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 data = (DynamicArray) 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(); - } - } -} diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorClouds.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorClouds.java deleted file mode 100644 index 9ac24e5f9..000000000 --- a/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorClouds.java +++ /dev/null @@ -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 dataArray = new ArrayList(1); - dataArray.add(BufferUtils.createByteBuffer(data)); - return new Texture3D(new Image(format, width, height, depth, dataArray)); - } -} diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorNoise.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorNoise.java deleted file mode 100644 index 02d4687e1..000000000 --- a/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorNoise.java +++ /dev/null @@ -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 dataArray = new ArrayList(1); - dataArray.add(BufferUtils.createByteBuffer(data)); - return new Texture3D(new Image(format, width, height, depth, dataArray)); - } -} diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorStucci.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorStucci.java deleted file mode 100644 index 76a6614d6..000000000 --- a/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorStucci.java +++ /dev/null @@ -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 dataArray = new ArrayList(1); - dataArray.add(BufferUtils.createByteBuffer(data)); - return new Texture3D(new Image(format, width, height, depth, dataArray)); - } -} diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorVoronoi.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorVoronoi.java deleted file mode 100644 index 1dee1586e..000000000 --- a/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorVoronoi.java +++ /dev/null @@ -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 dataArray = new ArrayList(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); - } -} diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureHelper.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureHelper.java index 0e0861d66..f3f812e59 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureHelper.java +++ b/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 textureGenerators = new HashMap(); + 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 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 arrayData = new ArrayList(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 assetNames = new ArrayList(); 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()); } } diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/TexturePixel.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/TexturePixel.java index 37c0122cc..6ed177810 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/textures/TexturePixel.java +++ b/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; } /** diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/TriangulatedTexture.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/TriangulatedTexture.java new file mode 100644 index 000000000..449e529f5 --- /dev/null +++ b/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 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 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 uvs, BlenderContext blenderContext) { + maxTextureSize = blenderContext.getBlenderKey().getMaxTextureSize(); + faceTextures = new TreeSet(new Comparator() { + @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 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 list = new ArrayList(faceTextures); + Collections.sort(list, new Comparator() { + @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 duplicatedFaceIndexes = new HashSet(); + int resultImageHeight = list.get(0).image.getHeight(); + int resultImageWidth = 0; + int currentXPos = 0, currentYPos = 0; + Map imageLayoutData = new HashMap(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(imageLayoutData.size() * 3); + for (int i = 0; i < imageLayoutData.size() * 3; ++i) { + resultUVS.add(null); + } + Vector2f[] uvs = new Vector2f[3]; + for (Entry 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 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 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 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; + } +} diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/UVCoordinatesGenerator.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/UVCoordinatesGenerator.java index 56e429310..a8d4a0173 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/textures/UVCoordinatesGenerator.java +++ b/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 geometries) { - if (textureDimension != 2 && textureDimension != 3) { - throw new IllegalStateException("Unsupported texture dimension: " + textureDimension); + public static List generateUVCoordinatesFor2DTexture(Mesh mesh, UVCoordinatesType texco, UVProjectionType projection, List geometries) { + List result = new ArrayList(); + 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 generateUVCoordinatesFor3DTexture(Mesh mesh, UVCoordinatesType texco, int[] coordinatesSwappingIndexes, List geometries) { + List result = new ArrayList(); 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 geometries) { + public static BoundingBox getBoundingBox(List 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 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 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() { diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/UVProjectionGenerator.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/UVProjectionGenerator.java index 4412d608a..ddab4a117 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/textures/UVProjectionGenerator.java +++ b/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-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-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; } } } diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/AbstractTextureBlender.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/AbstractTextureBlender.java index 505c94b56..d22d6d7e6 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/AbstractTextureBlender.java +++ b/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()); + } + } } diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlender.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlender.java index 08e25d32a..30951850f 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlender.java +++ b/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); } diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderAWT.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderAWT.java index 918072e41..515a20da5 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderAWT.java +++ b/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 dataArray = new ArrayList(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; } } diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderDDS.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderDDS.java index a532cfb37..7a81cbde2 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderDDS.java +++ b/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; *
  • 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 dataArray = new ArrayList(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); } } } diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderFactory.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderFactory.java index edc5cdad6..95877773e 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderFactory.java +++ b/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; + } } diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderLuminance.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderLuminance.java index a616685a3..d2d3513bd 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderLuminance.java +++ b/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 dataArray = new ArrayList(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); } } diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/NoiseGenerator.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/generating/NoiseGenerator.java similarity index 95% rename from engine/src/blender/com/jme3/scene/plugins/blender/textures/NoiseGenerator.java rename to engine/src/blender/com/jme3/scene/plugins/blender/textures/generating/NoiseGenerator.java index a8bd890ce..c3aa9b66a 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/textures/NoiseGenerator.java +++ b/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) { diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/generating/TextureGenerator.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/generating/TextureGenerator.java new file mode 100644 index 000000000..446594c92 --- /dev/null +++ b/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(); + } + } +} diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorBlend.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/generating/TextureGeneratorBlend.java similarity index 63% rename from engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorBlend.java rename to engine/src/blender/com/jme3/scene/plugins/blender/textures/generating/TextureGeneratorBlend.java index 18ef40921..5a5383417 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorBlend.java +++ b/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 dataArray = new ArrayList(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); } diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/generating/TextureGeneratorClouds.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/generating/TextureGeneratorClouds.java new file mode 100644 index 000000000..4ca730eb6 --- /dev/null +++ b/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); + } + } +} diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorDistnoise.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/generating/TextureGeneratorDistnoise.java similarity index 56% rename from engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorDistnoise.java rename to engine/src/blender/com/jme3/scene/plugins/blender/textures/generating/TextureGeneratorDistnoise.java index f66beb6e0..170ffc9b7 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorDistnoise.java +++ b/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 dataArray = new ArrayList(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. diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/generating/TextureGeneratorFactory.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/generating/TextureGeneratorFactory.java new file mode 100644 index 000000000..c580863a8 --- /dev/null +++ b/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); + } + } +} diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorMagic.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/generating/TextureGeneratorMagic.java similarity index 61% rename from engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorMagic.java rename to engine/src/blender/com/jme3/scene/plugins/blender/textures/generating/TextureGeneratorMagic.java index e8a1637e1..856ffc9fd 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorMagic.java +++ b/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 0) { + xyz[0] *= turb; + xyz[1] *= turb; + xyz[2] *= turb; + for (int m=0;m dataArray = new ArrayList(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 { diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorMarble.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/generating/TextureGeneratorMarble.java similarity index 64% rename from engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorMarble.java rename to engine/src/blender/com/jme3/scene/plugins/blender/textures/generating/TextureGeneratorMarble.java index 06c08a911..46901fa03 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorMarble.java +++ b/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 dataArray = new ArrayList(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) { diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorMusgrave.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/generating/TextureGeneratorMusgrave.java similarity index 51% rename from engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorMusgrave.java rename to engine/src/blender/com/jme3/scene/plugins/blender/textures/generating/TextureGeneratorMusgrave.java index 667063f69..cfc884e63 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorMusgrave.java +++ b/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 dataArray = new ArrayList(1); - dataArray.add(BufferUtils.createByteBuffer(data)); - return new Texture3D(new Image(format, width, height, depth, dataArray)); } protected static class MusgraveData { diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/generating/TextureGeneratorNoise.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/generating/TextureGeneratorNoise.java new file mode 100644 index 000000000..4fcf44977 --- /dev/null +++ b/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); + } + } +} diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/generating/TextureGeneratorStucci.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/generating/TextureGeneratorStucci.java new file mode 100644 index 000000000..9dce39951 --- /dev/null +++ b/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) + } +} diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/generating/TextureGeneratorVoronoi.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/generating/TextureGeneratorVoronoi.java new file mode 100644 index 000000000..9f5a8d671 --- /dev/null +++ b/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); + } + } +} diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorWood.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/generating/TextureGeneratorWood.java similarity index 72% rename from engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorWood.java rename to engine/src/blender/com/jme3/scene/plugins/blender/textures/generating/TextureGeneratorWood.java index bf5e050cc..8902a72ae 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorWood.java +++ b/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 dataArray = new ArrayList(1); - dataArray.add(BufferUtils.createByteBuffer(data)); - return new Texture3D(new Image(format, width, height, depth, dataArray)); } protected static WaveForm[] waveformFunctions = new WaveForm[3]; diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/noiseconstants.dat b/engine/src/blender/com/jme3/scene/plugins/blender/textures/generating/noiseconstants.dat similarity index 100% rename from engine/src/blender/com/jme3/scene/plugins/blender/textures/noiseconstants.dat rename to engine/src/blender/com/jme3/scene/plugins/blender/textures/generating/noiseconstants.dat diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/io/AWTPixelInputOutput.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/io/AWTPixelInputOutput.java new file mode 100644 index 000000000..a7a817ea6 --- /dev/null +++ b/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); + } +} diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/io/DDSPixelInputOutput.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/io/DDSPixelInputOutput.java new file mode 100644 index 000000000..be7f0100f --- /dev/null +++ b/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!"); + } +} diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/io/LuminancePixelInputOutput.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/io/LuminancePixelInputOutput.java new file mode 100644 index 000000000..44a49a9d7 --- /dev/null +++ b/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); + } +} diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/io/PixelIOFactory.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/io/PixelIOFactory.java new file mode 100644 index 000000000..b8f855e3b --- /dev/null +++ b/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 PIXEL_INPUT_OUTPUT = new HashMap(); + + /** + * 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; + } +} diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/io/PixelInputOutput.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/io/PixelInputOutput.java new file mode 100644 index 000000000..d259cd170 --- /dev/null +++ b/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); +}