Textures loading refactoring.
New features: - support for loading both 2D and 3D textures (they are merged and flattened to Texture2D) - support for colorband usage in flat textures - support for using color factors for flat images Bugfixes: - blend texture should be now calculated properly at the ends of the object - blend texture is now properly directed - flat texture projection (flat, bude and tube) should now be properly directed Other stuff: - better code separation and improved readability git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@9329 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
This commit is contained in:
parent
f669290a3a
commit
8de8bf2d3e
@ -1,7 +0,0 @@
|
||||
uniform sampler3D m_Texture;
|
||||
|
||||
varying vec3 texCoord;
|
||||
|
||||
void main(){
|
||||
gl_FragColor= texture3D(m_Texture,texCoord);
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -53,7 +53,6 @@ import com.jme3.scene.plugins.blender.file.BlenderInputStream;
|
||||
import com.jme3.scene.plugins.blender.file.DnaBlockData;
|
||||
import com.jme3.scene.plugins.blender.file.FileBlockHeader;
|
||||
import com.jme3.scene.plugins.blender.file.Structure;
|
||||
import com.jme3.scene.plugins.blender.materials.MaterialContext;
|
||||
import com.jme3.scene.plugins.blender.meshes.MeshContext;
|
||||
import com.jme3.scene.plugins.blender.modifiers.Modifier;
|
||||
import com.jme3.scene.plugins.ogre.AnimData;
|
||||
@ -117,8 +116,6 @@ public class BlenderContext {
|
||||
protected Map<Long, MeshContext> meshContexts = new HashMap<Long, MeshContext>();
|
||||
/** A map of bone contexts. */
|
||||
protected Map<Long, BoneContext> boneContexts = new HashMap<Long, BoneContext>();
|
||||
/** A map of material contexts. */
|
||||
protected Map<Material, MaterialContext> materialContexts = new HashMap<Material, MaterialContext>();
|
||||
/** A map og helpers that perform loading. */
|
||||
private Map<String, AbstractBlenderHelper> helpers = new HashMap<String, AbstractBlenderHelper>();
|
||||
|
||||
@ -589,31 +586,6 @@ public class BlenderContext {
|
||||
return boneContexts.get(boneOMA);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method sets the material context for the given material. If the
|
||||
* context is already set it will be replaced.
|
||||
*
|
||||
* @param material
|
||||
* the material
|
||||
* @param materialContext
|
||||
* the material's context
|
||||
*/
|
||||
public void setMaterialContext(Material material, MaterialContext materialContext) {
|
||||
this.materialContexts.put(material, materialContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns the material context for the given material. If no
|
||||
* context exists then <b>null</b> is returned.
|
||||
*
|
||||
* @param material
|
||||
* the material
|
||||
* @return material's context
|
||||
*/
|
||||
public MaterialContext getMaterialContext(Material material) {
|
||||
return materialContexts.get(material);
|
||||
}
|
||||
|
||||
/**
|
||||
* This metod returns the default material.
|
||||
*
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -1,6 +1,22 @@
|
||||
package com.jme3.scene.plugins.blender.materials;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import com.jme3.material.Material;
|
||||
import com.jme3.material.RenderState.BlendMode;
|
||||
import com.jme3.material.RenderState.FaceCullMode;
|
||||
import com.jme3.math.ColorRGBA;
|
||||
import com.jme3.math.Vector2f;
|
||||
import com.jme3.renderer.queue.RenderQueue.Bucket;
|
||||
import com.jme3.scene.Geometry;
|
||||
import com.jme3.scene.VertexBuffer;
|
||||
import com.jme3.scene.VertexBuffer.Format;
|
||||
import com.jme3.scene.VertexBuffer.Usage;
|
||||
import com.jme3.scene.plugins.blender.BlenderContext;
|
||||
import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
|
||||
import com.jme3.scene.plugins.blender.file.DynamicArray;
|
||||
@ -8,19 +24,12 @@ import com.jme3.scene.plugins.blender.file.Pointer;
|
||||
import com.jme3.scene.plugins.blender.file.Structure;
|
||||
import com.jme3.scene.plugins.blender.materials.MaterialHelper.DiffuseShader;
|
||||
import com.jme3.scene.plugins.blender.materials.MaterialHelper.SpecularShader;
|
||||
import com.jme3.scene.plugins.blender.textures.CombinedTexture;
|
||||
import com.jme3.scene.plugins.blender.textures.TextureHelper;
|
||||
import com.jme3.scene.plugins.blender.textures.blending.TextureBlender;
|
||||
import com.jme3.scene.plugins.blender.textures.blending.TextureBlenderFactory;
|
||||
import com.jme3.texture.Texture;
|
||||
import com.jme3.texture.Texture.Type;
|
||||
import com.jme3.texture.Texture.WrapMode;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import com.jme3.util.BufferUtils;
|
||||
|
||||
/**
|
||||
* This class holds the data about the material.
|
||||
@ -37,12 +46,7 @@ public final class MaterialContext {
|
||||
public static final int MTEX_ALPHA = 0x80;
|
||||
|
||||
/* package */final String name;
|
||||
/* package */final List<Structure> mTexs;
|
||||
/* package */final List<Structure> textures;
|
||||
/* package */final Map<Number, Texture> loadedTextures;
|
||||
/* package */final Map<Texture, Structure> textureToMTexMap;
|
||||
/* package */final int texturesCount;
|
||||
/* package */final Type textureType;
|
||||
/* package */final Map<Number, CombinedTexture> loadedTextures;
|
||||
|
||||
/* package */final ColorRGBA diffuseColor;
|
||||
/* package */final DiffuseShader diffuseShader;
|
||||
@ -54,10 +58,8 @@ public final class MaterialContext {
|
||||
/* package */final boolean vertexColor;
|
||||
/* package */final boolean transparent;
|
||||
/* package */final boolean vTangent;
|
||||
|
||||
/* package */int uvCoordinatesType = -1;
|
||||
/* package */int projectionType;
|
||||
|
||||
/* package */FaceCullMode faceCullMode;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
/* package */MaterialContext(Structure structure, BlenderContext blenderContext) throws BlenderFileException {
|
||||
name = structure.getName();
|
||||
@ -97,85 +99,61 @@ public final class MaterialContext {
|
||||
this.shininess = shininess > 0.0f ? shininess : MaterialHelper.DEFAULT_SHININESS;
|
||||
}
|
||||
|
||||
mTexs = new ArrayList<Structure>();
|
||||
textures = new ArrayList<Structure>();
|
||||
|
||||
DynamicArray<Pointer> mtexsArray = (DynamicArray<Pointer>) structure.getFieldValue("mtex");
|
||||
int separatedTextures = ((Number) structure.getFieldValue("septex")).intValue();
|
||||
Type firstTextureType = null;
|
||||
List<TextureData> texturesList = new ArrayList<TextureData>();
|
||||
for (int i = 0; i < mtexsArray.getTotalSize(); ++i) {
|
||||
Pointer p = mtexsArray.get(i);
|
||||
if (p.isNotNull() && (separatedTextures & 1 << i) == 0) {
|
||||
Structure mtex = p.fetchData(blenderContext.getInputStream()).get(0);
|
||||
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();
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
Pointer pTex = (Pointer) mtex.getFieldValue("tex");
|
||||
Pointer pTex = (Pointer) textureData.mtex.getFieldValue("tex");
|
||||
if (pTex.isNotNull()) {
|
||||
Structure tex = pTex.fetchData(blenderContext.getInputStream()).get(0);
|
||||
int type = ((Number) tex.getFieldValue("type")).intValue();
|
||||
Type textureType = this.getType(type);
|
||||
if (textureType != null) {
|
||||
if (firstTextureType == null) {
|
||||
firstTextureType = textureType;
|
||||
mTexs.add(mtex);
|
||||
textures.add(tex);
|
||||
} else if (firstTextureType == textureType) {
|
||||
mTexs.add(mtex);
|
||||
textures.add(tex);
|
||||
} else {
|
||||
LOGGER.log(Level.WARNING, "The texture with index: {0} is of different dimension than the first one! This texture will NOT be loaded!", i + 1);
|
||||
}
|
||||
}
|
||||
textureData.textureStructure = tex;
|
||||
texturesList.add(textureData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//loading the textures and merging them
|
||||
Map<Number, List<Structure[]>> sortedTextures = this.sortAndFilterTextures();
|
||||
loadedTextures = new HashMap<Number, Texture>(sortedTextures.size());
|
||||
textureToMTexMap = new HashMap<Texture, Structure>();
|
||||
Map<Number, List<TextureData>> textureDataMap = this.sortAndFilterTextures(texturesList);
|
||||
loadedTextures = new HashMap<Number, CombinedTexture>();
|
||||
float[] diffuseColorArray = new float[] {diffuseColor.r, diffuseColor.g, diffuseColor.b, diffuseColor.a};
|
||||
TextureHelper textureHelper = blenderContext.getHelper(TextureHelper.class);
|
||||
for(Entry<Number, List<Structure[]>> entry : sortedTextures.entrySet()) {
|
||||
for(Entry<Number, List<TextureData>> entry : textureDataMap.entrySet()) {
|
||||
if(entry.getValue().size()>0) {
|
||||
List<Texture> textures = new ArrayList<Texture>(entry.getValue().size());
|
||||
for(Structure[] mtexAndTex : entry.getValue()) {
|
||||
int texflag = ((Number) mtexAndTex[0].getFieldValue("texflag")).intValue();
|
||||
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]);
|
||||
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(textureData.textureStructure, textureData.mtex, blenderContext);
|
||||
int blendType = ((Number) textureData.mtex.getFieldValue("blendtype")).intValue();
|
||||
float[] color = new float[] { ((Number) textureData.mtex.getFieldValue("r")).floatValue(),
|
||||
((Number) textureData.mtex.getFieldValue("g")).floatValue(),
|
||||
((Number) textureData.mtex.getFieldValue("b")).floatValue() };
|
||||
float colfac = ((Number) textureData.mtex.getFieldValue("colfac")).floatValue();
|
||||
TextureBlender textureBlender = TextureBlenderFactory.createTextureBlender(texture.getImage().getFormat(),
|
||||
texflag, negateTexture, blendType, diffuseColorArray, color, colfac);
|
||||
combinedTexture.add(texture, textureBlender, textureData.uvCoordinatesType, textureData.projectionType, textureData.textureStructure, blenderContext);
|
||||
}
|
||||
loadedTextures.put(entry.getKey(), textureHelper.mergeTextures(textures, this));
|
||||
}
|
||||
}
|
||||
|
||||
this.texturesCount = mTexs.size();
|
||||
this.textureType = firstTextureType;
|
||||
|
||||
//veryfying if the transparency is present
|
||||
//(in blender transparent mask is 0x10000 but its better to verify it because blender can indicate transparency when
|
||||
//it is not required
|
||||
boolean transparent = false;
|
||||
if(diffuseColor != null) {
|
||||
transparent = diffuseColor.a < 1.0f;
|
||||
if(sortedTextures.size() > 0) {//texutre covers the material color
|
||||
if(textureDataMap.size() > 0) {//texutre covers the material color
|
||||
diffuseColor.set(1, 1, 1, 1);
|
||||
}
|
||||
}
|
||||
@ -188,6 +166,94 @@ public final class MaterialContext {
|
||||
this.transparent = transparent;
|
||||
}
|
||||
|
||||
public void applyMaterial(Geometry geometry, Long geometriesOMA, boolean noTextures, List<Vector2f> userDefinedUVCoordinates, BlenderContext blenderContext) {
|
||||
Material material = null;
|
||||
if (shadeless) {
|
||||
material = new Material(blenderContext.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");
|
||||
|
||||
if (!transparent) {
|
||||
diffuseColor.a = 1;
|
||||
}
|
||||
|
||||
material.setColor("Color", diffuseColor);
|
||||
} else {
|
||||
material = new Material(blenderContext.getAssetManager(), "Common/MatDefs/Light/Lighting.j3md");
|
||||
material.setBoolean("UseMaterialColors", Boolean.TRUE);
|
||||
|
||||
// setting the colors
|
||||
material.setBoolean("Minnaert", diffuseShader == DiffuseShader.MINNAERT);
|
||||
if (!transparent) {
|
||||
diffuseColor.a = 1;
|
||||
}
|
||||
material.setColor("Diffuse", diffuseColor);
|
||||
|
||||
material.setBoolean("WardIso", specularShader == SpecularShader.WARDISO);
|
||||
material.setColor("Specular", specularColor);
|
||||
|
||||
material.setColor("Ambient", ambientColor);
|
||||
material.setFloat("Shininess", shininess);
|
||||
}
|
||||
|
||||
//applying textures
|
||||
for(Entry<Number, CombinedTexture> entry : loadedTextures.entrySet()) {
|
||||
CombinedTexture combinedTexture = entry.getValue();
|
||||
combinedTexture.flatten(geometry, geometriesOMA, userDefinedUVCoordinates, blenderContext);
|
||||
VertexBuffer.Type uvCoordinatesType = null;
|
||||
|
||||
switch(entry.getKey().intValue()) {
|
||||
case MTEX_COL:
|
||||
uvCoordinatesType = VertexBuffer.Type.TexCoord;
|
||||
material.setTexture(shadeless ? MaterialHelper.TEXTURE_TYPE_COLOR : MaterialHelper.TEXTURE_TYPE_DIFFUSE,
|
||||
combinedTexture.getResultTexture());
|
||||
break;
|
||||
case MTEX_NOR:
|
||||
uvCoordinatesType = VertexBuffer.Type.TexCoord2;
|
||||
material.setTexture(MaterialHelper.TEXTURE_TYPE_NORMAL, combinedTexture.getResultTexture());
|
||||
break;
|
||||
case MTEX_SPEC:
|
||||
uvCoordinatesType = VertexBuffer.Type.TexCoord3;
|
||||
material.setTexture(MaterialHelper.TEXTURE_TYPE_SPECULAR, combinedTexture.getResultTexture());
|
||||
break;
|
||||
case MTEX_EMIT:
|
||||
uvCoordinatesType = VertexBuffer.Type.TexCoord4;
|
||||
material.setTexture(MaterialHelper.TEXTURE_TYPE_GLOW, combinedTexture.getResultTexture());
|
||||
break;
|
||||
case MTEX_ALPHA:
|
||||
uvCoordinatesType = VertexBuffer.Type.TexCoord5;
|
||||
material.setTexture(MaterialHelper.TEXTURE_TYPE_ALPHA, combinedTexture.getResultTexture());
|
||||
break;
|
||||
default:
|
||||
LOGGER.severe("Unknown mapping type: " + entry.getKey().intValue());
|
||||
}
|
||||
|
||||
//applying texture coordinates
|
||||
if(uvCoordinatesType != null) {
|
||||
VertexBuffer uvCoordsBuffer = new VertexBuffer(uvCoordinatesType);
|
||||
uvCoordsBuffer.setupData(Usage.Static, 2, Format.Float,
|
||||
BufferUtils.createFloatBuffer(combinedTexture.getResultUVS().toArray(new Vector2f[combinedTexture.getResultUVS().size()])));
|
||||
geometry.getMesh().setBuffer(uvCoordsBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
//applying additional data
|
||||
material.setName(name);
|
||||
if (vertexColor) {
|
||||
material.setBoolean(shadeless ? "VertexColor" : "UseVertexColor", true);
|
||||
}
|
||||
if(this.faceCullMode != null) {
|
||||
material.getAdditionalRenderState().setFaceCullMode(faceCullMode);
|
||||
} else {
|
||||
material.getAdditionalRenderState().setFaceCullMode(blenderContext.getBlenderKey().getFaceCullMode());
|
||||
}
|
||||
if (transparent) {
|
||||
material.setTransparent(true);
|
||||
material.getAdditionalRenderState().setBlendMode(BlendMode.Alpha);
|
||||
geometry.setQueueBucket(Bucket.Transparent);
|
||||
}
|
||||
|
||||
geometry.setMaterial(material);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method sorts the textures by their mapping type.
|
||||
* In each group only textures of one type are put (either two- or three-dimensional).
|
||||
@ -195,100 +261,33 @@ public final class MaterialContext {
|
||||
* discarded and will not be loaded and merged because texture with no alpha will cover them anyway.
|
||||
* @return a map with sorted and filtered textures
|
||||
*/
|
||||
private Map<Number, List<Structure[]>> sortAndFilterTextures() {
|
||||
Map<Number, List<Structure[]>> result = new HashMap<Number, List<Structure[]>>();
|
||||
for (int i = 0; i < mTexs.size(); ++i) {
|
||||
Structure mTex = mTexs.get(i);
|
||||
Structure texture = textures.get(i);
|
||||
Number mapto = (Number) mTex.getFieldValue("mapto");
|
||||
List<Structure[]> mtexs = result.get(mapto);
|
||||
if(mtexs==null) {
|
||||
mtexs = new ArrayList<Structure[]>();
|
||||
result.put(mapto, mtexs);
|
||||
private Map<Number, List<TextureData>> sortAndFilterTextures(List<TextureData> textures) {
|
||||
Map<Number, List<TextureData>> result = new HashMap<Number, List<TextureData>>();
|
||||
for (TextureData data : textures) {
|
||||
Number mapto = (Number) data.mtex.getFieldValue("mapto");
|
||||
List<TextureData> datas = result.get(mapto);
|
||||
if(datas==null) {
|
||||
datas = new ArrayList<TextureData>();
|
||||
result.put(mapto, datas);
|
||||
}
|
||||
if(mapto.intValue() == MTEX_COL && this.isWithoutAlpha(textures.get(i))) {
|
||||
mtexs.clear();//remove previous textures, they will be covered anyway
|
||||
}
|
||||
mtexs.add(new Structure[] {mTex, texture});
|
||||
datas.add(data);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method determines if the given texture has no alpha channel.
|
||||
*
|
||||
* @param texture
|
||||
* the texture to check for alpha channel
|
||||
* @return <b>true</b> if the texture has no alpha channel and <b>false</b>
|
||||
* otherwise
|
||||
* This method sets the face cull mode.
|
||||
* @param faceCullMode the face cull mode
|
||||
*/
|
||||
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;
|
||||
public void setFaceCullMode(FaceCullMode faceCullMode) {
|
||||
this.faceCullMode = faceCullMode;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This method returns the current material's texture UV coordinates type.
|
||||
* @return uv coordinates type
|
||||
* @return the face cull mode
|
||||
*/
|
||||
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
|
||||
*/
|
||||
public int getTexturesCount() {
|
||||
return textures == null ? 0 : textures.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public int[] getProjection(int textureIndex) {
|
||||
Structure mtex = mTexs.get(textureIndex);
|
||||
return new int[] { ((Number) mtex.getFieldValue("projx")).intValue(), ((Number) mtex.getFieldValue("projy")).intValue(), ((Number) mtex.getFieldValue("projz")).intValue() };
|
||||
public FaceCullMode getFaceCullMode() {
|
||||
return faceCullMode;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -358,126 +357,11 @@ public final class MaterialContext {
|
||||
}
|
||||
return new ColorRGBA(r, g, b, alpha);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method determines the type of the texture.
|
||||
* @param texType
|
||||
* texture type (from blender)
|
||||
* @return texture type (used by jme)
|
||||
*/
|
||||
private Type getType(int texType) {
|
||||
switch (texType) {
|
||||
case TextureHelper.TEX_IMAGE:// (it is first because probably this will be most commonly used)
|
||||
return Type.TwoDimensional;
|
||||
case TextureHelper.TEX_CLOUDS:
|
||||
case TextureHelper.TEX_WOOD:
|
||||
case TextureHelper.TEX_MARBLE:
|
||||
case TextureHelper.TEX_MAGIC:
|
||||
case TextureHelper.TEX_BLEND:
|
||||
case TextureHelper.TEX_STUCCI:
|
||||
case TextureHelper.TEX_NOISE:
|
||||
case TextureHelper.TEX_MUSGRAVE:
|
||||
case TextureHelper.TEX_VORONOI:
|
||||
case TextureHelper.TEX_DISTNOISE:
|
||||
return Type.ThreeDimensional;
|
||||
case TextureHelper.TEX_NONE:// No texture, do nothing
|
||||
return null;
|
||||
case TextureHelper.TEX_POINTDENSITY:
|
||||
case TextureHelper.TEX_VOXELDATA:
|
||||
case TextureHelper.TEX_PLUGIN:
|
||||
case TextureHelper.TEX_ENVMAP:
|
||||
LOGGER.log(Level.WARNING, "Texture type NOT supported: {0}", texType);
|
||||
return null;
|
||||
default:
|
||||
throw new IllegalStateException("Unknown texture type: " + texType);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return he material's name
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a copy of diffuse color
|
||||
*/
|
||||
public ColorRGBA getDiffuseColor() {
|
||||
return diffuseColor.clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return an enum describing the type of a diffuse shader used by this material
|
||||
*/
|
||||
public DiffuseShader getDiffuseShader() {
|
||||
return diffuseShader;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a copy of specular color
|
||||
*/
|
||||
public ColorRGBA getSpecularColor() {
|
||||
return specularColor.clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return an enum describing the type of a specular shader used by this material
|
||||
*/
|
||||
public SpecularShader getSpecularShader() {
|
||||
return specularShader;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return an ambient color used by the material
|
||||
*/
|
||||
public ColorRGBA getAmbientColor() {
|
||||
return ambientColor;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the sihiness of this material
|
||||
*/
|
||||
public float getShininess() {
|
||||
return shininess;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return <b>true</b> if the material is shadeless and <b>false</b> otherwise
|
||||
*/
|
||||
public boolean isShadeless() {
|
||||
return shadeless;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return <b>true</b> if the material uses vertex color and <b>false</b> otherwise
|
||||
*/
|
||||
public boolean isVertexColor() {
|
||||
return vertexColor;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return <b>true</b> if the material is transparent and <b>false</b> otherwise
|
||||
*/
|
||||
public boolean isTransparent() {
|
||||
return transparent;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return <b>true</b> if the material uses tangents and <b>false</b> otherwise
|
||||
*/
|
||||
public boolean isvTangent() {
|
||||
return vTangent;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param texture
|
||||
* the texture for which its mtex structure definition will be
|
||||
* fetched
|
||||
* @return mtex structure of the current texture or <b>null</b> if none
|
||||
* exists
|
||||
*/
|
||||
public Structure getMTex(Texture texture) {
|
||||
return textureToMTexMap.get(texture);
|
||||
private static class TextureData {
|
||||
Structure mtex;
|
||||
Structure textureStructure;
|
||||
int uvCoordinatesType;
|
||||
int projectionType;
|
||||
}
|
||||
}
|
||||
|
@ -31,12 +31,18 @@
|
||||
*/
|
||||
package com.jme3.scene.plugins.blender.materials;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import com.jme3.asset.BlenderKey.FeaturesToLoad;
|
||||
import com.jme3.material.MatParam;
|
||||
import com.jme3.material.MatParamTexture;
|
||||
import com.jme3.material.Material;
|
||||
import com.jme3.material.RenderState.BlendMode;
|
||||
import com.jme3.material.RenderState.FaceCullMode;
|
||||
import com.jme3.math.ColorRGBA;
|
||||
import com.jme3.math.FastMath;
|
||||
import com.jme3.scene.plugins.blender.AbstractBlenderHelper;
|
||||
@ -49,21 +55,12 @@ import com.jme3.shader.VarType;
|
||||
import com.jme3.texture.Image;
|
||||
import com.jme3.texture.Image.Format;
|
||||
import com.jme3.texture.Texture;
|
||||
import com.jme3.texture.Texture.Type;
|
||||
import com.jme3.util.BufferUtils;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class MaterialHelper extends AbstractBlenderHelper {
|
||||
private static final Logger LOGGER = Logger.getLogger(MaterialHelper.class.getName());
|
||||
protected static final float DEFAULT_SHININESS = 20.0f;
|
||||
|
||||
public static final String TEXTURE_TYPE_3D = "Texture";
|
||||
public static final String TEXTURE_TYPE_COLOR = "ColorMap";
|
||||
public static final String TEXTURE_TYPE_DIFFUSE = "DiffuseMap";
|
||||
public static final String TEXTURE_TYPE_NORMAL = "NormalMap";
|
||||
@ -91,9 +88,6 @@ public class MaterialHelper extends AbstractBlenderHelper {
|
||||
COOKTORRENCE, PHONG, BLINN, TOON, WARDISO
|
||||
}
|
||||
|
||||
/** Face cull mode. Should be excplicitly set before this helper is used. */
|
||||
protected FaceCullMode faceCullMode;
|
||||
|
||||
/**
|
||||
* This constructor parses the given blender version and stores the result. Some functionalities may differ in different blender
|
||||
* versions.
|
||||
@ -165,16 +159,6 @@ public class MaterialHelper extends AbstractBlenderHelper {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* This method sets the face cull mode to be used with every loaded material.
|
||||
*
|
||||
* @param faceCullMode
|
||||
* the face cull mode
|
||||
*/
|
||||
public void setFaceCullMode(FaceCullMode faceCullMode) {
|
||||
this.faceCullMode = faceCullMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method converts the material structure to jme Material.
|
||||
* @param structure
|
||||
@ -185,105 +169,15 @@ public class MaterialHelper extends AbstractBlenderHelper {
|
||||
* @throws BlenderFileException
|
||||
* an exception is throw when problems with blend file occur
|
||||
*/
|
||||
public Material toMaterial(Structure structure, BlenderContext blenderContext) throws BlenderFileException {
|
||||
public MaterialContext toMaterialContext(Structure structure, BlenderContext blenderContext) throws BlenderFileException {
|
||||
LOGGER.log(Level.INFO, "Loading material.");
|
||||
if (structure == null) {
|
||||
return blenderContext.getDefaultMaterial();
|
||||
}
|
||||
Material result = (Material) blenderContext.getLoadedFeature(structure.getOldMemoryAddress(), LoadedFeatureDataType.LOADED_FEATURE);
|
||||
MaterialContext result = (MaterialContext) blenderContext.getLoadedFeature(structure.getOldMemoryAddress(), LoadedFeatureDataType.LOADED_FEATURE);
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
|
||||
MaterialContext materialContext = new MaterialContext(structure, blenderContext);
|
||||
LOGGER.log(Level.INFO, "Material's name: {0}", materialContext.name);
|
||||
|
||||
if(materialContext.textures.size() > 1) {
|
||||
LOGGER.log(Level.WARNING, "Attetion! Many textures found for material: {0}. Only the first of each supported mapping types will be used!", materialContext.name);
|
||||
}
|
||||
|
||||
// texture
|
||||
Type colorTextureType = null;
|
||||
Map<String, Texture> texturesMap = new HashMap<String, Texture>();
|
||||
for(Entry<Number, Texture> textureEntry : materialContext.loadedTextures.entrySet()) {
|
||||
int mapto = textureEntry.getKey().intValue();
|
||||
Texture texture = textureEntry.getValue();
|
||||
if ((mapto & MaterialContext.MTEX_COL) != 0) {
|
||||
colorTextureType = texture.getType();
|
||||
if (materialContext.shadeless) {
|
||||
texturesMap.put(colorTextureType==Type.ThreeDimensional ? TEXTURE_TYPE_3D : TEXTURE_TYPE_COLOR, texture);
|
||||
} else {
|
||||
texturesMap.put(colorTextureType==Type.ThreeDimensional ? TEXTURE_TYPE_3D : TEXTURE_TYPE_DIFFUSE, texture);
|
||||
}
|
||||
}
|
||||
if(texture.getType()==Type.TwoDimensional) {//so far only 2D textures can be mapped in other way than color
|
||||
if ((mapto & MaterialContext.MTEX_NOR) != 0 && !materialContext.shadeless) {
|
||||
//Structure mTex = materialContext.getMTex(texture);
|
||||
//Texture normalMapTexture = textureHelper.convertToNormalMapTexture(texture, ((Number) mTex.getFieldValue("norfac")).floatValue());
|
||||
//texturesMap.put(TEXTURE_TYPE_NORMAL, normalMapTexture);
|
||||
texturesMap.put(TEXTURE_TYPE_NORMAL, texture);
|
||||
}
|
||||
if ((mapto & MaterialContext.MTEX_EMIT) != 0) {
|
||||
texturesMap.put(TEXTURE_TYPE_GLOW, texture);
|
||||
}
|
||||
if ((mapto & MaterialContext.MTEX_SPEC) != 0 && !materialContext.shadeless) {
|
||||
texturesMap.put(TEXTURE_TYPE_SPECULAR, texture);
|
||||
}
|
||||
if ((mapto & MaterialContext.MTEX_ALPHA) != 0 && !materialContext.shadeless) {
|
||||
texturesMap.put(TEXTURE_TYPE_ALPHA, texture);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//creating the material
|
||||
if(colorTextureType==Type.ThreeDimensional) {
|
||||
result = new Material(blenderContext.getAssetManager(), "Common/MatDefs/Texture3D/tex3D.j3md");
|
||||
} else {
|
||||
if (materialContext.shadeless) {
|
||||
result = new Material(blenderContext.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");
|
||||
|
||||
if (!materialContext.transparent) {
|
||||
materialContext.diffuseColor.a = 1;
|
||||
}
|
||||
|
||||
result.setColor("Color", materialContext.diffuseColor);
|
||||
} else {
|
||||
result = new Material(blenderContext.getAssetManager(), "Common/MatDefs/Light/Lighting.j3md");
|
||||
result.setBoolean("UseMaterialColors", Boolean.TRUE);
|
||||
|
||||
// setting the colors
|
||||
result.setBoolean("Minnaert", materialContext.diffuseShader == DiffuseShader.MINNAERT);
|
||||
if (!materialContext.transparent) {
|
||||
materialContext.diffuseColor.a = 1;
|
||||
}
|
||||
result.setColor("Diffuse", materialContext.diffuseColor);
|
||||
|
||||
result.setBoolean("WardIso", materialContext.specularShader == SpecularShader.WARDISO);
|
||||
result.setColor("Specular", materialContext.specularColor);
|
||||
|
||||
result.setColor("Ambient", materialContext.ambientColor);
|
||||
result.setFloat("Shininess", materialContext.shininess);
|
||||
}
|
||||
|
||||
if (materialContext.vertexColor) {
|
||||
result.setBoolean(materialContext.shadeless ? "VertexColor" : "UseVertexColor", true);
|
||||
}
|
||||
}
|
||||
|
||||
//applying textures
|
||||
for(Entry<String, Texture> textureEntry : texturesMap.entrySet()) {
|
||||
result.setTexture(textureEntry.getKey(), textureEntry.getValue());
|
||||
}
|
||||
|
||||
//applying other data
|
||||
result.getAdditionalRenderState().setFaceCullMode(faceCullMode);
|
||||
if (materialContext.transparent) {
|
||||
result.setTransparent(true);
|
||||
result.getAdditionalRenderState().setBlendMode(BlendMode.Alpha);
|
||||
}
|
||||
|
||||
result.setName(materialContext.getName());
|
||||
blenderContext.setMaterialContext(result, materialContext);
|
||||
result = new MaterialContext(structure, blenderContext);
|
||||
LOGGER.log(Level.INFO, "Material's name: {0}", result.name);
|
||||
blenderContext.addLoadedFeatures(structure.getOldMemoryAddress(), structure.getName(), structure, result);
|
||||
return result;
|
||||
}
|
||||
@ -392,9 +286,6 @@ public class MaterialHelper extends AbstractBlenderHelper {
|
||||
*/
|
||||
public boolean hasTexture(Material material) {
|
||||
if (material != null) {
|
||||
if (material.getTextureParam(TEXTURE_TYPE_3D) != null) {
|
||||
return true;
|
||||
}
|
||||
if (material.getTextureParam(TEXTURE_TYPE_ALPHA) != null) {
|
||||
return true;
|
||||
}
|
||||
@ -445,21 +336,17 @@ public class MaterialHelper extends AbstractBlenderHelper {
|
||||
* @throws BlenderFileException
|
||||
* this exception is thrown when the blend file structure is somehow invalid or corrupted
|
||||
*/
|
||||
public Material[] getMaterials(Structure structureWithMaterials, BlenderContext blenderContext) throws BlenderFileException {
|
||||
public MaterialContext[] getMaterials(Structure structureWithMaterials, BlenderContext blenderContext) throws BlenderFileException {
|
||||
Pointer ppMaterials = (Pointer) structureWithMaterials.getFieldValue("mat");
|
||||
Material[] materials = null;
|
||||
MaterialContext[] materials = null;
|
||||
if (ppMaterials.isNotNull()) {
|
||||
List<Structure> materialStructures = ppMaterials.fetchData(blenderContext.getInputStream());
|
||||
if (materialStructures != null && materialStructures.size() > 0) {
|
||||
MaterialHelper materialHelper = blenderContext.getHelper(MaterialHelper.class);
|
||||
materials = new Material[materialStructures.size()];
|
||||
materials = new MaterialContext[materialStructures.size()];
|
||||
int i = 0;
|
||||
for (Structure s : materialStructures) {
|
||||
Material material = (Material) blenderContext.getLoadedFeature(s.getOldMemoryAddress(), LoadedFeatureDataType.LOADED_FEATURE);
|
||||
if (material == null) {
|
||||
material = materialHelper.toMaterial(s, blenderContext);
|
||||
}
|
||||
materials[i++] = material;
|
||||
materials[i++] = materialHelper.toMaterialContext(s, blenderContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -40,11 +40,9 @@ import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import com.jme3.asset.BlenderKey.FeaturesToLoad;
|
||||
import com.jme3.material.Material;
|
||||
import com.jme3.math.FastMath;
|
||||
import com.jme3.math.Vector2f;
|
||||
import com.jme3.math.Vector3f;
|
||||
import com.jme3.renderer.queue.RenderQueue.Bucket;
|
||||
import com.jme3.scene.Geometry;
|
||||
import com.jme3.scene.Mesh;
|
||||
import com.jme3.scene.VertexBuffer;
|
||||
@ -62,7 +60,6 @@ import com.jme3.scene.plugins.blender.materials.MaterialContext;
|
||||
import com.jme3.scene.plugins.blender.materials.MaterialHelper;
|
||||
import com.jme3.scene.plugins.blender.objects.Properties;
|
||||
import com.jme3.scene.plugins.blender.textures.TextureHelper;
|
||||
import com.jme3.scene.plugins.blender.textures.UVCoordinatesGenerator;
|
||||
import com.jme3.texture.Texture;
|
||||
import com.jme3.util.BufferUtils;
|
||||
|
||||
@ -135,7 +132,7 @@ public class MeshHelper extends AbstractBlenderHelper {
|
||||
}
|
||||
|
||||
Pointer pMTFace = (Pointer) structure.getFieldValue("mtface");
|
||||
List<Vector2f> uvCoordinates = null;
|
||||
Map<Integer, List<Vector2f>> uvCoordinates = new HashMap<Integer, List<Vector2f>>();//<material_number; list of uv coordinates for mesh's vertices>
|
||||
List<Structure> mtFaces = null;
|
||||
|
||||
if (pMTFace.isNotNull()) {
|
||||
@ -144,7 +141,6 @@ public class MeshHelper extends AbstractBlenderHelper {
|
||||
if (mtFaces.size() != facesAmount) {
|
||||
throw new BlenderFileException("The amount of faces uv coordinates is not equal to faces amount!");
|
||||
}
|
||||
uvCoordinates = new ArrayList<Vector2f>();
|
||||
}
|
||||
|
||||
// normalMap merges normals of faces that will be rendered smooth
|
||||
@ -160,21 +156,28 @@ public class MeshHelper extends AbstractBlenderHelper {
|
||||
int vertexColorIndex = 0;
|
||||
for (int i = 0; i < mFaces.size(); ++i) {
|
||||
Structure mFace = mFaces.get(i);
|
||||
int matNr = ((Number) mFace.getFieldValue("mat_nr")).intValue();
|
||||
boolean smooth = (((Number) mFace.getFieldValue("flag")).byteValue() & 0x01) != 0x00;
|
||||
DynamicArray<Number> uvs = null;
|
||||
boolean materialWithoutTextures = false;
|
||||
Pointer pImage = null;
|
||||
|
||||
List<Vector2f> uvCoordinatesList = uvCoordinates.get(Integer.valueOf(matNr));
|
||||
if(uvCoordinatesList == null) {
|
||||
uvCoordinatesList = new ArrayList<Vector2f>();
|
||||
uvCoordinates.put(Integer.valueOf(matNr), uvCoordinatesList);
|
||||
}
|
||||
|
||||
if (mtFaces != null) {
|
||||
Structure mtFace = mtFaces.get(i);
|
||||
pImage = (Pointer) mtFace.getFieldValue("tpage");
|
||||
materialWithoutTextures = pImage.isNull();
|
||||
// uvs always must be added wheater we have texture or not
|
||||
uvs = (DynamicArray<Number>) mtFace.getFieldValue("uv");
|
||||
uvCoordinates.add(new Vector2f(uvs.get(0, 0).floatValue(), uvs.get(0, 1).floatValue()));
|
||||
uvCoordinates.add(new Vector2f(uvs.get(1, 0).floatValue(), uvs.get(1, 1).floatValue()));
|
||||
uvCoordinates.add(new Vector2f(uvs.get(2, 0).floatValue(), uvs.get(2, 1).floatValue()));
|
||||
uvCoordinatesList.add(new Vector2f(uvs.get(0, 0).floatValue(), uvs.get(0, 1).floatValue()));
|
||||
uvCoordinatesList.add(new Vector2f(uvs.get(1, 0).floatValue(), uvs.get(1, 1).floatValue()));
|
||||
uvCoordinatesList.add(new Vector2f(uvs.get(2, 0).floatValue(), uvs.get(2, 1).floatValue()));
|
||||
}
|
||||
int matNr = ((Number) mFace.getFieldValue("mat_nr")).intValue();
|
||||
Integer materialNumber = Integer.valueOf(materialWithoutTextures ? -1 * matNr - 1 : matNr);
|
||||
List<Integer> indexList = meshesMap.get(materialNumber);
|
||||
if (indexList == null) {
|
||||
@ -216,9 +219,9 @@ public class MeshHelper extends AbstractBlenderHelper {
|
||||
|
||||
if (v4 > 0) {
|
||||
if (uvs != null) {
|
||||
uvCoordinates.add(new Vector2f(uvs.get(0, 0).floatValue(), uvs.get(0, 1).floatValue()));
|
||||
uvCoordinates.add(new Vector2f(uvs.get(2, 0).floatValue(), uvs.get(2, 1).floatValue()));
|
||||
uvCoordinates.add(new Vector2f(uvs.get(3, 0).floatValue(), uvs.get(3, 1).floatValue()));
|
||||
uvCoordinatesList.add(new Vector2f(uvs.get(0, 0).floatValue(), uvs.get(0, 1).floatValue()));
|
||||
uvCoordinatesList.add(new Vector2f(uvs.get(2, 0).floatValue(), uvs.get(2, 1).floatValue()));
|
||||
uvCoordinatesList.add(new Vector2f(uvs.get(3, 0).floatValue(), uvs.get(3, 1).floatValue()));
|
||||
}
|
||||
this.appendVertexReference(v1, vertexList.size(), vertexReferenceMap);
|
||||
indexList.add(vertexList.size());
|
||||
@ -266,11 +269,9 @@ public class MeshHelper extends AbstractBlenderHelper {
|
||||
|
||||
// reading materials
|
||||
MaterialHelper materialHelper = blenderContext.getHelper(MaterialHelper.class);
|
||||
Material[] materials = null;
|
||||
Material[] nonTexturedMaterials = null;
|
||||
MaterialContext[] materials = null;
|
||||
if ((blenderContext.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.MATERIALS) != 0) {
|
||||
materials = materialHelper.getMaterials(structure, blenderContext);
|
||||
nonTexturedMaterials = materials == null ? null : new Material[materials.length];// fill it when needed
|
||||
}
|
||||
|
||||
// creating the result meshes
|
||||
@ -291,23 +292,20 @@ public class MeshHelper extends AbstractBlenderHelper {
|
||||
VertexBuffer normalsBind = new VertexBuffer(Type.BindPoseNormal);
|
||||
normalsBind.setupData(Usage.CpuOnly, 3, Format.Float, BufferUtils.clone(normalsBuffer.getData()));
|
||||
|
||||
VertexBuffer uvCoordsBuffer = null;
|
||||
if (uvCoordinates != null) {
|
||||
uvCoordsBuffer = new VertexBuffer(Type.TexCoord);
|
||||
uvCoordsBuffer.setupData(Usage.Static, 2, Format.Float,
|
||||
BufferUtils.createFloatBuffer(uvCoordinates.toArray(new Vector2f[uvCoordinates.size()])));
|
||||
}
|
||||
|
||||
//reading custom properties
|
||||
Properties properties = this.loadProperties(structure, blenderContext);
|
||||
|
||||
// generating meshes
|
||||
//FloatBuffer verticesColorsBuffer = this.createFloatBuffer(verticesColors);
|
||||
ByteBuffer verticesColorsBuffer = createByteBuffer(verticesColors);
|
||||
ByteBuffer verticesColorsBuffer = this.createByteBuffer(verticesColors);
|
||||
verticesAmount = vertexList.size();
|
||||
Map<Mesh, Integer> meshToMAterialMap = new HashMap<Mesh, Integer>(meshesMap.size());
|
||||
for (Entry<Integer, List<Integer>> meshEntry : meshesMap.entrySet()) {
|
||||
//key is the material index (or -1 if the material has no texture)
|
||||
//value is a list of vertex indices
|
||||
Mesh mesh = new Mesh();
|
||||
|
||||
meshToMAterialMap.put(mesh, meshEntry.getKey());
|
||||
|
||||
// creating vertices indices for this mesh
|
||||
List<Integer> indexList = meshEntry.getValue();
|
||||
if(verticesAmount <= Short.MAX_VALUE) {
|
||||
@ -339,76 +337,32 @@ public class MeshHelper extends AbstractBlenderHelper {
|
||||
|
||||
// creating the result
|
||||
Geometry geometry = new Geometry(name + (geometries.size() + 1), mesh);
|
||||
if (materials != null) {
|
||||
int materialNumber = meshEntry.getKey().intValue();
|
||||
Material material;
|
||||
if (materialNumber >= 0) {
|
||||
material = materials[materialNumber];
|
||||
if (materialNumberToTexture.containsKey(Integer.valueOf(materialNumber))) {
|
||||
if (material.getMaterialDef().getAssetName().contains("Lighting")) {
|
||||
if (!materialHelper.hasTexture(material, MaterialHelper.TEXTURE_TYPE_DIFFUSE)) {
|
||||
material = material.clone();
|
||||
material.setTexture(MaterialHelper.TEXTURE_TYPE_DIFFUSE,
|
||||
materialNumberToTexture.get(Integer.valueOf(materialNumber)));
|
||||
}
|
||||
} else {
|
||||
if (!materialHelper.hasTexture(material, MaterialHelper.TEXTURE_TYPE_COLOR)) {
|
||||
material = material.clone();
|
||||
material.setTexture(MaterialHelper.TEXTURE_TYPE_COLOR,
|
||||
materialNumberToTexture.get(Integer.valueOf(materialNumber)));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
materialNumber = -1 * (materialNumber + 1);
|
||||
if (nonTexturedMaterials[materialNumber] == null) {
|
||||
nonTexturedMaterials[materialNumber] = materialHelper.getNonTexturedMaterial(materials[materialNumber],
|
||||
TextureHelper.TEX_IMAGE);
|
||||
}
|
||||
material = nonTexturedMaterials[materialNumber];
|
||||
}
|
||||
geometry.setMaterial(material);
|
||||
if (material.isTransparent()) {
|
||||
geometry.setQueueBucket(Bucket.Transparent);
|
||||
}
|
||||
} else {
|
||||
geometry.setMaterial(blenderContext.getDefaultMaterial());
|
||||
}
|
||||
if (properties != null && properties.getValue() != null) {
|
||||
geometry.setUserData("properties", properties);
|
||||
}
|
||||
geometries.add(geometry);
|
||||
}
|
||||
|
||||
//applying uvCoordinates for all the meshes
|
||||
if (uvCoordsBuffer != null) {
|
||||
for (Geometry geom : geometries) {
|
||||
geom.getMesh().setBuffer(uvCoordsBuffer);
|
||||
}
|
||||
|
||||
//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 {
|
||||
Map<Material, List<Geometry>> materialMap = new HashMap<Material, List<Geometry>>();
|
||||
for (Geometry geom : geometries) {
|
||||
Material material = geom.getMaterial();
|
||||
List<Geometry> geomsWithCommonMaterial = materialMap.get(material);
|
||||
if (geomsWithCommonMaterial == null) {
|
||||
geomsWithCommonMaterial = new ArrayList<Geometry>();
|
||||
materialMap.put(material, geomsWithCommonMaterial);
|
||||
}
|
||||
geomsWithCommonMaterial.add(geom);
|
||||
|
||||
}
|
||||
for (Entry<Material, List<Geometry>> entry : materialMap.entrySet()) {
|
||||
MaterialContext materialContext = blenderContext.getMaterialContext(entry.getKey());
|
||||
if (materialContext != null && materialContext.getTexturesCount() > 0) {
|
||||
VertexBuffer coords = UVCoordinatesGenerator.generateUVCoordinates(materialContext.getUvCoordinatesType(),
|
||||
materialContext.getProjectionType(), materialContext.getTextureDimension(),
|
||||
materialContext.getProjection(0), entry.getValue());
|
||||
//setting the coordinates inside the mesh context
|
||||
for (Geometry geometry : entry.getValue()) {
|
||||
meshContext.addUVCoordinates(geometry, coords);
|
||||
}
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,315 @@
|
||||
package com.jme3.scene.plugins.blender.textures;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import com.jme3.math.FastMath;
|
||||
import com.jme3.scene.plugins.blender.BlenderContext;
|
||||
import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
|
||||
import com.jme3.scene.plugins.blender.file.DynamicArray;
|
||||
import com.jme3.scene.plugins.blender.file.Pointer;
|
||||
import com.jme3.scene.plugins.blender.file.Structure;
|
||||
|
||||
/**
|
||||
* A class constaining the colorband data.
|
||||
*
|
||||
* @author Marcin Roguski (Kaelthas)
|
||||
*/
|
||||
public class ColorBand {
|
||||
private static final Logger LOGGER = Logger.getLogger(ColorBand.class.getName());
|
||||
|
||||
// interpolation types
|
||||
public static final int IPO_LINEAR = 0;
|
||||
public static final int IPO_EASE = 1;
|
||||
public static final int IPO_BSPLINE = 2;
|
||||
public static final int IPO_CARDINAL = 3;
|
||||
public static final int IPO_CONSTANT = 4;
|
||||
|
||||
private int cursorsAmount, ipoType;
|
||||
private ColorBandData[] data;
|
||||
|
||||
/**
|
||||
* Constructor. Loads the data from the given structure.
|
||||
*
|
||||
* @param cbdataStructure
|
||||
* the colorband structure
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public ColorBand(Structure tex, BlenderContext blenderContext) {
|
||||
int flag = ((Number) tex.getFieldValue("flag")).intValue();
|
||||
if ((flag & GeneratedTexture.TEX_COLORBAND) != 0) {
|
||||
Pointer pColorband = (Pointer) tex.getFieldValue("coba");
|
||||
try {
|
||||
Structure colorbandStructure = pColorband.fetchData(blenderContext.getInputStream()).get(0);
|
||||
this.cursorsAmount = ((Number) colorbandStructure.getFieldValue("tot")).intValue();
|
||||
this.ipoType = ((Number) colorbandStructure.getFieldValue("ipotype")).intValue();
|
||||
this.data = new ColorBandData[this.cursorsAmount];
|
||||
DynamicArray<Structure> data = (DynamicArray<Structure>) colorbandStructure.getFieldValue("data");
|
||||
for (int i = 0; i < this.cursorsAmount; ++i) {
|
||||
this.data[i] = new ColorBandData(data.get(i));
|
||||
}
|
||||
} catch (BlenderFileException e) {
|
||||
LOGGER.log(Level.WARNING, "Cannot fetch the colorband structure. The reason: {0}", e.getLocalizedMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method determines if the colorband has any transparencies or is not
|
||||
* transparent at all.
|
||||
*
|
||||
* @return <b>true</b> if the colorband has transparencies and <b>false</b>
|
||||
* otherwise
|
||||
*/
|
||||
public boolean hasTransparencies() {
|
||||
if (data != null) {
|
||||
for (ColorBandData colorBandData : data) {
|
||||
if (colorBandData.a < 1.0f) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method computes the values of the colorband.
|
||||
*
|
||||
* @return an array of 1001 elements and each element is float[4] object
|
||||
* containing rgba values
|
||||
*/
|
||||
public float[][] computeValues() {
|
||||
float[][] result = null;
|
||||
if (data != null) {
|
||||
result = new float[1001][4];// 1001 - amount of possible cursor
|
||||
// positions; 4 = [r, g, b, a]
|
||||
|
||||
if (data.length == 1) {// special case; use only one color for all
|
||||
// types of colorband interpolation
|
||||
for (int i = 0; i < result.length; ++i) {
|
||||
result[i][0] = data[0].r;
|
||||
result[i][1] = data[0].g;
|
||||
result[i][2] = data[0].b;
|
||||
result[i][3] = data[0].a;
|
||||
}
|
||||
} else {
|
||||
int currentCursor = 0;
|
||||
ColorBandData currentData = data[0];
|
||||
ColorBandData nextData = data[0];
|
||||
switch (ipoType) {
|
||||
case ColorBand.IPO_LINEAR:
|
||||
float rDiff = 0,
|
||||
gDiff = 0,
|
||||
bDiff = 0,
|
||||
aDiff = 0,
|
||||
posDiff;
|
||||
for (int i = 0; i < result.length; ++i) {
|
||||
posDiff = i - currentData.pos;
|
||||
result[i][0] = currentData.r + rDiff * posDiff;
|
||||
result[i][1] = currentData.g + gDiff * posDiff;
|
||||
result[i][2] = currentData.b + bDiff * posDiff;
|
||||
result[i][3] = currentData.a + aDiff * posDiff;
|
||||
if (nextData.pos == i) {
|
||||
currentData = data[currentCursor++];
|
||||
if (currentCursor < data.length) {
|
||||
nextData = data[currentCursor];
|
||||
// calculate differences
|
||||
int d = nextData.pos - currentData.pos;
|
||||
rDiff = (nextData.r - currentData.r) / d;
|
||||
gDiff = (nextData.g - currentData.g) / d;
|
||||
bDiff = (nextData.b - currentData.b) / d;
|
||||
aDiff = (nextData.a - currentData.a) / d;
|
||||
} else {
|
||||
rDiff = gDiff = bDiff = aDiff = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ColorBand.IPO_BSPLINE:
|
||||
case ColorBand.IPO_CARDINAL:
|
||||
Map<Integer, ColorBandData> cbDataMap = new TreeMap<Integer, ColorBandData>();
|
||||
for (int i = 0; i < data.length; ++i) {
|
||||
cbDataMap.put(Integer.valueOf(i), data[i]);
|
||||
}
|
||||
|
||||
if (data[0].pos == 0) {
|
||||
cbDataMap.put(Integer.valueOf(-1), data[0]);
|
||||
} else {
|
||||
ColorBandData cbData = data[0].clone();
|
||||
cbData.pos = 0;
|
||||
cbDataMap.put(Integer.valueOf(-1), cbData);
|
||||
cbDataMap.put(Integer.valueOf(-2), cbData);
|
||||
}
|
||||
|
||||
if (data[data.length - 1].pos == 1000) {
|
||||
cbDataMap.put(Integer.valueOf(data.length), data[data.length - 1]);
|
||||
} else {
|
||||
ColorBandData cbData = data[data.length - 1].clone();
|
||||
cbData.pos = 1000;
|
||||
cbDataMap.put(Integer.valueOf(data.length), cbData);
|
||||
cbDataMap.put(Integer.valueOf(data.length + 1), cbData);
|
||||
}
|
||||
|
||||
float[] ipoFactors = new float[4];
|
||||
float f;
|
||||
|
||||
ColorBandData data0 = cbDataMap.get(currentCursor - 2);
|
||||
ColorBandData data1 = cbDataMap.get(currentCursor - 1);
|
||||
ColorBandData data2 = cbDataMap.get(currentCursor);
|
||||
ColorBandData data3 = cbDataMap.get(currentCursor + 1);
|
||||
|
||||
for (int i = 0; i < result.length; ++i) {
|
||||
if (data2.pos != data1.pos) {
|
||||
f = (i - data2.pos) / (float) (data1.pos - data2.pos);
|
||||
f = FastMath.clamp(f, 0.0f, 1.0f);
|
||||
} else {
|
||||
f = 0.0f;
|
||||
}
|
||||
this.getIpoData(f, ipoFactors);
|
||||
result[i][0] = ipoFactors[3] * data0.r + ipoFactors[2] * data1.r + ipoFactors[1] * data2.r + ipoFactors[0] * data3.r;
|
||||
result[i][1] = ipoFactors[3] * data0.g + ipoFactors[2] * data1.g + ipoFactors[1] * data2.g + ipoFactors[0] * data3.g;
|
||||
result[i][2] = ipoFactors[3] * data0.b + ipoFactors[2] * data1.b + ipoFactors[1] * data2.b + ipoFactors[0] * data3.b;
|
||||
result[i][3] = ipoFactors[3] * data0.a + ipoFactors[2] * data1.a + ipoFactors[1] * data2.a + ipoFactors[0] * data3.a;
|
||||
result[i][0] = FastMath.clamp(result[i][0], 0.0f, 1.0f);
|
||||
result[i][1] = FastMath.clamp(result[i][1], 0.0f, 1.0f);
|
||||
result[i][2] = FastMath.clamp(result[i][2], 0.0f, 1.0f);
|
||||
result[i][3] = FastMath.clamp(result[i][3], 0.0f, 1.0f);
|
||||
|
||||
if (nextData.pos == i) {
|
||||
++currentCursor;
|
||||
data0 = cbDataMap.get(currentCursor - 2);
|
||||
data1 = cbDataMap.get(currentCursor - 1);
|
||||
data2 = cbDataMap.get(currentCursor);
|
||||
data3 = cbDataMap.get(currentCursor + 1);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ColorBand.IPO_EASE:
|
||||
float d,
|
||||
a,
|
||||
b,
|
||||
d2;
|
||||
for (int i = 0; i < result.length; ++i) {
|
||||
if (nextData.pos != currentData.pos) {
|
||||
d = (i - currentData.pos) / (float) (nextData.pos - currentData.pos);
|
||||
d2 = d * d;
|
||||
a = 3.0f * d2 - 2.0f * d * d2;
|
||||
b = 1.0f - a;
|
||||
} else {
|
||||
d = a = 0.0f;
|
||||
b = 1.0f;
|
||||
}
|
||||
|
||||
result[i][0] = b * currentData.r + a * nextData.r;
|
||||
result[i][1] = b * currentData.g + a * nextData.g;
|
||||
result[i][2] = b * currentData.b + a * nextData.b;
|
||||
result[i][3] = b * currentData.a + a * nextData.a;
|
||||
if (nextData.pos == i) {
|
||||
currentData = data[currentCursor++];
|
||||
if (currentCursor < data.length) {
|
||||
nextData = data[currentCursor];
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ColorBand.IPO_CONSTANT:
|
||||
for (int i = 0; i < result.length; ++i) {
|
||||
result[i][0] = currentData.r;
|
||||
result[i][1] = currentData.g;
|
||||
result[i][2] = currentData.b;
|
||||
result[i][3] = currentData.a;
|
||||
if (nextData.pos == i) {
|
||||
currentData = data[currentCursor++];
|
||||
if (currentCursor < data.length) {
|
||||
nextData = data[currentCursor];
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("Unknown interpolation type: " + ipoType);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns the data for either B-spline of Cardinal
|
||||
* interpolation.
|
||||
*
|
||||
* @param d
|
||||
* distance factor for the current intensity
|
||||
* @param ipoFactors
|
||||
* table to store the results (size of the table must be at least
|
||||
* 4)
|
||||
*/
|
||||
private void getIpoData(float d, float[] ipoFactors) {
|
||||
float d2 = d * d;
|
||||
float d3 = d2 * d;
|
||||
if (ipoType == ColorBand.IPO_BSPLINE) {
|
||||
ipoFactors[0] = -0.71f * d3 + 1.42f * d2 - 0.71f * d;
|
||||
ipoFactors[1] = 1.29f * d3 - 2.29f * d2 + 1.0f;
|
||||
ipoFactors[2] = -1.29f * d3 + 1.58f * d2 + 0.71f * d;
|
||||
ipoFactors[3] = 0.71f * d3 - 0.71f * d2;
|
||||
} else if (ipoType == ColorBand.IPO_CARDINAL) {
|
||||
ipoFactors[0] = -0.16666666f * d3 + 0.5f * d2 - 0.5f * d + 0.16666666f;
|
||||
ipoFactors[1] = 0.5f * d3 - d2 + 0.6666666f;
|
||||
ipoFactors[2] = -0.5f * d3 + 0.5f * d2 + 0.5f * d + 0.16666666f;
|
||||
ipoFactors[3] = 0.16666666f * d3;
|
||||
} else {
|
||||
throw new IllegalStateException("Cannot get interpolation data for other colorband types than B-spline and Cardinal!");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class to store the single colorband cursor data.
|
||||
*
|
||||
* @author Marcin Roguski (Kaelthas)
|
||||
*/
|
||||
private static class ColorBandData implements Cloneable {
|
||||
public final float r, g, b, a;
|
||||
public int pos;
|
||||
|
||||
/**
|
||||
* Copy constructor.
|
||||
*/
|
||||
private ColorBandData(ColorBandData data) {
|
||||
this.r = data.r;
|
||||
this.g = data.g;
|
||||
this.b = data.b;
|
||||
this.a = data.a;
|
||||
this.pos = data.pos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor. Loads the data from the given structure.
|
||||
*
|
||||
* @param cbdataStructure
|
||||
* the structure containing the CBData object
|
||||
*/
|
||||
public ColorBandData(Structure cbdataStructure) {
|
||||
this.r = ((Number) cbdataStructure.getFieldValue("r")).floatValue();
|
||||
this.g = ((Number) cbdataStructure.getFieldValue("g")).floatValue();
|
||||
this.b = ((Number) cbdataStructure.getFieldValue("b")).floatValue();
|
||||
this.a = ((Number) cbdataStructure.getFieldValue("a")).floatValue();
|
||||
this.pos = (int) (((Number) cbdataStructure.getFieldValue("pos")).floatValue() * 1000.0f);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ColorBandData clone() {
|
||||
try {
|
||||
return (ColorBandData) super.clone();
|
||||
} catch (CloneNotSupportedException e) {
|
||||
return new ColorBandData(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "P: " + this.pos + " [" + this.r + ", " + this.g + ", " + this.b + ", " + this.a + "]";
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,367 @@
|
||||
package com.jme3.scene.plugins.blender.textures;
|
||||
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.RenderingHints;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import jme3tools.converters.ImageToAwt;
|
||||
|
||||
import com.jme3.math.Vector2f;
|
||||
import com.jme3.scene.Geometry;
|
||||
import com.jme3.scene.Mesh;
|
||||
import com.jme3.scene.plugins.blender.BlenderContext;
|
||||
import com.jme3.scene.plugins.blender.BlenderContext.LoadedFeatureDataType;
|
||||
import com.jme3.scene.plugins.blender.file.Structure;
|
||||
import com.jme3.scene.plugins.blender.textures.UVCoordinatesGenerator.UVCoordinatesType;
|
||||
import com.jme3.scene.plugins.blender.textures.UVProjectionGenerator.UVProjectionType;
|
||||
import com.jme3.scene.plugins.blender.textures.blending.TextureBlender;
|
||||
import com.jme3.scene.plugins.blender.textures.blending.TextureBlenderFactory;
|
||||
import com.jme3.scene.plugins.blender.textures.io.PixelIOFactory;
|
||||
import com.jme3.scene.plugins.blender.textures.io.PixelInputOutput;
|
||||
import com.jme3.texture.Image;
|
||||
import com.jme3.texture.Texture;
|
||||
import com.jme3.texture.Texture.MagFilter;
|
||||
import com.jme3.texture.Texture.MinFilter;
|
||||
import com.jme3.texture.Texture.WrapMode;
|
||||
import com.jme3.texture.Texture2D;
|
||||
|
||||
/**
|
||||
* This class represents a texture that is defined for the material. It can be
|
||||
* made of several textures (both 2D and 3D) that are merged together and
|
||||
* returned as a single texture.
|
||||
*
|
||||
* @author Marcin Roguski (Kaelthas)
|
||||
*/
|
||||
public class CombinedTexture {
|
||||
/** The data for each of the textures. */
|
||||
private List<TextureData> textureDatas = new ArrayList<TextureData>();
|
||||
|
||||
/** The result texture. */
|
||||
private Texture resultTexture;
|
||||
/** The UV values for the result texture. */
|
||||
private List<Vector2f> resultUVS;
|
||||
|
||||
/**
|
||||
* This method adds a texture data to the resulting texture.
|
||||
*
|
||||
* @param texture
|
||||
* the source texture
|
||||
* @param textureBlender
|
||||
* the texture blender (to mix the texture with its material
|
||||
* color)
|
||||
* @param uvCoordinatesType
|
||||
* the type of UV coordinates
|
||||
* @param projectionType
|
||||
* the type of UV coordinates projection (for flat textures)
|
||||
* @param textureStructure
|
||||
* the texture sructure
|
||||
* @param blenderContext
|
||||
* the blender context
|
||||
*/
|
||||
public void add(Texture texture, TextureBlender textureBlender, int uvCoordinatesType, int projectionType, Structure textureStructure, BlenderContext blenderContext) {
|
||||
if (!(texture instanceof GeneratedTexture) && !(texture instanceof Texture2D)) {
|
||||
throw new IllegalArgumentException("Unsupported texture type: " + (texture == null ? "null" : texture.getClass()));
|
||||
}
|
||||
TextureData textureData = new TextureData();
|
||||
textureData.texture = texture;
|
||||
textureData.textureBlender = textureBlender;
|
||||
textureData.uvCoordinatesType = UVCoordinatesType.valueOf(uvCoordinatesType);
|
||||
textureData.projectionType = UVProjectionType.valueOf(projectionType);
|
||||
textureData.textureStructure = textureStructure;
|
||||
|
||||
if (this.isWithoutAlpha(textureData, blenderContext)) {
|
||||
textureDatas.clear();// clear previous textures, they will be
|
||||
// covered anyway
|
||||
}
|
||||
textureDatas.add(textureData);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method flattens the texture and creates a single result of Texture2D
|
||||
* type.
|
||||
*
|
||||
* @param geometry
|
||||
* the geometry the texture is created for
|
||||
* @param geometriesOMA
|
||||
* the old memory address of the geometries list that the given
|
||||
* geometry belongs to (needed for bounding box creation)
|
||||
* @param userDefinedUVCoordinates
|
||||
* the UV's defined by user (null or zero length table if none
|
||||
* were defined)
|
||||
* @param blenderContext
|
||||
* the blender context
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public void flatten(Geometry geometry, Long geometriesOMA, List<Vector2f> userDefinedUVCoordinates, BlenderContext blenderContext) {
|
||||
TextureHelper textureHelper = blenderContext.getHelper(TextureHelper.class);
|
||||
Mesh mesh = geometry.getMesh();
|
||||
Texture previousTexture = null;
|
||||
UVCoordinatesType masterUVCoordinatesType = null;
|
||||
for (TextureData textureData : textureDatas) {
|
||||
// decompress compressed textures (all will be merged into one
|
||||
// texture anyway)
|
||||
if (textureDatas.size() > 1 && textureData.texture.getImage().getFormat().isCompressed()) {
|
||||
textureData.texture.setImage(textureHelper.decompress(textureData.texture.getImage()));
|
||||
textureData.textureBlender = TextureBlenderFactory.alterTextureType(textureData.texture.getImage().getFormat(), textureData.textureBlender);
|
||||
}
|
||||
|
||||
if (previousTexture == null) {// the first texture will lead the others to its shape
|
||||
if (textureData.texture instanceof GeneratedTexture) {
|
||||
resultTexture = ((GeneratedTexture) textureData.texture).triangulate(mesh, geometriesOMA, textureData.uvCoordinatesType, blenderContext);
|
||||
} else if (textureData.texture instanceof Texture2D) {
|
||||
resultTexture = textureData.texture;
|
||||
|
||||
if (userDefinedUVCoordinates == null || userDefinedUVCoordinates.size() == 0) {
|
||||
List<Geometry> geometries = (List<Geometry>) blenderContext.getLoadedFeature(geometriesOMA, LoadedFeatureDataType.LOADED_FEATURE);
|
||||
resultUVS = UVCoordinatesGenerator.generateUVCoordinatesFor2DTexture(mesh, textureData.uvCoordinatesType, textureData.projectionType, geometries);
|
||||
} else {
|
||||
resultUVS = userDefinedUVCoordinates;
|
||||
}
|
||||
}
|
||||
this.blend(resultTexture, textureData.textureBlender, blenderContext);
|
||||
|
||||
previousTexture = resultTexture;
|
||||
masterUVCoordinatesType = textureData.uvCoordinatesType;
|
||||
} else {
|
||||
if (textureData.texture instanceof GeneratedTexture) {
|
||||
if (!(resultTexture instanceof TriangulatedTexture)) {
|
||||
resultTexture = new TriangulatedTexture((Texture2D) resultTexture, resultUVS, blenderContext);
|
||||
resultUVS = null;
|
||||
previousTexture = resultTexture;
|
||||
}
|
||||
|
||||
TriangulatedTexture triangulatedTexture = ((GeneratedTexture) textureData.texture).triangulate(mesh, geometriesOMA, textureData.uvCoordinatesType, blenderContext);
|
||||
triangulatedTexture.castToUVS((TriangulatedTexture) resultTexture, blenderContext);
|
||||
triangulatedTexture.blend(textureData.textureBlender, (TriangulatedTexture) resultTexture, blenderContext);
|
||||
resultTexture = previousTexture = triangulatedTexture;
|
||||
} else if (textureData.texture instanceof Texture2D) {
|
||||
if (masterUVCoordinatesType == textureData.uvCoordinatesType && resultTexture instanceof Texture2D) {
|
||||
this.scale((Texture2D) textureData.texture, resultTexture.getImage().getWidth(), resultTexture.getImage().getHeight());
|
||||
this.merge((Texture2D) resultTexture, (Texture2D) textureData.texture);
|
||||
previousTexture = resultTexture;
|
||||
} else {
|
||||
if (!(resultTexture instanceof TriangulatedTexture)) {
|
||||
resultTexture = new TriangulatedTexture((Texture2D) resultTexture, resultUVS, blenderContext);
|
||||
resultUVS = null;
|
||||
}
|
||||
// first triangulate the current texture
|
||||
List<Vector2f> textureUVS = null;
|
||||
if (textureData.uvCoordinatesType != UVCoordinatesType.TEXCO_UV) {
|
||||
List<Geometry> geometries = (List<Geometry>) blenderContext.getLoadedFeature(geometriesOMA, LoadedFeatureDataType.LOADED_FEATURE);
|
||||
textureUVS = UVCoordinatesGenerator.generateUVCoordinatesFor2DTexture(mesh, textureData.uvCoordinatesType, textureData.projectionType, geometries);
|
||||
} else {
|
||||
textureUVS = userDefinedUVCoordinates;
|
||||
}
|
||||
TriangulatedTexture triangulatedTexture = new TriangulatedTexture((Texture2D) textureData.texture, textureUVS, blenderContext);
|
||||
// then move the texture to different UV's
|
||||
triangulatedTexture.castToUVS((TriangulatedTexture) resultTexture, blenderContext);
|
||||
((TriangulatedTexture) resultTexture).merge(triangulatedTexture);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (resultTexture instanceof TriangulatedTexture) {
|
||||
resultUVS = ((TriangulatedTexture) resultTexture).getResultUVS();
|
||||
resultTexture = ((TriangulatedTexture) resultTexture).getResultTexture();
|
||||
}
|
||||
|
||||
// setting additional data
|
||||
resultTexture.setWrap(WrapMode.Repeat);
|
||||
// the filters are required if generated textures are used because
|
||||
// otherwise ugly lines appear between the mesh faces
|
||||
resultTexture.setMagFilter(MagFilter.Nearest);
|
||||
resultTexture.setMinFilter(MinFilter.NearestNoMipMaps);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method blends the texture.
|
||||
*
|
||||
* @param texture
|
||||
* the texture to be blended
|
||||
* @param textureBlender
|
||||
* blending definition for the texture
|
||||
* @param blenderContext
|
||||
* the blender context
|
||||
*/
|
||||
private void blend(Texture texture, TextureBlender textureBlender, BlenderContext blenderContext) {
|
||||
if (texture instanceof TriangulatedTexture) {
|
||||
((TriangulatedTexture) texture).blend(textureBlender, null, blenderContext);
|
||||
} else if (texture instanceof Texture2D) {
|
||||
Image blendedImage = textureBlender.blend(texture.getImage(), null, blenderContext);
|
||||
texture.setImage(blendedImage);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Invalid type for texture to blend!");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the result texture
|
||||
*/
|
||||
public Texture getResultTexture() {
|
||||
return resultTexture;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the result UV coordinates
|
||||
*/
|
||||
public List<Vector2f> getResultUVS() {
|
||||
return resultUVS;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method merges two given textures. The result is stored in the
|
||||
* 'target' texture.
|
||||
*
|
||||
* @param target
|
||||
* the target texture
|
||||
* @param source
|
||||
* the source texture
|
||||
*/
|
||||
private void merge(Texture2D target, Texture2D source) {
|
||||
Image sourceImage = source.getImage();
|
||||
Image targetImage = target.getImage();
|
||||
PixelInputOutput sourceIO = PixelIOFactory.getPixelIO(sourceImage.getFormat());
|
||||
PixelInputOutput targetIO = PixelIOFactory.getPixelIO(targetImage.getFormat());
|
||||
TexturePixel sourcePixel = new TexturePixel();
|
||||
TexturePixel targetPixel = new TexturePixel();
|
||||
|
||||
for (int x = 0; x < sourceImage.getWidth(); ++x) {
|
||||
for (int y = 0; y < sourceImage.getHeight(); ++y) {
|
||||
sourceIO.read(sourceImage, sourcePixel, x, y);
|
||||
targetIO.read(targetImage, targetPixel, x, y);
|
||||
targetPixel.merge(sourcePixel);
|
||||
targetIO.write(targetImage, targetPixel, x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method determines if the given texture has no alpha channel.
|
||||
*
|
||||
* @param texture
|
||||
* the texture to check for alpha channel
|
||||
* @return <b>true</b> if the texture has no alpha channel and <b>false</b>
|
||||
* otherwise
|
||||
*/
|
||||
private boolean isWithoutAlpha(TextureData textureData, BlenderContext blenderContext) {
|
||||
ColorBand colorBand = new ColorBand(textureData.textureStructure, blenderContext);
|
||||
if (!colorBand.hasTransparencies()) {
|
||||
int type = ((Number) textureData.textureStructure.getFieldValue("type")).intValue();
|
||||
if (type == TextureHelper.TEX_MAGIC) {
|
||||
return true;
|
||||
}
|
||||
if (type == TextureHelper.TEX_VORONOI) {
|
||||
int voronoiColorType = ((Number) textureData.textureStructure.getFieldValue("vn_coltype")).intValue();
|
||||
return voronoiColorType != 0;// voronoiColorType == 0:
|
||||
// intensity, voronoiColorType
|
||||
// != 0: col1, col2 or col3
|
||||
}
|
||||
if (type == TextureHelper.TEX_CLOUDS) {
|
||||
int sType = ((Number) textureData.textureStructure.getFieldValue("stype")).intValue();
|
||||
return sType == 1;// sType==0: without colors, sType==1: with
|
||||
// colors
|
||||
}
|
||||
|
||||
// checking the flat textures for alpha values presence
|
||||
if (type == TextureHelper.TEX_IMAGE) {
|
||||
Image image = textureData.texture.getImage();
|
||||
switch (image.getFormat()) {
|
||||
case BGR8:
|
||||
case DXT1:
|
||||
case Luminance16:
|
||||
case Luminance16F:
|
||||
case Luminance32F:
|
||||
case Luminance8:
|
||||
case RGB10:
|
||||
case RGB111110F:
|
||||
case RGB16:
|
||||
case RGB16F:
|
||||
case RGB32F:
|
||||
case RGB565:
|
||||
case RGB8:
|
||||
return true;// these types have no alpha by definition
|
||||
case ABGR8:
|
||||
case DXT3:
|
||||
case DXT5:
|
||||
case Luminance16Alpha16:
|
||||
case Luminance16FAlpha16F:
|
||||
case Luminance8Alpha8:
|
||||
case RGBA16:
|
||||
case RGBA16F:
|
||||
case RGBA32F:
|
||||
case RGBA8:// with these types it is better to make sure if
|
||||
// the texture is or is not transparent
|
||||
PixelInputOutput pixelInputOutput = PixelIOFactory.getPixelIO(image.getFormat());
|
||||
TexturePixel pixel = new TexturePixel();
|
||||
for (int x = 0; x < image.getWidth(); ++x) {
|
||||
for (int y = 0; y < image.getHeight(); ++y) {
|
||||
pixelInputOutput.read(image, pixel, x, y);
|
||||
if (pixel.alpha < 1.0f) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method scales the given texture to the given size.
|
||||
*
|
||||
* @param texture
|
||||
* the texture to be scaled
|
||||
* @param width
|
||||
* new width of the texture
|
||||
* @param height
|
||||
* new height of the texture
|
||||
*/
|
||||
private void scale(Texture2D texture, int width, int height) {
|
||||
// first determine if scaling is required
|
||||
boolean scaleRequired = texture.getImage().getWidth() != width || texture.getImage().getHeight() != height;
|
||||
|
||||
if (scaleRequired) {
|
||||
Image image = texture.getImage();
|
||||
BufferedImage sourceImage = ImageToAwt.convert(image, false, true, 0);
|
||||
|
||||
int sourceWidth = sourceImage.getWidth();
|
||||
int sourceHeight = sourceImage.getHeight();
|
||||
|
||||
BufferedImage targetImage = new BufferedImage(width, height, sourceImage.getType());
|
||||
|
||||
Graphics2D g = targetImage.createGraphics();
|
||||
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
|
||||
g.drawImage(sourceImage, 0, 0, width, height, 0, 0, sourceWidth, sourceHeight, null);
|
||||
g.dispose();
|
||||
|
||||
Image output = new ImageLoader().load(targetImage, false);
|
||||
image.setWidth(width);
|
||||
image.setHeight(height);
|
||||
image.setData(output.getData(0));
|
||||
image.setFormat(output.getFormat());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple class to aggregate the texture data (improves code quality).
|
||||
*
|
||||
* @author Marcin Roguski (Kaelthas)
|
||||
*/
|
||||
private static class TextureData {
|
||||
/** The texture. */
|
||||
public Texture texture;
|
||||
/** The texture blender (to mix the texture with its material color). */
|
||||
public TextureBlender textureBlender;
|
||||
/** The type of UV coordinates. */
|
||||
public UVCoordinatesType uvCoordinatesType;
|
||||
/** The type of UV coordinates projection (for flat textures). */
|
||||
public UVProjectionType projectionType;
|
||||
/** The texture sructure. */
|
||||
public Structure textureStructure;
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -0,0 +1,151 @@
|
||||
package com.jme3.scene.plugins.blender.textures;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import com.jme3.bounding.BoundingBox;
|
||||
import com.jme3.math.Vector3f;
|
||||
import com.jme3.scene.Geometry;
|
||||
import com.jme3.scene.Mesh;
|
||||
import com.jme3.scene.plugins.blender.BlenderContext;
|
||||
import com.jme3.scene.plugins.blender.BlenderContext.LoadedFeatureDataType;
|
||||
import com.jme3.scene.plugins.blender.file.Structure;
|
||||
import com.jme3.scene.plugins.blender.textures.TriangulatedTexture.TriangleTextureElement;
|
||||
import com.jme3.scene.plugins.blender.textures.UVCoordinatesGenerator.UVCoordinatesType;
|
||||
import com.jme3.scene.plugins.blender.textures.generating.TextureGenerator;
|
||||
import com.jme3.texture.Image;
|
||||
import com.jme3.texture.Texture;
|
||||
|
||||
/**
|
||||
* The generated texture loaded from blender file. The texture is not generated
|
||||
* after being read. This class rather stores all required data and can compute
|
||||
* a pixel in the required 3D space position.
|
||||
*
|
||||
* @author Marcin Roguski (Kaelthas)
|
||||
*/
|
||||
/* package */class GeneratedTexture extends Texture {
|
||||
// flag values
|
||||
public static final int TEX_COLORBAND = 1;
|
||||
public static final int TEX_FLIPBLEND = 2;
|
||||
public static final int TEX_NEGALPHA = 4;
|
||||
public static final int TEX_CHECKER_ODD = 8;
|
||||
public static final int TEX_CHECKER_EVEN = 16;
|
||||
public static final int TEX_PRV_ALPHA = 32;
|
||||
public static final int TEX_PRV_NOR = 64;
|
||||
public static final int TEX_REPEAT_XMIR = 128;
|
||||
public static final int TEX_REPEAT_YMIR = 256;
|
||||
public static final int TEX_FLAG_MASK = TEX_COLORBAND | TEX_FLIPBLEND | TEX_NEGALPHA | TEX_CHECKER_ODD | TEX_CHECKER_EVEN | TEX_PRV_ALPHA | TEX_PRV_NOR | TEX_REPEAT_XMIR | TEX_REPEAT_YMIR;
|
||||
|
||||
/** Material-texture link structure. */
|
||||
private final Structure mTex;
|
||||
/** Texture generateo for the specified texture type. */
|
||||
private final TextureGenerator textureGenerator;
|
||||
|
||||
/**
|
||||
* Constructor. Reads the required data from the 'tex' structure.
|
||||
*
|
||||
* @param tex
|
||||
* the texture structure
|
||||
* @param mTex
|
||||
* the material-texture link data structure
|
||||
* @param textureGenerator
|
||||
* the generator for the required texture type
|
||||
* @param blenderContext
|
||||
* the blender context
|
||||
*/
|
||||
public GeneratedTexture(Structure tex, Structure mTex, TextureGenerator textureGenerator, BlenderContext blenderContext) {
|
||||
this.mTex = mTex;
|
||||
this.textureGenerator = textureGenerator;
|
||||
this.textureGenerator.readData(tex, blenderContext);
|
||||
super.setImage(new GeneratedTextureImage(textureGenerator.getImageFormat()));
|
||||
}
|
||||
|
||||
/**
|
||||
* This method computes the textyre color/intensity at the specified (u, v,
|
||||
* s) position in 3D space.
|
||||
*
|
||||
* @param pixel
|
||||
* the pixel where the result is stored
|
||||
* @param u
|
||||
* the U factor
|
||||
* @param v
|
||||
* the V factor
|
||||
* @param s
|
||||
* the S factor
|
||||
*/
|
||||
public void getPixel(TexturePixel pixel, float u, float v, float s) {
|
||||
textureGenerator.getPixel(pixel, u, v, s);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method triangulates the texture. In the result we get a set of small
|
||||
* flat textures for each face of the given mesh. This can be later merged
|
||||
* into one flat texture.
|
||||
*
|
||||
* @param mesh
|
||||
* the mesh we create the texture for
|
||||
* @param geometriesOMA
|
||||
* the old memory address of the geometries group that the given
|
||||
* mesh belongs to (required for bounding box calculations)
|
||||
* @param coordinatesType
|
||||
* the types of UV coordinates
|
||||
* @param blenderContext
|
||||
* the blender context
|
||||
* @return triangulated texture
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public TriangulatedTexture triangulate(Mesh mesh, Long geometriesOMA, UVCoordinatesType coordinatesType, BlenderContext blenderContext) {
|
||||
List<Geometry> geometries = (List<Geometry>) blenderContext.getLoadedFeature(geometriesOMA, LoadedFeatureDataType.LOADED_FEATURE);
|
||||
|
||||
int[] coordinatesSwappingIndexes = new int[] { ((Number) mTex.getFieldValue("projx")).intValue(), ((Number) mTex.getFieldValue("projy")).intValue(), ((Number) mTex.getFieldValue("projz")).intValue() };
|
||||
List<Vector3f> uvs = UVCoordinatesGenerator.generateUVCoordinatesFor3DTexture(mesh, coordinatesType, coordinatesSwappingIndexes, geometries);
|
||||
Vector3f[] uvsArray = uvs.toArray(new Vector3f[uvs.size()]);
|
||||
BoundingBox boundingBox = UVCoordinatesGenerator.getBoundingBox(geometries);
|
||||
Set<TriangleTextureElement> triangleTextureElements = new TreeSet<TriangleTextureElement>(new Comparator<TriangleTextureElement>() {
|
||||
@Override
|
||||
public int compare(TriangleTextureElement o1, TriangleTextureElement o2) {
|
||||
return o1.faceIndex - o2.faceIndex;
|
||||
}
|
||||
});
|
||||
for (int i = 0; i < mesh.getTriangleCount(); ++i) {
|
||||
triangleTextureElements.add(new TriangleTextureElement(i, boundingBox, this, uvsArray, blenderContext));
|
||||
}
|
||||
return new TriangulatedTexture(triangleTextureElements, blenderContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWrap(WrapAxis axis, WrapMode mode) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWrap(WrapMode mode) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public WrapMode getWrap(WrapAxis axis) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType() {
|
||||
return Type.ThreeDimensional;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Texture createSimpleClone() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Private class to give the format of the 'virtual' 3D texture image.
|
||||
*
|
||||
* @author Marcin Roguski (Kaelthas)
|
||||
*/
|
||||
private static class GeneratedTextureImage extends Image {
|
||||
public GeneratedTextureImage(Format imageFormat) {
|
||||
super.format = imageFormat;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,416 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2010 jMonkeyEngine
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package com.jme3.scene.plugins.blender.textures;
|
||||
|
||||
import com.jme3.math.FastMath;
|
||||
import com.jme3.scene.plugins.blender.BlenderContext;
|
||||
import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
|
||||
import com.jme3.scene.plugins.blender.file.DynamicArray;
|
||||
import com.jme3.scene.plugins.blender.file.Pointer;
|
||||
import com.jme3.scene.plugins.blender.file.Structure;
|
||||
import com.jme3.texture.Texture;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* This class is a base class for texture generators.
|
||||
* @author Marcin Roguski (Kaelthas)
|
||||
*/
|
||||
/* package */abstract class TextureGenerator {
|
||||
private static final Logger LOGGER = Logger.getLogger(TextureGenerator.class.getName());
|
||||
|
||||
protected NoiseGenerator noiseGenerator;
|
||||
|
||||
public TextureGenerator(NoiseGenerator noiseGenerator) {
|
||||
this.noiseGenerator = noiseGenerator;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method generates the texture.
|
||||
* @param tex
|
||||
* texture's structure
|
||||
* @param width
|
||||
* the width of the result texture
|
||||
* @param height
|
||||
* the height of the result texture
|
||||
* @param depth
|
||||
* the depth of the texture
|
||||
* @param blenderContext
|
||||
* the blender context
|
||||
* @return newly generated texture
|
||||
*/
|
||||
protected abstract Texture generate(Structure tex, int width, int height, int depth, BlenderContext blenderContext);
|
||||
|
||||
/**
|
||||
* This method reads the colorband data from the given texture structure.
|
||||
*
|
||||
* @param tex
|
||||
* the texture structure
|
||||
* @param blenderContext
|
||||
* the blender context
|
||||
* @return read colorband or null if not present
|
||||
*/
|
||||
private ColorBand readColorband(Structure tex, BlenderContext blenderContext) {
|
||||
ColorBand result = null;
|
||||
int flag = ((Number) tex.getFieldValue("flag")).intValue();
|
||||
if ((flag & NoiseGenerator.TEX_COLORBAND) != 0) {
|
||||
Pointer pColorband = (Pointer) tex.getFieldValue("coba");
|
||||
Structure colorbandStructure;
|
||||
try {
|
||||
colorbandStructure = pColorband.fetchData(blenderContext.getInputStream()).get(0);
|
||||
result = new ColorBand(colorbandStructure);
|
||||
} catch (BlenderFileException e) {
|
||||
LOGGER.log(Level.WARNING, "Cannot fetch the colorband structure. The reason: {0}", e.getLocalizedMessage());
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
protected float[][] computeColorband(Structure tex, BlenderContext blenderContext) {
|
||||
ColorBand colorBand = this.readColorband(tex, blenderContext);
|
||||
float[][] result = null;
|
||||
if(colorBand!=null) {
|
||||
result = new float[1001][4];//1001 - amount of possible cursor positions; 4 = [r, g, b, a]
|
||||
ColorBandData[] dataArray = colorBand.data;
|
||||
|
||||
if(dataArray.length==1) {//special case; use only one color for all types of colorband interpolation
|
||||
for(int i=0;i<result.length;++i) {
|
||||
result[i][0] = dataArray[0].r;
|
||||
result[i][1] = dataArray[0].g;
|
||||
result[i][2] = dataArray[0].b;
|
||||
result[i][3] = dataArray[0].a;
|
||||
}
|
||||
} else {
|
||||
int currentCursor = 0;
|
||||
ColorBandData currentData = dataArray[0];
|
||||
ColorBandData nextData = dataArray[0];
|
||||
switch(colorBand.ipoType) {
|
||||
case ColorBand.IPO_LINEAR:
|
||||
float rDiff = 0, gDiff = 0, bDiff = 0, aDiff = 0, posDiff;
|
||||
for(int i=0;i<result.length;++i) {
|
||||
posDiff = i - currentData.pos;
|
||||
result[i][0] = currentData.r + rDiff * posDiff;
|
||||
result[i][1] = currentData.g + gDiff * posDiff;
|
||||
result[i][2] = currentData.b + bDiff * posDiff;
|
||||
result[i][3] = currentData.a + aDiff * posDiff;
|
||||
if(nextData.pos==i) {
|
||||
currentData = dataArray[currentCursor++];
|
||||
if(currentCursor < dataArray.length) {
|
||||
nextData = dataArray[currentCursor];
|
||||
//calculate differences
|
||||
int d = nextData.pos - currentData.pos;
|
||||
rDiff = (nextData.r - currentData.r)/d;
|
||||
gDiff = (nextData.g - currentData.g)/d;
|
||||
bDiff = (nextData.b - currentData.b)/d;
|
||||
aDiff = (nextData.a - currentData.a)/d;
|
||||
} else {
|
||||
rDiff = gDiff = bDiff = aDiff = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ColorBand.IPO_BSPLINE:
|
||||
case ColorBand.IPO_CARDINAL:
|
||||
Map<Integer, ColorBandData> cbDataMap = new TreeMap<Integer, ColorBandData>();
|
||||
for(int i=0;i<colorBand.data.length;++i) {
|
||||
cbDataMap.put(Integer.valueOf(i), colorBand.data[i]);
|
||||
}
|
||||
|
||||
if(colorBand.data[0].pos==0) {
|
||||
cbDataMap.put(Integer.valueOf(-1), colorBand.data[0]);
|
||||
} else {
|
||||
ColorBandData cbData = colorBand.data[0].clone();
|
||||
cbData.pos = 0;
|
||||
cbDataMap.put(Integer.valueOf(-1), cbData);
|
||||
cbDataMap.put(Integer.valueOf(-2), cbData);
|
||||
}
|
||||
|
||||
if(colorBand.data[colorBand.data.length - 1].pos==1000) {
|
||||
cbDataMap.put(Integer.valueOf(colorBand.data.length), colorBand.data[colorBand.data.length - 1]);
|
||||
} else {
|
||||
ColorBandData cbData = colorBand.data[colorBand.data.length - 1].clone();
|
||||
cbData.pos = 1000;
|
||||
cbDataMap.put(Integer.valueOf(colorBand.data.length), cbData);
|
||||
cbDataMap.put(Integer.valueOf(colorBand.data.length + 1), cbData);
|
||||
}
|
||||
|
||||
float[] ipoFactors = new float[4];
|
||||
float f;
|
||||
|
||||
ColorBandData data0 = cbDataMap.get(currentCursor - 2);
|
||||
ColorBandData data1 = cbDataMap.get(currentCursor - 1);
|
||||
ColorBandData data2 = cbDataMap.get(currentCursor);
|
||||
ColorBandData data3 = cbDataMap.get(currentCursor + 1);
|
||||
|
||||
for(int i=0;i<result.length;++i) {
|
||||
if (data2.pos != data1.pos) {
|
||||
f = (i - data2.pos) / (float)(data1.pos - data2.pos);
|
||||
} else {
|
||||
f = 0.0f;
|
||||
}
|
||||
|
||||
f = FastMath.clamp(f, 0.0f, 1.0f);
|
||||
|
||||
this.getIpoData(colorBand, f, ipoFactors);
|
||||
result[i][0] = ipoFactors[3] * data0.r + ipoFactors[2] * data1.r + ipoFactors[1] * data2.r + ipoFactors[0] * data3.r;
|
||||
result[i][1] = ipoFactors[3] * data0.g + ipoFactors[2] * data1.g + ipoFactors[1] * data2.g + ipoFactors[0] * data3.g;
|
||||
result[i][2] = ipoFactors[3] * data0.b + ipoFactors[2] * data1.b + ipoFactors[1] * data2.b + ipoFactors[0] * data3.b;
|
||||
result[i][3] = ipoFactors[3] * data0.a + ipoFactors[2] * data1.a + ipoFactors[1] * data2.a + ipoFactors[0] * data3.a;
|
||||
result[i][0] = FastMath.clamp(result[i][0], 0.0f, 1.0f);
|
||||
result[i][1] = FastMath.clamp(result[i][1], 0.0f, 1.0f);
|
||||
result[i][2] = FastMath.clamp(result[i][2], 0.0f, 1.0f);
|
||||
result[i][3] = FastMath.clamp(result[i][3], 0.0f, 1.0f);
|
||||
|
||||
if(nextData.pos==i) {
|
||||
++currentCursor;
|
||||
data0 = cbDataMap.get(currentCursor - 2);
|
||||
data1 = cbDataMap.get(currentCursor - 1);
|
||||
data2 = cbDataMap.get(currentCursor);
|
||||
data3 = cbDataMap.get(currentCursor + 1);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ColorBand.IPO_EASE:
|
||||
float d, a, b, d2;
|
||||
for(int i=0;i<result.length;++i) {
|
||||
if(nextData.pos != currentData.pos) {
|
||||
d = (i - currentData.pos) / (float)(nextData.pos - currentData.pos);
|
||||
d2 = d * d;
|
||||
a = 3.0f * d2 - 2.0f * d * d2;
|
||||
b = 1.0f - a;
|
||||
} else {
|
||||
d = a = 0.0f;
|
||||
b = 1.0f;
|
||||
}
|
||||
|
||||
result[i][0] = b * currentData.r + a * nextData.r;
|
||||
result[i][1] = b * currentData.g + a * nextData.g;
|
||||
result[i][2] = b * currentData.b + a * nextData.b;
|
||||
result[i][3] = b * currentData.a + a * nextData.a;
|
||||
if(nextData.pos==i) {
|
||||
currentData = dataArray[currentCursor++];
|
||||
if(currentCursor < dataArray.length) {
|
||||
nextData = dataArray[currentCursor];
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ColorBand.IPO_CONSTANT:
|
||||
for(int i=0;i<result.length;++i) {
|
||||
result[i][0] = currentData.r;
|
||||
result[i][1] = currentData.g;
|
||||
result[i][2] = currentData.b;
|
||||
result[i][3] = currentData.a;
|
||||
if(nextData.pos==i) {
|
||||
currentData = dataArray[currentCursor++];
|
||||
if(currentCursor < dataArray.length) {
|
||||
nextData = dataArray[currentCursor];
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("Unknown interpolation type: " + colorBand.ipoType);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns the data for either B-spline of Cardinal interpolation.
|
||||
* @param colorBand the color band
|
||||
* @param d distance factor for the current intensity
|
||||
* @param ipoFactors table to store the results (size of the table must be at least 4)
|
||||
*/
|
||||
private void getIpoData(ColorBand colorBand, float d, float[] ipoFactors) {
|
||||
float d2 = d * d;
|
||||
float d3 = d2 * d;
|
||||
if(colorBand.ipoType==ColorBand.IPO_BSPLINE) {
|
||||
ipoFactors[0] = -0.71f * d3 + 1.42f * d2 - 0.71f * d;
|
||||
ipoFactors[1] = 1.29f * d3 - 2.29f * d2 + 1.0f;
|
||||
ipoFactors[2] = -1.29f * d3 + 1.58f * d2 + 0.71f * d;
|
||||
ipoFactors[3] = 0.71f * d3 - 0.71f * d2;
|
||||
} else if(colorBand.ipoType==ColorBand.IPO_CARDINAL) {
|
||||
ipoFactors[0] = -0.16666666f * d3 + 0.5f * d2 - 0.5f * d + 0.16666666f;
|
||||
ipoFactors[1] = 0.5f * d3 - d2 + 0.6666666f;
|
||||
ipoFactors[2] = -0.5f * d3 + 0.5f * d2 + 0.5f * d + 0.16666666f;
|
||||
ipoFactors[3] = 0.16666666f * d3;
|
||||
} else {
|
||||
throw new IllegalStateException("Cannot get interpolation data for other colorband types than B-spline and Cardinal!");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method applies brightness and contrast for RGB textures.
|
||||
* @param tex texture structure
|
||||
* @param texres
|
||||
*/
|
||||
protected void applyBrightnessAndContrast(BrightnessAndContrastData bacd, TexturePixel texres) {
|
||||
texres.red = (texres.red - 0.5f) * bacd.contrast + bacd.brightness;
|
||||
if (texres.red < 0.0f) {
|
||||
texres.red = 0.0f;
|
||||
}
|
||||
texres.green =(texres.green - 0.5f) * bacd.contrast + bacd.brightness;
|
||||
if (texres.green < 0.0f) {
|
||||
texres.green = 0.0f;
|
||||
}
|
||||
texres.blue = (texres.blue - 0.5f) * bacd.contrast + bacd.brightness;
|
||||
if (texres.blue < 0.0f) {
|
||||
texres.blue = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method applies brightness and contrast for Luminance textures.
|
||||
* @param texres
|
||||
* @param contrast
|
||||
* @param brightness
|
||||
*/
|
||||
protected void applyBrightnessAndContrast(TexturePixel texres, float contrast, float brightness) {
|
||||
texres.intensity = (texres.intensity - 0.5f) * contrast + brightness;
|
||||
if (texres.intensity < 0.0f) {
|
||||
texres.intensity = 0.0f;
|
||||
} else if (texres.intensity > 1.0f) {
|
||||
texres.intensity = 1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A class constaining the colorband data.
|
||||
*
|
||||
* @author Marcin Roguski (Kaelthas)
|
||||
*/
|
||||
protected static class ColorBand {
|
||||
//interpolation types
|
||||
public static final int IPO_LINEAR = 0;
|
||||
public static final int IPO_EASE = 1;
|
||||
public static final int IPO_BSPLINE = 2;
|
||||
public static final int IPO_CARDINAL = 3;
|
||||
public static final int IPO_CONSTANT = 4;
|
||||
|
||||
public int cursorsAmount, ipoType;
|
||||
public ColorBandData[] data;
|
||||
|
||||
/**
|
||||
* Constructor. Loads the data from the given structure.
|
||||
*
|
||||
* @param cbdataStructure
|
||||
* the colorband structure
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public ColorBand(Structure colorbandStructure) {
|
||||
this.cursorsAmount = ((Number) colorbandStructure.getFieldValue("tot")).intValue();
|
||||
this.ipoType = ((Number) colorbandStructure.getFieldValue("ipotype")).intValue();
|
||||
this.data = new ColorBandData[this.cursorsAmount];
|
||||
DynamicArray<Structure> data = (DynamicArray<Structure>) colorbandStructure.getFieldValue("data");
|
||||
for (int i = 0; i < this.cursorsAmount; ++i) {
|
||||
this.data[i] = new ColorBandData(data.get(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class to store the single colorband cursor data.
|
||||
*
|
||||
* @author Marcin Roguski (Kaelthas)
|
||||
*/
|
||||
protected static class ColorBandData implements Cloneable {
|
||||
public final float r, g, b, a;
|
||||
public int pos;
|
||||
|
||||
/**
|
||||
* Copy constructor.
|
||||
*/
|
||||
private ColorBandData(ColorBandData data) {
|
||||
this.r = data.r;
|
||||
this.g = data.g;
|
||||
this.b = data.b;
|
||||
this.a = data.a;
|
||||
this.pos = data.pos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor. Loads the data from the given structure.
|
||||
*
|
||||
* @param cbdataStructure
|
||||
* the structure containing the CBData object
|
||||
*/
|
||||
public ColorBandData(Structure cbdataStructure) {
|
||||
this.r = ((Number) cbdataStructure.getFieldValue("r")).floatValue();
|
||||
this.g = ((Number) cbdataStructure.getFieldValue("g")).floatValue();
|
||||
this.b = ((Number) cbdataStructure.getFieldValue("b")).floatValue();
|
||||
this.a = ((Number) cbdataStructure.getFieldValue("a")).floatValue();
|
||||
this.pos = (int) (((Number) cbdataStructure.getFieldValue("pos")).floatValue() * 1000.0f);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ColorBandData clone() {
|
||||
try {
|
||||
return (ColorBandData) super.clone();
|
||||
} catch (CloneNotSupportedException e) {
|
||||
return new ColorBandData(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "P: " + this.pos + " [" + this.r+", "+this.g+", "+this.b+", "+this.a+"]";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This class contains brightness and contrast data.
|
||||
* @author Marcin Roguski (Kaelthas)
|
||||
*/
|
||||
protected static class BrightnessAndContrastData {
|
||||
public final float contrast;
|
||||
public final float brightness;
|
||||
public final float rFactor;
|
||||
public final float gFactor;
|
||||
public final float bFactor;
|
||||
|
||||
/**
|
||||
* Constructor reads the required data from the given structure.
|
||||
* @param tex texture structure
|
||||
*/
|
||||
public BrightnessAndContrastData(Structure tex) {
|
||||
contrast = ((Number) tex.getFieldValue("contrast")).floatValue();
|
||||
brightness = ((Number) tex.getFieldValue("bright")).floatValue() - 0.5f;
|
||||
rFactor = ((Number) tex.getFieldValue("rfac")).floatValue();
|
||||
gFactor = ((Number) tex.getFieldValue("gfac")).floatValue();
|
||||
bFactor = ((Number) tex.getFieldValue("bfac")).floatValue();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,131 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2010 jMonkeyEngine
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package com.jme3.scene.plugins.blender.textures;
|
||||
|
||||
import com.jme3.math.FastMath;
|
||||
import com.jme3.scene.plugins.blender.BlenderContext;
|
||||
import com.jme3.scene.plugins.blender.file.Structure;
|
||||
import com.jme3.texture.Image;
|
||||
import com.jme3.texture.Image.Format;
|
||||
import com.jme3.texture.Texture;
|
||||
import com.jme3.texture.Texture3D;
|
||||
import com.jme3.util.BufferUtils;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* This class generates the 'clouds' texture.
|
||||
* @author Marcin Roguski (Kaelthas)
|
||||
*/
|
||||
public class TextureGeneratorClouds extends TextureGenerator {
|
||||
// tex->noisetype
|
||||
protected static final int TEX_NOISESOFT = 0;
|
||||
protected static final int TEX_NOISEPERL = 1;
|
||||
|
||||
// tex->stype
|
||||
protected static final int TEX_DEFAULT = 0;
|
||||
protected static final int TEX_COLOR = 1;
|
||||
|
||||
/**
|
||||
* Constructor stores the given noise generator.
|
||||
* @param noiseGenerator
|
||||
* the noise generator
|
||||
*/
|
||||
public TextureGeneratorClouds(NoiseGenerator noiseGenerator) {
|
||||
super(noiseGenerator);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Texture generate(Structure tex, int width, int height, int depth, BlenderContext blenderContext) {
|
||||
float[] texvec = new float[] { 0, 0, 0 };
|
||||
TexturePixel texres = new TexturePixel();
|
||||
|
||||
// reading the data from the texture structure
|
||||
float noisesize = ((Number) tex.getFieldValue("noisesize")).floatValue();
|
||||
int noiseDepth = ((Number) tex.getFieldValue("noisedepth")).intValue();
|
||||
int noiseBasis = ((Number) tex.getFieldValue("noisebasis")).intValue();
|
||||
int noiseType = ((Number) tex.getFieldValue("noisetype")).intValue();
|
||||
boolean isHard = noiseType != TEX_NOISESOFT;
|
||||
int sType = ((Number) tex.getFieldValue("stype")).intValue();
|
||||
int halfW = width >> 1, halfH = height >> 1, halfD = depth >> 1, index = 0;
|
||||
float wDelta = 1.0f / halfW, hDelta = 1.0f / halfH, dDelta = 1.0f / halfD;
|
||||
float[][] colorBand = this.computeColorband(tex, blenderContext);
|
||||
Format format = sType == TEX_COLOR || colorBand != null ? Format.RGBA8 : Format.Luminance8;
|
||||
int bytesPerPixel = sType == TEX_COLOR || colorBand != null ? 4 : 1;
|
||||
BrightnessAndContrastData bacd = new BrightnessAndContrastData(tex);
|
||||
|
||||
byte[] data = new byte[width * height * depth * bytesPerPixel];
|
||||
for (int i = -halfW; i < halfW; ++i) {
|
||||
texvec[0] = wDelta * i;
|
||||
for (int j = -halfH; j < halfH; ++j) {
|
||||
texvec[1] = hDelta * j;
|
||||
for (int k = -halfD; k < halfD; ++k) {
|
||||
texvec[2] = dDelta * k;
|
||||
texres.intensity = NoiseGenerator.NoiseFunctions.turbulence(texvec[0], texvec[1], texvec[2], noisesize, noiseDepth, noiseBasis, isHard);
|
||||
texres.intensity = FastMath.clamp(texres.intensity, 0.0f, 1.0f);
|
||||
if (colorBand != null) {
|
||||
int colorbandIndex = (int) (texres.intensity * 1000.0f);
|
||||
texres.red = colorBand[colorbandIndex][0];
|
||||
texres.green = colorBand[colorbandIndex][1];
|
||||
texres.blue = colorBand[colorbandIndex][2];
|
||||
|
||||
this.applyBrightnessAndContrast(bacd, texres);
|
||||
data[index++] = (byte) (texres.red * 255.0f);
|
||||
data[index++] = (byte) (texres.green * 255.0f);
|
||||
data[index++] = (byte) (texres.blue * 255.0f);
|
||||
data[index++] = (byte) (colorBand[colorbandIndex][3] * 255.0f);
|
||||
} else if (sType == TEX_COLOR) {
|
||||
texres.red = texres.intensity;
|
||||
texres.green = NoiseGenerator.NoiseFunctions.turbulence(texvec[1], texvec[0], texvec[2], noisesize, noiseDepth, noiseBasis, isHard);
|
||||
texres.blue = NoiseGenerator.NoiseFunctions.turbulence(texvec[1], texvec[2], texvec[0], noisesize, noiseDepth, noiseBasis, isHard);
|
||||
|
||||
texres.green = FastMath.clamp(texres.green, 0.0f, 1.0f);
|
||||
texres.blue = FastMath.clamp(texres.blue, 0.0f, 1.0f);
|
||||
|
||||
this.applyBrightnessAndContrast(bacd, texres);
|
||||
data[index++] = (byte) (texres.red * 255.0f);
|
||||
data[index++] = (byte) (texres.green * 255.0f);
|
||||
data[index++] = (byte) (texres.blue * 255.0f);
|
||||
data[index++] = (byte) (255);//1.0f * 255.0f
|
||||
} else {
|
||||
this.applyBrightnessAndContrast(texres, bacd.contrast, bacd.brightness);
|
||||
data[index++] = (byte) (texres.intensity * 255.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ArrayList<ByteBuffer> dataArray = new ArrayList<ByteBuffer>(1);
|
||||
dataArray.add(BufferUtils.createByteBuffer(data));
|
||||
return new Texture3D(new Image(format, width, height, depth, dataArray));
|
||||
}
|
||||
}
|
@ -1,106 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2010 jMonkeyEngine
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package com.jme3.scene.plugins.blender.textures;
|
||||
|
||||
import com.jme3.math.FastMath;
|
||||
import com.jme3.scene.plugins.blender.BlenderContext;
|
||||
import com.jme3.scene.plugins.blender.file.Structure;
|
||||
import com.jme3.texture.Image;
|
||||
import com.jme3.texture.Image.Format;
|
||||
import com.jme3.texture.Texture;
|
||||
import com.jme3.texture.Texture3D;
|
||||
import com.jme3.util.BufferUtils;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* This class generates the 'noise' texture.
|
||||
* @author Marcin Roguski (Kaelthas)
|
||||
*/
|
||||
public class TextureGeneratorNoise extends TextureGenerator {
|
||||
|
||||
/**
|
||||
* Constructor stores the given noise generator.
|
||||
* @param noiseGenerator
|
||||
* the noise generator
|
||||
*/
|
||||
public TextureGeneratorNoise(NoiseGenerator noiseGenerator) {
|
||||
super(noiseGenerator);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Texture generate(Structure tex, int width, int height, int depth, BlenderContext blenderContext) {
|
||||
int val, random, loop;
|
||||
int noisedepth = ((Number) tex.getFieldValue("noisedepth")).intValue();
|
||||
TexturePixel texres = new TexturePixel();
|
||||
int halfW = width >> 1, halfH = height >> 1, halfD = depth >> 1, index = 0;
|
||||
float[][] colorBand = this.computeColorband(tex, blenderContext);
|
||||
Format format = colorBand != null ? Format.RGBA8 : Format.Luminance8;
|
||||
int bytesPerPixel = colorBand != null ? 4 : 1;
|
||||
BrightnessAndContrastData bacd = new BrightnessAndContrastData(tex);
|
||||
|
||||
byte[] data = new byte[width * height * depth * bytesPerPixel];
|
||||
for (int i = -halfW; i < halfW; ++i) {
|
||||
for (int j = -halfH; j < halfH; ++j) {
|
||||
for (int k = -halfD; k < halfD; ++k) {
|
||||
random = FastMath.rand.nextInt();
|
||||
val = random & 3;
|
||||
|
||||
loop = noisedepth;
|
||||
while (loop-- != 0) {
|
||||
random >>= 2;
|
||||
val *= random & 3;
|
||||
}
|
||||
texres.intensity = FastMath.clamp(val, 0.0f, 1.0f);
|
||||
if (colorBand != null) {
|
||||
int colorbandIndex = (int) (texres.intensity * 1000.0f);
|
||||
texres.red = colorBand[colorbandIndex][0];
|
||||
texres.green = colorBand[colorbandIndex][1];
|
||||
texres.blue = colorBand[colorbandIndex][2];
|
||||
|
||||
this.applyBrightnessAndContrast(bacd, texres);
|
||||
data[index++] = (byte) (texres.red * 255.0f);
|
||||
data[index++] = (byte) (texres.green * 255.0f);
|
||||
data[index++] = (byte) (texres.blue * 255.0f);
|
||||
data[index++] = (byte) (colorBand[colorbandIndex][3] * 255.0f);
|
||||
} else {
|
||||
this.applyBrightnessAndContrast(texres, bacd.contrast, bacd.brightness);
|
||||
data[index++] = (byte) (texres.intensity * 255.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ArrayList<ByteBuffer> dataArray = new ArrayList<ByteBuffer>(1);
|
||||
dataArray.add(BufferUtils.createByteBuffer(data));
|
||||
return new Texture3D(new Image(format, width, height, depth, dataArray));
|
||||
}
|
||||
}
|
@ -1,125 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2010 jMonkeyEngine
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package com.jme3.scene.plugins.blender.textures;
|
||||
|
||||
import com.jme3.scene.plugins.blender.BlenderContext;
|
||||
import com.jme3.scene.plugins.blender.file.Structure;
|
||||
import com.jme3.texture.Image;
|
||||
import com.jme3.texture.Image.Format;
|
||||
import com.jme3.texture.Texture;
|
||||
import com.jme3.texture.Texture3D;
|
||||
import com.jme3.util.BufferUtils;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* This class generates the 'stucci' texture.
|
||||
* @author Marcin Roguski (Kaelthas)
|
||||
*/
|
||||
public class TextureGeneratorStucci extends TextureGenerator {
|
||||
protected static final int TEX_NOISESOFT = 0;
|
||||
|
||||
/**
|
||||
* Constructor stores the given noise generator.
|
||||
* @param noiseGenerator
|
||||
* the noise generator
|
||||
*/
|
||||
public TextureGeneratorStucci(NoiseGenerator noiseGenerator) {
|
||||
super(noiseGenerator);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Texture generate(Structure tex, int width, int height, int depth, BlenderContext blenderContext) {
|
||||
float noisesize = ((Number) tex.getFieldValue("noisesize")).floatValue();
|
||||
int noisebasis = ((Number) tex.getFieldValue("noisebasis")).intValue();
|
||||
int noisetype = ((Number) tex.getFieldValue("noisetype")).intValue();
|
||||
float turbul = ((Number) tex.getFieldValue("turbul")).floatValue();
|
||||
boolean isHard = noisetype != TEX_NOISESOFT;
|
||||
int stype = ((Number) tex.getFieldValue("stype")).intValue();
|
||||
|
||||
if(noisesize<=0.001f) {//the texture goes black if this value is lower than 0.001f
|
||||
noisesize = 0.001f;
|
||||
}
|
||||
|
||||
float[] texvec = new float[] { 0, 0, 0 };
|
||||
TexturePixel texres = new TexturePixel();
|
||||
int halfW = width >> 1, halfH = height >> 1, halfD = depth >> 1, index = 0;
|
||||
float wDelta = 1.0f / halfW, hDelta = 1.0f / halfH, dDelta = 1.0f / halfD, noiseValue, ofs;;
|
||||
float[][] colorBand = this.computeColorband(tex, blenderContext);
|
||||
Format format = colorBand != null ? Format.RGBA8 : Format.Luminance8;
|
||||
int bytesPerPixel = colorBand != null ? 4 : 1;
|
||||
|
||||
byte[] data = new byte[width * height * depth * bytesPerPixel];
|
||||
for (int i = -halfW; i < halfW; ++i) {
|
||||
texvec[0] = wDelta * i;
|
||||
for (int j = -halfH; j < halfH; ++j) {
|
||||
texvec[1] = hDelta * j;
|
||||
for (int k = -halfD; k < halfD; ++k) {
|
||||
texvec[2] = dDelta * k;
|
||||
noiseValue = NoiseGenerator.NoiseFunctions.noise(texvec[0], texvec[1], texvec[2], noisesize, 0, noisebasis, isHard);
|
||||
ofs = turbul / 200.0f;
|
||||
if (stype != 0) {
|
||||
ofs *= noiseValue * noiseValue;
|
||||
}
|
||||
|
||||
texres.intensity = NoiseGenerator.NoiseFunctions.noise(texvec[0], texvec[1], texvec[2] + ofs, noisesize, 0, noisebasis, isHard);
|
||||
if (colorBand != null) {
|
||||
int colorbandIndex = (int) (texres.intensity * 1000.0f);
|
||||
texres.red = colorBand[colorbandIndex][0];
|
||||
texres.green = colorBand[colorbandIndex][1];
|
||||
texres.blue = colorBand[colorbandIndex][2];
|
||||
texres.alpha = colorBand[colorbandIndex][3];
|
||||
}
|
||||
|
||||
if (stype == NoiseGenerator.TEX_WALLOUT) {
|
||||
texres.intensity = 1.0f - texres.intensity;
|
||||
}
|
||||
if (texres.intensity < 0.0f) {
|
||||
texres.intensity = 0.0f;
|
||||
}
|
||||
//no brightness and contrast needed for stucci (it doesn't affect the texture)
|
||||
if (colorBand != null) {
|
||||
data[index++] = (byte) (texres.red * 255.0f);
|
||||
data[index++] = (byte) (texres.green * 255.0f);
|
||||
data[index++] = (byte) (texres.blue * 255.0f);
|
||||
data[index++] = (byte) (texres.alpha * 255.0f);
|
||||
} else {
|
||||
data[index++] = (byte) (texres.intensity * 255.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ArrayList<ByteBuffer> dataArray = new ArrayList<ByteBuffer>(1);
|
||||
dataArray.add(BufferUtils.createByteBuffer(data));
|
||||
return new Texture3D(new Image(format, width, height, depth, dataArray));
|
||||
}
|
||||
}
|
@ -1,171 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2010 jMonkeyEngine
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package com.jme3.scene.plugins.blender.textures;
|
||||
|
||||
import com.jme3.math.FastMath;
|
||||
import com.jme3.scene.plugins.blender.BlenderContext;
|
||||
import com.jme3.scene.plugins.blender.file.Structure;
|
||||
import com.jme3.scene.plugins.blender.textures.NoiseGenerator.NoiseMath;
|
||||
import com.jme3.texture.Image;
|
||||
import com.jme3.texture.Image.Format;
|
||||
import com.jme3.texture.Texture;
|
||||
import com.jme3.texture.Texture3D;
|
||||
import com.jme3.util.BufferUtils;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* This class generates the 'voronoi' texture.
|
||||
* @author Marcin Roguski (Kaelthas)
|
||||
*/
|
||||
public class TextureGeneratorVoronoi extends TextureGenerator {
|
||||
|
||||
/**
|
||||
* Constructor stores the given noise generator.
|
||||
* @param noiseGenerator
|
||||
* the noise generator
|
||||
*/
|
||||
public TextureGeneratorVoronoi(NoiseGenerator noiseGenerator) {
|
||||
super(noiseGenerator);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Texture generate(Structure tex, int width, int height, int depth, BlenderContext blenderContext) {
|
||||
float voronoiWeight1 = ((Number) tex.getFieldValue("vn_w1")).floatValue();
|
||||
float voronoiWeight2 = ((Number) tex.getFieldValue("vn_w2")).floatValue();
|
||||
float voronoiWeight3 = ((Number) tex.getFieldValue("vn_w3")).floatValue();
|
||||
float voronoiWeight4 = ((Number) tex.getFieldValue("vn_w4")).floatValue();
|
||||
float noisesize = ((Number) tex.getFieldValue("noisesize")).floatValue();
|
||||
float outscale = ((Number) tex.getFieldValue("ns_outscale")).floatValue();
|
||||
float mexp = ((Number) tex.getFieldValue("vn_mexp")).floatValue();
|
||||
int distm = ((Number) tex.getFieldValue("vn_distm")).intValue();
|
||||
int voronoiColorType = ((Number) tex.getFieldValue("vn_coltype")).intValue();
|
||||
|
||||
TexturePixel texres = new TexturePixel();
|
||||
float[] texvec = new float[] { 0, 0, 0 };
|
||||
int halfW = width >> 1, halfH = height >> 1, halfD = depth >> 1, index = 0;
|
||||
float wDelta = 1.0f / halfW, hDelta = 1.0f / halfH, dDelta = 1.0f / halfD;
|
||||
|
||||
float[][] colorBand = this.computeColorband(tex, blenderContext);
|
||||
Format format = voronoiColorType != 0 || colorBand != null ? Format.RGBA8 : Format.Luminance8;
|
||||
int bytesPerPixel = voronoiColorType != 0 || colorBand != null ? 4 : 1;
|
||||
BrightnessAndContrastData bacd = new BrightnessAndContrastData(tex);
|
||||
|
||||
float[] da = new float[4], pa = new float[12];
|
||||
float[] hashPoint = voronoiColorType != 0 ? new float[3] : null;
|
||||
float[] voronoiWeights = new float[] {FastMath.abs(voronoiWeight1), FastMath.abs(voronoiWeight2),
|
||||
FastMath.abs(voronoiWeight3), FastMath.abs(voronoiWeight4)};
|
||||
float weight;
|
||||
float sc = voronoiWeights[0] + voronoiWeights[1] + voronoiWeights[2] + voronoiWeights[3];
|
||||
if (sc != 0.0f) {
|
||||
sc = outscale / sc;
|
||||
}
|
||||
|
||||
byte[] data = new byte[width * height * depth * bytesPerPixel];
|
||||
for (int i = -halfW; i < halfW; ++i) {
|
||||
texvec[0] = wDelta * i / noisesize;
|
||||
for (int j = -halfH; j < halfH; ++j) {
|
||||
texvec[1] = hDelta * j / noisesize;
|
||||
for (int k = -halfD; k < halfD; ++k) {
|
||||
texvec[2] = dDelta * k;
|
||||
NoiseGenerator.NoiseFunctions.voronoi(texvec[0], texvec[1], texvec[2], da, pa, mexp, distm);
|
||||
texres.intensity = sc * FastMath.abs(voronoiWeight1 * da[0] + voronoiWeight2 * da[1] + voronoiWeight3 * da[2] + voronoiWeight4 * da[3]);
|
||||
if(texres.intensity>1.0f) {
|
||||
texres.intensity = 1.0f;
|
||||
} else if(texres.intensity<0.0f) {
|
||||
texres.intensity = 0.0f;
|
||||
}
|
||||
|
||||
if (colorBand != null) {//colorband ALWAYS goes first and covers the color (if set)
|
||||
int colorbandIndex = (int) (texres.intensity * 1000.0f);
|
||||
texres.red = colorBand[colorbandIndex][0];
|
||||
texres.green = colorBand[colorbandIndex][1];
|
||||
texres.blue = colorBand[colorbandIndex][2];
|
||||
texres.alpha = colorBand[colorbandIndex][3];
|
||||
} else if (voronoiColorType != 0) {
|
||||
texres.red = texres.green = texres.blue = 0.0f;
|
||||
texres.alpha = 1.0f;
|
||||
for(int m=0; m<12; m+=3) {
|
||||
weight = voronoiWeights[m/3];
|
||||
this.cellNoiseV(pa[m], pa[m + 1], pa[m + 2], hashPoint);
|
||||
texres.red += weight * hashPoint[0];
|
||||
texres.green += weight * hashPoint[1];
|
||||
texres.blue += weight * hashPoint[2];
|
||||
}
|
||||
if (voronoiColorType >= 2) {
|
||||
float t1 = (da[1] - da[0]) * 10.0f;
|
||||
if (t1 > 1.0f) {
|
||||
t1 = 1.0f;
|
||||
}
|
||||
if (voronoiColorType == 3) {
|
||||
t1 *= texres.intensity;
|
||||
} else {
|
||||
t1 *= sc;
|
||||
}
|
||||
texres.red *= t1;
|
||||
texres.green *= t1;
|
||||
texres.blue *= t1;
|
||||
} else {
|
||||
texres.red *= sc;
|
||||
texres.green *= sc;
|
||||
texres.blue *= sc;
|
||||
}
|
||||
}
|
||||
|
||||
if (voronoiColorType != 0 || colorBand != null) {
|
||||
this.applyBrightnessAndContrast(bacd, texres);
|
||||
data[index++] = (byte) (texres.red * 255.0f);
|
||||
data[index++] = (byte) (texres.green * 255.0f);
|
||||
data[index++] = (byte) (texres.blue * 255.0f);
|
||||
data[index++] = (byte) (texres.alpha * 255.0f);
|
||||
} else {
|
||||
this.applyBrightnessAndContrast(texres, bacd.contrast, bacd.brightness);
|
||||
data[index++] = (byte) (texres.intensity * 255.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ArrayList<ByteBuffer> dataArray = new ArrayList<ByteBuffer>(1);
|
||||
dataArray.add(BufferUtils.createByteBuffer(data));
|
||||
return new Texture3D(new Image(format, width, height, depth, dataArray));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a vector/point/color in ca, using point hasharray directly
|
||||
*/
|
||||
private void cellNoiseV(float x, float y, float z, float[] hashPoint) {
|
||||
int xi = (int) Math.floor(x);
|
||||
int yi = (int) Math.floor(y);
|
||||
int zi = (int) Math.floor(z);
|
||||
NoiseMath.hash(xi, yi, zi, hashPoint);
|
||||
}
|
||||
}
|
@ -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+
|
||||
public static final int TEX_NONE = 0;
|
||||
public static final int TEX_CLOUDS = 1;
|
||||
public static final int TEX_WOOD = 2;
|
||||
public static final int TEX_MARBLE = 3;
|
||||
public static final int TEX_MAGIC = 4;
|
||||
public static final int TEX_BLEND = 5;
|
||||
public static final int TEX_STUCCI = 6;
|
||||
public static final int TEX_NOISE = 7;
|
||||
public static final int TEX_IMAGE = 8;
|
||||
public static final int TEX_PLUGIN = 9;
|
||||
public static final int TEX_ENVMAP = 10;
|
||||
public static final int TEX_MUSGRAVE = 11;
|
||||
public static final int TEX_VORONOI = 12;
|
||||
public static final int TEX_DISTNOISE = 13;
|
||||
public static final int TEX_POINTDENSITY = 14; // v.
|
||||
// 25+
|
||||
public static final int TEX_VOXELDATA = 15; // v.
|
||||
// 25+
|
||||
|
||||
// mapto
|
||||
public static final int MAP_COL = 1;
|
||||
public static final int MAP_NORM = 2;
|
||||
public static final int MAP_COLSPEC = 4;
|
||||
public static final int MAP_COLMIR = 8;
|
||||
public static final int MAP_VARS = 0xFFF0;
|
||||
public static final int MAP_REF = 16;
|
||||
public static final int MAP_SPEC = 32;
|
||||
public static final int MAP_EMIT = 64;
|
||||
public static final int MAP_ALPHA = 128;
|
||||
public static final int MAP_HAR = 256;
|
||||
public static final int MAP_RAYMIRR = 512;
|
||||
public static final int MAP_TRANSLU = 1024;
|
||||
public static final int MAP_AMB = 2048;
|
||||
public static final int MAP_DISPLACE = 4096;
|
||||
public static final int MAP_WARP = 8192;
|
||||
public static final int MAP_LAYER = 16384;
|
||||
|
||||
protected NoiseGenerator noiseGenerator;
|
||||
private Map<Integer, TextureGenerator> textureGenerators = new HashMap<Integer, TextureGenerator>();
|
||||
private TextureGeneratorFactory textureGeneratorFactory;
|
||||
|
||||
/**
|
||||
* This constructor parses the given blender version and stores the result.
|
||||
* It creates noise generator and texture generators.
|
||||
*
|
||||
* @param blenderVersion
|
||||
* the version read from the blend file
|
||||
* the version read from the blend file
|
||||
* @param fixUpAxis
|
||||
* a variable that indicates if the Y asxis is the UP axis or not
|
||||
* a variable that indicates if the Y asxis is the UP axis or not
|
||||
*/
|
||||
public TextureHelper(String blenderVersion, boolean fixUpAxis) {
|
||||
super(blenderVersion, false);
|
||||
noiseGenerator = new NoiseGenerator(blenderVersion);
|
||||
textureGenerators.put(Integer.valueOf(TEX_BLEND), new TextureGeneratorBlend(noiseGenerator));
|
||||
textureGenerators.put(Integer.valueOf(TEX_CLOUDS), new TextureGeneratorClouds(noiseGenerator));
|
||||
textureGenerators.put(Integer.valueOf(TEX_DISTNOISE), new TextureGeneratorDistnoise(noiseGenerator));
|
||||
textureGenerators.put(Integer.valueOf(TEX_MAGIC), new TextureGeneratorMagic(noiseGenerator));
|
||||
textureGenerators.put(Integer.valueOf(TEX_MARBLE), new TextureGeneratorMarble(noiseGenerator));
|
||||
textureGenerators.put(Integer.valueOf(TEX_MUSGRAVE), new TextureGeneratorMusgrave(noiseGenerator));
|
||||
textureGenerators.put(Integer.valueOf(TEX_NOISE), new TextureGeneratorNoise(noiseGenerator));
|
||||
textureGenerators.put(Integer.valueOf(TEX_STUCCI), new TextureGeneratorStucci(noiseGenerator));
|
||||
textureGenerators.put(Integer.valueOf(TEX_VORONOI), new TextureGeneratorVoronoi(noiseGenerator));
|
||||
textureGenerators.put(Integer.valueOf(TEX_WOOD), new TextureGeneratorWood(noiseGenerator));
|
||||
textureGeneratorFactory = new TextureGeneratorFactory(blenderVersion);
|
||||
}
|
||||
|
||||
/**
|
||||
* This class returns a texture read from the file or from packed blender data. The returned texture has the name set to the value of
|
||||
* its blender type.
|
||||
* This class returns a texture read from the file or from packed blender
|
||||
* data. The returned texture has the name set to the value of its blender
|
||||
* type.
|
||||
*
|
||||
* @param tex
|
||||
* texture structure filled with data
|
||||
* texture structure filled with data
|
||||
* @param blenderContext
|
||||
* the blender context
|
||||
* the blender context
|
||||
* @return the texture that can be used by JME engine
|
||||
* @throws BlenderFileException
|
||||
* this exception is thrown when the blend file structure is somehow invalid or corrupted
|
||||
* this exception is thrown when the blend file structure is
|
||||
* somehow invalid or corrupted
|
||||
*/
|
||||
public Texture getTexture(Structure tex, BlenderContext blenderContext) throws BlenderFileException {
|
||||
public Texture getTexture(Structure tex, Structure mTex, BlenderContext blenderContext) throws BlenderFileException {
|
||||
Texture result = (Texture) blenderContext.getLoadedFeature(tex.getOldMemoryAddress(), LoadedFeatureDataType.LOADED_FEATURE);
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
int type = ((Number) tex.getFieldValue("type")).intValue();
|
||||
int width = blenderContext.getBlenderKey().getGeneratedTextureWidth();
|
||||
int height = blenderContext.getBlenderKey().getGeneratedTextureHeight();
|
||||
int depth = blenderContext.getBlenderKey().getGeneratedTextureDepth();
|
||||
|
||||
switch (type) {
|
||||
case TEX_IMAGE:// (it is first because probably this will be most commonly used)
|
||||
Pointer pImage = (Pointer) tex.getFieldValue("ima");
|
||||
if (pImage.isNotNull()){
|
||||
Structure image = pImage.fetchData(blenderContext.getInputStream()).get(0);
|
||||
result = this.getTextureFromImage(image, blenderContext);
|
||||
}
|
||||
break;
|
||||
case TEX_CLOUDS:
|
||||
case TEX_WOOD:
|
||||
case TEX_MARBLE:
|
||||
case TEX_MAGIC:
|
||||
case TEX_BLEND:
|
||||
case TEX_STUCCI:
|
||||
case TEX_NOISE:
|
||||
case TEX_MUSGRAVE:
|
||||
case TEX_VORONOI:
|
||||
case TEX_DISTNOISE:
|
||||
TextureGenerator textureGenerator = textureGenerators.get(Integer.valueOf(type));
|
||||
result = textureGenerator.generate(tex, width, height, depth, blenderContext);
|
||||
break;
|
||||
case TEX_NONE:// No texture, do nothing
|
||||
break;
|
||||
case TEX_POINTDENSITY:
|
||||
LOGGER.warning("Point density texture loading currently not supported!");
|
||||
break;
|
||||
case TEX_VOXELDATA:
|
||||
LOGGER.warning("Voxel data texture loading currently not supported!");
|
||||
break;
|
||||
case TEX_PLUGIN:
|
||||
case TEX_ENVMAP:// TODO: implement envmap texture
|
||||
LOGGER.log(Level.WARNING, "Unsupported texture type: {0} for texture: {1}", new Object[]{type, tex.getName()});
|
||||
break;
|
||||
default:
|
||||
throw new BlenderFileException("Unknown texture type: " + type + " for texture: " + tex.getName());
|
||||
case TEX_IMAGE:// (it is first because probably this will be most commonly used)
|
||||
Pointer pImage = (Pointer) tex.getFieldValue("ima");
|
||||
if (pImage.isNotNull()) {
|
||||
Structure image = pImage.fetchData(blenderContext.getInputStream()).get(0);
|
||||
result = this.getTextureFromImage(image, blenderContext);
|
||||
this.applyColorbandAndColorFactors(tex, result.getImage(), blenderContext);
|
||||
}
|
||||
break;
|
||||
case TEX_CLOUDS:
|
||||
case TEX_WOOD:
|
||||
case TEX_MARBLE:
|
||||
case TEX_MAGIC:
|
||||
case TEX_BLEND:
|
||||
case TEX_STUCCI:
|
||||
case TEX_NOISE:
|
||||
case TEX_MUSGRAVE:
|
||||
case TEX_VORONOI:
|
||||
case TEX_DISTNOISE:
|
||||
result = new GeneratedTexture(tex, mTex, textureGeneratorFactory.createTextureGenerator(type), blenderContext);
|
||||
break;
|
||||
case TEX_NONE:// No texture, do nothing
|
||||
break;
|
||||
case TEX_POINTDENSITY:
|
||||
LOGGER.warning("Point density texture loading currently not supported!");
|
||||
break;
|
||||
case TEX_VOXELDATA:
|
||||
LOGGER.warning("Voxel data texture loading currently not supported!");
|
||||
break;
|
||||
case TEX_PLUGIN:
|
||||
case TEX_ENVMAP:
|
||||
LOGGER.log(Level.WARNING, "Unsupported texture type: {0} for texture: {1}", new Object[] { type, tex.getName() });
|
||||
break;
|
||||
default:
|
||||
throw new BlenderFileException("Unknown texture type: " + type + " for texture: " + tex.getName());
|
||||
}
|
||||
if (result != null) {
|
||||
result.setName(tex.getName());
|
||||
result.setWrap(WrapMode.Repeat);
|
||||
// NOTE: Enable mipmaps FOR ALL TEXTURES EVER
|
||||
result.setMinFilter(MinFilter.Trilinear);
|
||||
if(type != TEX_IMAGE) {//only generated textures should have this key
|
||||
if (type != TEX_IMAGE) {// only generated textures should have this key
|
||||
result.setKey(new GeneratedTextureKey(tex.getName()));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method merges the given textures. The result texture has no alpha
|
||||
* factor (is always opaque).
|
||||
*
|
||||
* @param sources
|
||||
* the textures to be merged
|
||||
* @param materialContext
|
||||
* the context of the material
|
||||
* @return merged textures
|
||||
*/
|
||||
public Texture mergeTextures(List<Texture> sources, MaterialContext materialContext) {
|
||||
Texture result = null;
|
||||
if(sources!=null && sources.size()>0) {
|
||||
if(sources.size() == 1) {
|
||||
return sources.get(0);//just return the texture
|
||||
}
|
||||
//checking the sizes of the textures (tehy should perfectly match)
|
||||
int lastTextureWithoutAlphaIndex = 0;
|
||||
int width = sources.get(0).getImage().getWidth();
|
||||
int height = sources.get(0).getImage().getHeight();
|
||||
int depth = sources.get(0).getImage().getDepth();
|
||||
|
||||
for(Texture source : sources) {
|
||||
if(source.getImage().getWidth() != width) {
|
||||
throw new IllegalArgumentException("The texture " + source.getName() + " has invalid width! It should be: " + width + '!');
|
||||
}
|
||||
if(source.getImage().getHeight() != height) {
|
||||
throw new IllegalArgumentException("The texture " + source.getName() + " has invalid height! It should be: " + height + '!');
|
||||
}
|
||||
if(source.getImage().getDepth() != depth) {
|
||||
throw new IllegalArgumentException("The texture " + source.getName() + " has invalid depth! It should be: " + depth + '!');
|
||||
}
|
||||
//support for more formats is not necessary at the moment
|
||||
if(source.getImage().getFormat()!=Format.RGB8 && source.getImage().getFormat()!=Format.BGR8) {
|
||||
++lastTextureWithoutAlphaIndex;
|
||||
}
|
||||
}
|
||||
if(depth==0) {
|
||||
depth = 1;
|
||||
}
|
||||
|
||||
//remove textures before the one without alpha (they will be covered anyway)
|
||||
if(lastTextureWithoutAlphaIndex > 0 && lastTextureWithoutAlphaIndex<sources.size()-1) {
|
||||
sources = sources.subList(lastTextureWithoutAlphaIndex, sources.size()-1);
|
||||
}
|
||||
int pixelsAmount = width * height * depth;
|
||||
|
||||
ByteBuffer data = BufferUtils.createByteBuffer(pixelsAmount * 3);
|
||||
TexturePixel resultPixel = new TexturePixel();
|
||||
TexturePixel sourcePixel = new TexturePixel();
|
||||
ColorRGBA diffuseColor = materialContext.getDiffuseColor();
|
||||
for (int i = 0; i < pixelsAmount; ++i) {
|
||||
for (int j = 0; j < sources.size(); ++j) {
|
||||
Image image = sources.get(j).getImage();
|
||||
ByteBuffer sourceData = image.getData(0);
|
||||
if(j==0) {
|
||||
resultPixel.fromColor(diffuseColor);
|
||||
sourcePixel.fromImage(image.getFormat(), sourceData, i);
|
||||
resultPixel.merge(sourcePixel);
|
||||
} else {
|
||||
sourcePixel.fromImage(image.getFormat(), sourceData, i);
|
||||
resultPixel.merge(sourcePixel);
|
||||
}
|
||||
}
|
||||
data.put((byte)(255 * resultPixel.red));
|
||||
data.put((byte)(255 * resultPixel.green));
|
||||
data.put((byte)(255 * resultPixel.blue));
|
||||
resultPixel.clear();
|
||||
}
|
||||
|
||||
if(depth==1) {
|
||||
result = new Texture2D(new Image(Format.RGB8, width, height, data));
|
||||
} else {
|
||||
ArrayList<ByteBuffer> arrayData = new ArrayList<ByteBuffer>(1);
|
||||
arrayData.add(data);
|
||||
result = new Texture3D(new Image(Format.RGB8, width, height, depth, arrayData));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method converts the given texture into normal-map texture.
|
||||
*
|
||||
* @param source
|
||||
* the source texture
|
||||
* the source texture
|
||||
* @param strengthFactor
|
||||
* the normal strength factor
|
||||
* the normal strength factor
|
||||
* @return normal-map texture
|
||||
*/
|
||||
public Texture convertToNormalMapTexture(Texture source, float strengthFactor) {
|
||||
@ -337,14 +228,172 @@ public class TextureHelper extends AbstractBlenderHelper {
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns the height represented by the specified pixel in the given texture.
|
||||
* The given texture should be a height-map.
|
||||
* This method decompresses the given image. If the given image is already
|
||||
* decompressed nothing happens and it is simply returned.
|
||||
*
|
||||
* @param image
|
||||
* the height-map texture
|
||||
* the image to decompress
|
||||
* @return the decompressed image
|
||||
*/
|
||||
public Image decompress(Image image) {
|
||||
byte[] bytes = null;
|
||||
TexturePixel[] colors = null;
|
||||
ByteBuffer data = image.getData(0);// TODO: support decompression of all data 'layers'
|
||||
data.rewind();
|
||||
Format format = image.getFormat();
|
||||
|
||||
DDSTexelData texelData = new DDSTexelData(data.remaining() / (format.getBitsPerPixel() * 2), image.getWidth(), image.getHeight(), format != Format.DXT1);
|
||||
switch (format) {// TODO: DXT1A
|
||||
case DXT1:// BC1
|
||||
bytes = new byte[image.getWidth() * image.getHeight() * 4];
|
||||
colors = new TexturePixel[] { new TexturePixel(), new TexturePixel(), new TexturePixel(), new TexturePixel() };
|
||||
while (data.hasRemaining()) {
|
||||
short c0 = data.getShort();
|
||||
short c1 = data.getShort();
|
||||
int col0 = RGB565.RGB565_to_ARGB8(c0);
|
||||
int col1 = RGB565.RGB565_to_ARGB8(c1);
|
||||
colors[0].fromARGB8(col0);
|
||||
colors[1].fromARGB8(col1);
|
||||
|
||||
if (col0 > col1) {
|
||||
// creating color2 = 2/3color0 + 1/3color1
|
||||
colors[2].fromPixel(colors[0]);
|
||||
colors[2].mult(2);
|
||||
colors[2].add(colors[1]);
|
||||
colors[2].divide(3);
|
||||
|
||||
// creating color3 = 1/3color0 + 2/3color1;
|
||||
colors[3].fromPixel(colors[1]);
|
||||
colors[3].mult(2);
|
||||
colors[3].add(colors[0]);
|
||||
colors[3].divide(3);
|
||||
} else {
|
||||
// creating color2 = 1/2color0 + 1/2color1
|
||||
colors[2].fromPixel(colors[0]);
|
||||
colors[2].add(colors[1]);
|
||||
colors[2].mult(0.5f);
|
||||
|
||||
colors[3].fromARGB8(0);
|
||||
}
|
||||
int indexes = data.getInt();// 4-byte table with color indexes in decompressed table
|
||||
texelData.add(colors, indexes);
|
||||
}
|
||||
break;
|
||||
case DXT3:// BC2
|
||||
bytes = new byte[image.getWidth() * image.getHeight() * 4];
|
||||
colors = new TexturePixel[] { new TexturePixel(), new TexturePixel(), new TexturePixel(), new TexturePixel() };
|
||||
while (data.hasRemaining()) {
|
||||
long alpha = data.getLong();
|
||||
float[] alphas = new float[16];
|
||||
long alphasIndex = 0;
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
alphasIndex |= i << i * 4;
|
||||
byte a = (byte) ((alpha >> i * 4 & 0x0F) << 4);
|
||||
alphas[i] = a >= 0 ? a / 255.0f : 1.0f - ~a / 255.0f;
|
||||
}
|
||||
|
||||
short c0 = data.getShort();
|
||||
short c1 = data.getShort();
|
||||
int col0 = RGB565.RGB565_to_ARGB8(c0);
|
||||
int col1 = RGB565.RGB565_to_ARGB8(c1);
|
||||
colors[0].fromARGB8(col0);
|
||||
colors[1].fromARGB8(col1);
|
||||
|
||||
// creating color2 = 2/3color0 + 1/3color1
|
||||
colors[2].fromPixel(colors[0]);
|
||||
colors[2].mult(2);
|
||||
colors[2].add(colors[1]);
|
||||
colors[2].divide(3);
|
||||
|
||||
// creating color3 = 1/3color0 + 2/3color1;
|
||||
colors[3].fromPixel(colors[1]);
|
||||
colors[3].mult(2);
|
||||
colors[3].add(colors[0]);
|
||||
colors[3].divide(3);
|
||||
|
||||
int indexes = data.getInt();// 4-byte table with color indexes in decompressed table
|
||||
texelData.add(colors, indexes, alphas, alphasIndex);
|
||||
}
|
||||
break;
|
||||
case DXT5:// BC3
|
||||
bytes = new byte[image.getWidth() * image.getHeight() * 4];
|
||||
colors = new TexturePixel[] { new TexturePixel(), new TexturePixel(), new TexturePixel(), new TexturePixel() };
|
||||
float[] alphas = new float[8];
|
||||
while (data.hasRemaining()) {
|
||||
alphas[0] = data.get() * 255.0f;
|
||||
alphas[1] = data.get() * 255.0f;
|
||||
long alphaIndices = data.get() | data.get() << 8 | data.get() << 16 | data.get() << 24 | data.get() << 32 | data.get() << 40;
|
||||
if (alphas[0] > alphas[1]) {// 6 interpolated alpha values.
|
||||
alphas[2] = (6 * alphas[0] + alphas[1]) / 7;
|
||||
alphas[3] = (5 * alphas[0] + 2 * alphas[1]) / 7;
|
||||
alphas[4] = (4 * alphas[0] + 3 * alphas[1]) / 7;
|
||||
alphas[5] = (3 * alphas[0] + 4 * alphas[1]) / 7;
|
||||
alphas[6] = (2 * alphas[0] + 5 * alphas[1]) / 7;
|
||||
alphas[7] = (alphas[0] + 6 * alphas[1]) / 7;
|
||||
} else {
|
||||
alphas[2] = (4 * alphas[0] + alphas[1]) * 0.2f;
|
||||
alphas[3] = (3 * alphas[0] + 2 * alphas[1]) * 0.2f;
|
||||
alphas[4] = (2 * alphas[0] + 3 * alphas[1]) * 0.2f;
|
||||
alphas[5] = (alphas[0] + 4 * alphas[1]) * 0.2f;
|
||||
alphas[6] = 0;
|
||||
alphas[7] = 1;
|
||||
}
|
||||
|
||||
short c0 = data.getShort();
|
||||
short c1 = data.getShort();
|
||||
int col0 = RGB565.RGB565_to_ARGB8(c0);
|
||||
int col1 = RGB565.RGB565_to_ARGB8(c1);
|
||||
colors[0].fromARGB8(col0);
|
||||
colors[1].fromARGB8(col1);
|
||||
|
||||
// creating color2 = 2/3color0 + 1/3color1
|
||||
colors[2].fromPixel(colors[0]);
|
||||
colors[2].mult(2);
|
||||
colors[2].add(colors[1]);
|
||||
colors[2].divide(3);
|
||||
|
||||
// creating color3 = 1/3color0 + 2/3color1;
|
||||
colors[3].fromPixel(colors[1]);
|
||||
colors[3].mult(2);
|
||||
colors[3].add(colors[0]);
|
||||
colors[3].divide(3);
|
||||
|
||||
int indexes = data.getInt();// 4-byte table with color indexes in decompressed table
|
||||
texelData.add(colors, indexes, alphas, alphaIndices);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
LOGGER.fine("Unsupported decompression format.");
|
||||
}
|
||||
|
||||
if (bytes != null) {// writing the data to the result table
|
||||
byte[] pixelBytes = new byte[4];
|
||||
for (int i = 0; i < image.getWidth(); ++i) {
|
||||
for (int j = 0; j < image.getHeight(); ++j) {
|
||||
texelData.getRGBA8(i, j, pixelBytes);
|
||||
bytes[(j * image.getWidth() + i) * 4] = pixelBytes[0];
|
||||
bytes[(j * image.getWidth() + i) * 4 + 1] = pixelBytes[1];
|
||||
bytes[(j * image.getWidth() + i) * 4 + 2] = pixelBytes[2];
|
||||
bytes[(j * image.getWidth() + i) * 4 + 3] = pixelBytes[3];
|
||||
}
|
||||
}
|
||||
// TODO: think of other image formats (ie. RGB if the texture has no
|
||||
// alpha values)
|
||||
return new Image(Format.RGBA8, image.getWidth(), image.getHeight(), BufferUtils.createByteBuffer(bytes));
|
||||
}
|
||||
return image;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns the height represented by the specified pixel in the
|
||||
* given texture. The given texture should be a height-map.
|
||||
*
|
||||
* @param image
|
||||
* the height-map texture
|
||||
* @param x
|
||||
* pixel's X coordinate
|
||||
* pixel's X coordinate
|
||||
* @param y
|
||||
* pixel's Y coordinate
|
||||
* pixel's Y coordinate
|
||||
* @return height reprezented by the given texture in the specified location
|
||||
*/
|
||||
protected int getHeight(BufferedImage image, int x, int y) {
|
||||
@ -362,10 +411,15 @@ public class TextureHelper extends AbstractBlenderHelper {
|
||||
}
|
||||
|
||||
/**
|
||||
* This method transforms given vector's coordinates into ARGB color (A is always = 255).
|
||||
* @param x X factor of the vector
|
||||
* @param y Y factor of the vector
|
||||
* @param z Z factor of the vector
|
||||
* This method transforms given vector's coordinates into ARGB color (A is
|
||||
* always = 255).
|
||||
*
|
||||
* @param x
|
||||
* X factor of the vector
|
||||
* @param y
|
||||
* Y factor of the vector
|
||||
* @param z
|
||||
* Z factor of the vector
|
||||
* @return color representation of the given vector
|
||||
*/
|
||||
protected int vectorToColor(float x, float y, float z) {
|
||||
@ -376,15 +430,17 @@ public class TextureHelper extends AbstractBlenderHelper {
|
||||
}
|
||||
|
||||
/**
|
||||
* This class returns a texture read from the file or from packed blender data.
|
||||
* This class returns a texture read from the file or from packed blender
|
||||
* data.
|
||||
*
|
||||
* @param image
|
||||
* image structure filled with data
|
||||
* image structure filled with data
|
||||
* @param blenderContext
|
||||
* the blender context
|
||||
* the blender context
|
||||
* @return the texture that can be used by JME engine
|
||||
* @throws BlenderFileException
|
||||
* this exception is thrown when the blend file structure is somehow invalid or corrupted
|
||||
* this exception is thrown when the blend file structure is
|
||||
* somehow invalid or corrupted
|
||||
*/
|
||||
public Texture getTextureFromImage(Structure image, BlenderContext blenderContext) throws BlenderFileException {
|
||||
LOGGER.log(Level.FINE, "Fetching texture with OMA = {0}", image.getOldMemoryAddress());
|
||||
@ -412,8 +468,8 @@ public class TextureHelper extends AbstractBlenderHelper {
|
||||
if (result != null) {
|
||||
result.setName(texturePath);
|
||||
result.setWrap(Texture.WrapMode.Repeat);
|
||||
if(LOGGER.isLoggable(Level.FINE)) {
|
||||
LOGGER.log(Level.FINE, "Adding texture {0} to the loaded features with OMA = {1}", new Object[] {texturePath, image.getOldMemoryAddress()});
|
||||
if (LOGGER.isLoggable(Level.FINE)) {
|
||||
LOGGER.log(Level.FINE, "Adding texture {0} to the loaded features with OMA = {1}", new Object[] { texturePath, image.getOldMemoryAddress() });
|
||||
}
|
||||
blenderContext.addLoadedFeatures(image.getOldMemoryAddress(), image.getName(), image, result);
|
||||
}
|
||||
@ -421,20 +477,71 @@ public class TextureHelper extends AbstractBlenderHelper {
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method applies the colorband and color factors to image type
|
||||
* textures. If there is no colorband defined for the texture or the color
|
||||
* factors are all equal to 1.0f then no changes are made.
|
||||
*
|
||||
* @param tex
|
||||
* the texture structure
|
||||
* @param image
|
||||
* the image that will be altered if necessary
|
||||
* @param blenderContext
|
||||
* the blender context
|
||||
*/
|
||||
private void applyColorbandAndColorFactors(Structure tex, Image image, BlenderContext blenderContext) {
|
||||
float rfac = ((Number) tex.getFieldValue("rfac")).floatValue();
|
||||
float gfac = ((Number) tex.getFieldValue("gfac")).floatValue();
|
||||
float bfac = ((Number) tex.getFieldValue("bfac")).floatValue();
|
||||
float[][] colorBand = new ColorBand(tex, blenderContext).computeValues();
|
||||
|
||||
if (colorBand != null) {
|
||||
TexturePixel pixel = new TexturePixel();
|
||||
PixelInputOutput imageIO = PixelIOFactory.getPixelIO(image.getFormat());
|
||||
for (int x = 0; x < image.getWidth(); ++x) {
|
||||
for (int y = 0; y < image.getHeight(); ++y) {
|
||||
imageIO.read(image, pixel, x, y);
|
||||
|
||||
int colorbandIndex = (int) (pixel.alpha * 1000.0f);
|
||||
pixel.red = colorBand[colorbandIndex][0] * rfac;
|
||||
pixel.green = colorBand[colorbandIndex][1] * gfac;
|
||||
pixel.blue = colorBand[colorbandIndex][2] * bfac;
|
||||
pixel.alpha = colorBand[colorbandIndex][3];
|
||||
|
||||
imageIO.write(image, pixel, x, y);
|
||||
}
|
||||
}
|
||||
} else if (rfac != 1.0f || gfac != 1.0f || bfac != 1.0f) {
|
||||
TexturePixel pixel = new TexturePixel();
|
||||
PixelInputOutput imageIO = PixelIOFactory.getPixelIO(image.getFormat());
|
||||
for (int x = 0; x < image.getWidth(); ++x) {
|
||||
for (int y = 0; y < image.getHeight(); ++y) {
|
||||
imageIO.read(image, pixel, x, y);
|
||||
|
||||
pixel.red *= rfac;
|
||||
pixel.green *= gfac;
|
||||
pixel.blue *= bfac;
|
||||
|
||||
imageIO.write(image, pixel, x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method loads the textre from outside the blend file.
|
||||
*
|
||||
* @param name
|
||||
* the path to the image
|
||||
* the path to the image
|
||||
* @param blenderContext
|
||||
* the blender context
|
||||
* the blender context
|
||||
* @return the loaded image or null if the image cannot be found
|
||||
*/
|
||||
protected Texture loadTextureFromFile(String name, BlenderContext blenderContext) {
|
||||
if (!name.contains(".")){
|
||||
return null; // no extension means not a valid image
|
||||
}
|
||||
|
||||
if (!name.contains(".")) {
|
||||
return null; // no extension means not a valid image
|
||||
}
|
||||
|
||||
AssetManager assetManager = blenderContext.getAssetManager();
|
||||
name = name.replaceAll("\\\\", "\\/");
|
||||
Texture result = null;
|
||||
@ -442,32 +549,34 @@ public class TextureHelper extends AbstractBlenderHelper {
|
||||
List<String> assetNames = new ArrayList<String>();
|
||||
if (name.startsWith("//")) {
|
||||
String relativePath = name.substring(2);
|
||||
//augument the path with blender key path
|
||||
// augument the path with blender key path
|
||||
BlenderKey blenderKey = blenderContext.getBlenderKey();
|
||||
int idx = blenderKey.getName().lastIndexOf('/');
|
||||
int idx = blenderKey.getName().lastIndexOf('/');
|
||||
String blenderAssetFolder = blenderKey.getName().substring(0, idx != -1 ? idx : 0);
|
||||
assetNames.add(blenderAssetFolder+'/'+relativePath);
|
||||
} else {//use every path from the asset name to the root (absolute path)
|
||||
assetNames.add(blenderAssetFolder + '/' + relativePath);
|
||||
} else {// use every path from the asset name to the root (absolute
|
||||
// path)
|
||||
String[] paths = name.split("\\/");
|
||||
StringBuilder sb = new StringBuilder(paths[paths.length-1]);//the asset name
|
||||
assetNames.add(paths[paths.length-1]);
|
||||
StringBuilder sb = new StringBuilder(paths[paths.length - 1]);// the asset name
|
||||
assetNames.add(paths[paths.length - 1]);
|
||||
|
||||
for(int i=paths.length-2;i>=0;--i) {
|
||||
for (int i = paths.length - 2; i >= 0; --i) {
|
||||
sb.insert(0, '/');
|
||||
sb.insert(0, paths[i]);
|
||||
assetNames.add(0, sb.toString());
|
||||
}
|
||||
}
|
||||
|
||||
//now try to locate the asset
|
||||
for(String assetName : assetNames) {
|
||||
// now try to locate the asset
|
||||
for (String assetName : assetNames) {
|
||||
try {
|
||||
TextureKey key = new TextureKey(assetName);
|
||||
key.setGenerateMips(true);
|
||||
key.setAsCube(false);
|
||||
TextureKey key = new TextureKey(assetName);
|
||||
key.setGenerateMips(true);
|
||||
key.setAsCube(false);
|
||||
result = assetManager.loadTexture(key);
|
||||
break;//if no exception is thrown then accept the located asset and break the loop
|
||||
} catch(AssetNotFoundException e) {
|
||||
break;// if no exception is thrown then accept the located asset
|
||||
// and break the loop
|
||||
} catch (AssetNotFoundException e) {
|
||||
LOGGER.fine(e.getLocalizedMessage());
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -0,0 +1,721 @@
|
||||
package com.jme3.scene.plugins.blender.textures;
|
||||
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.RenderingHints;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import jme3tools.converters.ImageToAwt;
|
||||
|
||||
import com.jme3.bounding.BoundingBox;
|
||||
import com.jme3.math.FastMath;
|
||||
import com.jme3.math.Vector2f;
|
||||
import com.jme3.math.Vector3f;
|
||||
import com.jme3.scene.plugins.blender.BlenderContext;
|
||||
import com.jme3.scene.plugins.blender.textures.blending.TextureBlender;
|
||||
import com.jme3.scene.plugins.blender.textures.io.PixelIOFactory;
|
||||
import com.jme3.scene.plugins.blender.textures.io.PixelInputOutput;
|
||||
import com.jme3.texture.Image;
|
||||
import com.jme3.texture.Image.Format;
|
||||
import com.jme3.texture.Texture;
|
||||
import com.jme3.texture.Texture2D;
|
||||
import com.jme3.util.BufferUtils;
|
||||
|
||||
/**
|
||||
* This texture holds a set of images for each face in the specified mesh. It
|
||||
* helps to flatten 3D texture, merge 3D and 2D textures and merge 2D textures
|
||||
* with different UV coordinates.
|
||||
*
|
||||
* @author Marcin Roguski (Kaelthas)
|
||||
*/
|
||||
/* package */class TriangulatedTexture extends Texture {
|
||||
/** The result image format. */
|
||||
private Format format;
|
||||
/** The collection of images for each face. */
|
||||
private Collection<TriangleTextureElement> faceTextures;
|
||||
/**
|
||||
* The maximum texture size (width/height). This is taken from the blender
|
||||
* key.
|
||||
*/
|
||||
private int maxTextureSize;
|
||||
|
||||
/** The result texture. */
|
||||
private Texture2D resultTexture;
|
||||
/** The result texture's UV coordinates. */
|
||||
private List<Vector2f> resultUVS;
|
||||
|
||||
/**
|
||||
* This method triangulates the given flat texture. The given texture is not
|
||||
* changed.
|
||||
*
|
||||
* @param texture2d
|
||||
* the texture to be triangulated
|
||||
* @param uvs
|
||||
* the UV coordinates for each face
|
||||
*/
|
||||
public TriangulatedTexture(Texture2D texture2d, List<Vector2f> uvs, BlenderContext blenderContext) {
|
||||
maxTextureSize = blenderContext.getBlenderKey().getMaxTextureSize();
|
||||
faceTextures = new TreeSet<TriangleTextureElement>(new Comparator<TriangleTextureElement>() {
|
||||
@Override
|
||||
public int compare(TriangleTextureElement o1, TriangleTextureElement o2) {
|
||||
return o1.faceIndex - o2.faceIndex;
|
||||
}
|
||||
});
|
||||
int facesCount = uvs.size() / 3;
|
||||
for (int i = 0; i < facesCount; ++i) {
|
||||
faceTextures.add(new TriangleTextureElement(i, texture2d, uvs));
|
||||
}
|
||||
this.format = texture2d.getImage().getFormat();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor that simply stores precalculated images.
|
||||
*
|
||||
* @param faceTextures
|
||||
* a collection of images for the mesh's faces
|
||||
* @param blenderContext
|
||||
* the blender context
|
||||
*/
|
||||
public TriangulatedTexture(Collection<TriangleTextureElement> faceTextures, BlenderContext blenderContext) {
|
||||
maxTextureSize = blenderContext.getBlenderKey().getMaxTextureSize();
|
||||
this.faceTextures = faceTextures;
|
||||
for (TriangleTextureElement faceTextureElement : faceTextures) {
|
||||
if (format == null) {
|
||||
format = faceTextureElement.image.getFormat();
|
||||
} else if (format != faceTextureElement.image.getFormat()) {
|
||||
throw new IllegalArgumentException("Face texture element images MUST have the same image format!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method blends the each image using the given blender and taking base
|
||||
* texture into consideration.
|
||||
*
|
||||
* @param textureBlender
|
||||
* the texture blender that holds the blending definition
|
||||
* @param baseTexture
|
||||
* the texture that is 'below' the current texture (can be null)
|
||||
* @param blenderContext
|
||||
* the blender context
|
||||
*/
|
||||
public void blend(TextureBlender textureBlender, TriangulatedTexture baseTexture, BlenderContext blenderContext) {
|
||||
Format newFormat = null;
|
||||
for (TriangleTextureElement triangleTextureElement : this.faceTextures) {
|
||||
Image baseImage = baseTexture == null ? null : baseTexture.getFaceTextureElement(triangleTextureElement.faceIndex).image;
|
||||
triangleTextureElement.image = textureBlender.blend(triangleTextureElement.image, baseImage, blenderContext);
|
||||
if (newFormat == null) {
|
||||
newFormat = triangleTextureElement.image.getFormat();
|
||||
} else if (newFormat != triangleTextureElement.image.getFormat()) {
|
||||
throw new IllegalArgumentException("Face texture element images MUST have the same image format!");
|
||||
}
|
||||
}
|
||||
this.format = newFormat;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method alters the images to fit them into UV coordinates of the
|
||||
* given target texture.
|
||||
*
|
||||
* @param targetTexture
|
||||
* the texture to whose UV coordinates we fit current images
|
||||
* @param blenderContext
|
||||
* the blender context
|
||||
*/
|
||||
public void castToUVS(TriangulatedTexture targetTexture, BlenderContext blenderContext) {
|
||||
int[] sourceSize = new int[2], targetSize = new int[2];
|
||||
ImageLoader imageLoader = new ImageLoader();
|
||||
for (TriangleTextureElement entry : faceTextures) {
|
||||
TriangleTextureElement targetFaceTextureElement = targetTexture.getFaceTextureElement(entry.faceIndex);
|
||||
Vector2f[] dest = targetFaceTextureElement.uv;
|
||||
|
||||
// get the sizes of the source and target images
|
||||
sourceSize[0] = entry.image.getWidth();
|
||||
sourceSize[1] = entry.image.getHeight();
|
||||
targetSize[0] = targetFaceTextureElement.image.getWidth();
|
||||
targetSize[1] = targetFaceTextureElement.image.getHeight();
|
||||
|
||||
// create triangle transformation
|
||||
AffineTransform affineTransform = this.createTransform(entry.uv, dest, sourceSize, targetSize);
|
||||
|
||||
// compute the result texture
|
||||
BufferedImage sourceImage = ImageToAwt.convert(entry.image, false, true, 0);
|
||||
|
||||
BufferedImage targetImage = new BufferedImage(targetSize[0], targetSize[1], sourceImage.getType());
|
||||
Graphics2D g = targetImage.createGraphics();
|
||||
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
|
||||
g.drawImage(sourceImage, affineTransform, null);
|
||||
g.dispose();
|
||||
|
||||
Image output = imageLoader.load(targetImage, false);
|
||||
entry.image = output;
|
||||
entry.uv[0].set(dest[0]);
|
||||
entry.uv[1].set(dest[1]);
|
||||
entry.uv[2].set(dest[2]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method merges the current texture with the given one. The given
|
||||
* texture is not changed.
|
||||
*
|
||||
* @param triangulatedTexture
|
||||
* the texture we merge current texture with
|
||||
*/
|
||||
public void merge(TriangulatedTexture triangulatedTexture) {
|
||||
TexturePixel sourcePixel = new TexturePixel();
|
||||
TexturePixel targetPixel = new TexturePixel();
|
||||
for (TriangleTextureElement triangleTextureElement : this.faceTextures) {
|
||||
Image sourceImage = triangulatedTexture.getFaceTextureElement(triangleTextureElement.faceIndex).image;
|
||||
Image targetImage = triangleTextureElement.image;
|
||||
PixelInputOutput sourceIO = PixelIOFactory.getPixelIO(sourceImage.getFormat());
|
||||
PixelInputOutput targetIO = PixelIOFactory.getPixelIO(targetImage.getFormat());
|
||||
|
||||
for (int x = 0; x < sourceImage.getWidth(); ++x) {
|
||||
for (int y = 0; y < sourceImage.getHeight(); ++y) {
|
||||
sourceIO.read(sourceImage, sourcePixel, x, y);
|
||||
targetIO.read(targetImage, targetPixel, x, y);
|
||||
targetPixel.merge(sourcePixel);
|
||||
targetIO.write(targetImage, targetPixel, x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns the flat texture. It is calculated if required or if
|
||||
* it was not created before. Images that are identical are discarded to
|
||||
* reduce the texture size.
|
||||
*
|
||||
* @param rebuild
|
||||
* a variable that forces texture recomputation (even if it was
|
||||
* computed vefore)
|
||||
* @return flat result texture (all images merged into one)
|
||||
*/
|
||||
public Texture2D getResultTexture(boolean rebuild) {
|
||||
if (resultTexture == null || rebuild) {
|
||||
// sorting the parts by their height (from highest to the lowest)
|
||||
List<TriangleTextureElement> list = new ArrayList<TriangleTextureElement>(faceTextures);
|
||||
Collections.sort(list, new Comparator<TriangleTextureElement>() {
|
||||
@Override
|
||||
public int compare(TriangleTextureElement o1, TriangleTextureElement o2) {
|
||||
return o2.image.getHeight() - o1.image.getHeight();
|
||||
}
|
||||
});
|
||||
|
||||
// arraging the images on the resulting image (calculating the result image width and height)
|
||||
Set<Integer> duplicatedFaceIndexes = new HashSet<Integer>();
|
||||
int resultImageHeight = list.get(0).image.getHeight();
|
||||
int resultImageWidth = 0;
|
||||
int currentXPos = 0, currentYPos = 0;
|
||||
Map<TriangleTextureElement, Integer[]> imageLayoutData = new HashMap<TriangleTextureElement, Integer[]>(list.size());
|
||||
while (list.size() > 0) {
|
||||
TriangleTextureElement currentElement = list.remove(0);
|
||||
if (currentXPos + currentElement.image.getWidth() > maxTextureSize) {
|
||||
currentXPos = 0;
|
||||
currentYPos = resultImageHeight;
|
||||
resultImageHeight += currentElement.image.getHeight();
|
||||
}
|
||||
Integer[] currentPositions = new Integer[] { currentXPos, currentYPos };
|
||||
imageLayoutData.put(currentElement, currentPositions);
|
||||
|
||||
// removing identical images
|
||||
for (int i = 0; i < list.size(); ++i) {
|
||||
if (currentElement.image.equals(list.get(i).image)) {
|
||||
duplicatedFaceIndexes.add(list.get(i).faceIndex);
|
||||
imageLayoutData.put(list.remove(i--), currentPositions);
|
||||
}
|
||||
}
|
||||
|
||||
currentXPos += currentElement.image.getWidth();
|
||||
resultImageWidth = Math.max(resultImageWidth, currentXPos);
|
||||
// currentYPos += currentElement.image.getHeight();
|
||||
|
||||
// TODO: implement that to compact the result image
|
||||
// try to add smaller images below the current one
|
||||
// int remainingHeight = resultImageHeight -
|
||||
// currentElement.image.getHeight();
|
||||
// while(remainingHeight > 0) {
|
||||
// for(int i=list.size() - 1;i>=0;--i) {
|
||||
//
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
// computing the result UV coordinates
|
||||
resultUVS = new ArrayList<Vector2f>(imageLayoutData.size() * 3);
|
||||
for (int i = 0; i < imageLayoutData.size() * 3; ++i) {
|
||||
resultUVS.add(null);
|
||||
}
|
||||
Vector2f[] uvs = new Vector2f[3];
|
||||
for (Entry<TriangleTextureElement, Integer[]> entry : imageLayoutData.entrySet()) {
|
||||
Integer[] position = entry.getValue();
|
||||
entry.getKey().computeFinalUVCoordinates(resultImageWidth, resultImageHeight, position[0], position[1], uvs);
|
||||
resultUVS.set(entry.getKey().faceIndex * 3, uvs[0]);
|
||||
resultUVS.set(entry.getKey().faceIndex * 3 + 1, uvs[1]);
|
||||
resultUVS.set(entry.getKey().faceIndex * 3 + 2, uvs[2]);
|
||||
}
|
||||
|
||||
Image resultImage = new Image(format, resultImageWidth, resultImageHeight, BufferUtils.createByteBuffer(resultImageWidth * resultImageHeight * (format.getBitsPerPixel() >> 3)));
|
||||
resultTexture = new Texture2D(resultImage);
|
||||
for (Entry<TriangleTextureElement, Integer[]> entry : imageLayoutData.entrySet()) {
|
||||
if (!duplicatedFaceIndexes.contains(entry.getKey().faceIndex)) {
|
||||
this.draw(resultImage, entry.getKey().image, entry.getValue()[0], entry.getValue()[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return resultTexture;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the result flat texture
|
||||
*/
|
||||
public Texture2D getResultTexture() {
|
||||
return this.getResultTexture(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the result texture's UV coordinates
|
||||
*/
|
||||
public List<Vector2f> getResultUVS() {
|
||||
this.getResultTexture();// this is called here to make sure that the result UVS are computed
|
||||
return resultUVS;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns a single image element for the given face index.
|
||||
*
|
||||
* @param faceIndex
|
||||
* the face index
|
||||
* @return image element for the required face index
|
||||
* @throws IllegalStateException
|
||||
* this exception is thrown if the current image set does not
|
||||
* contain an image for the given face index
|
||||
*/
|
||||
private TriangleTextureElement getFaceTextureElement(int faceIndex) {
|
||||
for (TriangleTextureElement textureElement : faceTextures) {
|
||||
if (textureElement.faceIndex == faceIndex) {
|
||||
return textureElement;
|
||||
}
|
||||
}
|
||||
throw new IllegalStateException("No face texture element found for index: " + faceIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method draws the source image on the target image starting with the
|
||||
* specified positions.
|
||||
*
|
||||
* @param target
|
||||
* the target image
|
||||
* @param source
|
||||
* the source image
|
||||
* @param targetXPos
|
||||
* start X position on the target image
|
||||
* @param targetYPos
|
||||
* start Y position on the target image
|
||||
*/
|
||||
private void draw(Image target, Image source, int targetXPos, int targetYPos) {
|
||||
PixelInputOutput sourceIO = PixelIOFactory.getPixelIO(source.getFormat());
|
||||
PixelInputOutput targetIO = PixelIOFactory.getPixelIO(target.getFormat());
|
||||
TexturePixel pixel = new TexturePixel();
|
||||
|
||||
for (int x = 0; x < source.getWidth(); ++x) {
|
||||
for (int y = 0; y < source.getHeight(); ++y) {
|
||||
sourceIO.read(source, pixel, x, y);
|
||||
targetIO.write(target, pixel, targetXPos + x, targetYPos + y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method creates the affine transform that is used to transform a
|
||||
* triangle defined by one UV coordinates into a triangle defined by
|
||||
* different UV's.
|
||||
*
|
||||
* @param source
|
||||
* source UV coordinates
|
||||
* @param dest
|
||||
* target UV coordinates
|
||||
* @param sourceSize
|
||||
* the width and height of the source image
|
||||
* @param targetSize
|
||||
* the width and height of the target image
|
||||
* @return affine transform to transform one triangle to another
|
||||
*/
|
||||
private AffineTransform createTransform(Vector2f[] source, Vector2f[] dest, int[] sourceSize, int[] targetSize) {
|
||||
float x11 = source[0].getX() * sourceSize[0];
|
||||
float x12 = source[0].getY() * sourceSize[1];
|
||||
float x21 = source[1].getX() * sourceSize[0];
|
||||
float x22 = source[1].getY() * sourceSize[1];
|
||||
float x31 = source[2].getX() * sourceSize[0];
|
||||
float x32 = source[2].getY() * sourceSize[1];
|
||||
float y11 = dest[0].getX() * targetSize[0];
|
||||
float y12 = dest[0].getY() * targetSize[1];
|
||||
float y21 = dest[1].getX() * targetSize[0];
|
||||
float y22 = dest[1].getY() * targetSize[1];
|
||||
float y31 = dest[2].getX() * targetSize[0];
|
||||
float y32 = dest[2].getY() * targetSize[1];
|
||||
|
||||
float a1 = ((y11 - y21) * (x12 - x32) - (y11 - y31) * (x12 - x22)) / ((x11 - x21) * (x12 - x32) - (x11 - x31) * (x12 - x22));
|
||||
float a2 = ((y11 - y21) * (x11 - x31) - (y11 - y31) * (x11 - x21)) / ((x12 - x22) * (x11 - x31) - (x12 - x32) * (x11 - x21));
|
||||
float a3 = y11 - a1 * x11 - a2 * x12;
|
||||
float a4 = ((y12 - y22) * (x12 - x32) - (y12 - y32) * (x12 - x22)) / ((x11 - x21) * (x12 - x32) - (x11 - x31) * (x12 - x22));
|
||||
float a5 = ((y12 - y22) * (x11 - x31) - (y12 - y32) * (x11 - x21)) / ((x12 - x22) * (x11 - x31) - (x12 - x32) * (x11 - x21));
|
||||
float a6 = y12 - a4 * x11 - a5 * x12;
|
||||
return new AffineTransform(a1, a4, a2, a5, a3, a6);
|
||||
}
|
||||
|
||||
/**
|
||||
* A class that represents an image for a single face of the mesh.
|
||||
*
|
||||
* @author Marcin Roguski (Kaelthas)
|
||||
*/
|
||||
/* package */static class TriangleTextureElement {
|
||||
/** The image for the face. */
|
||||
public Image image;
|
||||
/** The UV coordinates for the image. */
|
||||
public final Vector2f[] uv;
|
||||
/** The index of the face this image refers to. */
|
||||
public final int faceIndex;
|
||||
|
||||
/**
|
||||
* Constructor that creates the image element from the given texture and
|
||||
* UV coordinates (it cuts out the smallest rectasngle possible from the
|
||||
* given image that will hold the triangle defined by the given UV
|
||||
* coordinates). After the image is cut out the UV coordinates are
|
||||
* recalculated to be fit for the new image.
|
||||
*
|
||||
* @param faceIndex
|
||||
* the index of mesh's face this image refers to
|
||||
* @param texture
|
||||
* the source texture
|
||||
* @param uvCoordinates
|
||||
* the UV coordinates that define the image
|
||||
*/
|
||||
public TriangleTextureElement(int faceIndex, Texture2D texture, List<Vector2f> uvCoordinates) {
|
||||
this.faceIndex = faceIndex;
|
||||
Image sourceImage = texture.getImage();
|
||||
|
||||
uv = new Vector2f[] { uvCoordinates.get(faceIndex * 3).clone(), uvCoordinates.get(faceIndex * 3 + 1).clone(), uvCoordinates.get(faceIndex * 3 + 2).clone() };
|
||||
|
||||
float pixelWidth = 1 / (float) sourceImage.getWidth();
|
||||
float pixelHeight = 1 / (float) sourceImage.getHeight();
|
||||
|
||||
// be careful here, floating point operations might cause the
|
||||
// texture positions to be inapropriate
|
||||
int[][] texturePosition = new int[3][2];
|
||||
for (int i = 0; i < texturePosition.length; ++i) {
|
||||
float x = uv[i].x * sourceImage.getWidth();
|
||||
float y = uv[i].y * sourceImage.getHeight();
|
||||
// here is where errors may occur
|
||||
texturePosition[i][0] = (int) x;
|
||||
texturePosition[i][1] = (int) y;
|
||||
// here is where we repair errors :)
|
||||
if (Math.abs(texturePosition[i][0] - x) > pixelWidth) {
|
||||
++texturePosition[i][0];
|
||||
}
|
||||
if (Math.abs(texturePosition[i][1] - y) > pixelHeight) {
|
||||
++texturePosition[i][1];
|
||||
}
|
||||
}
|
||||
|
||||
// calculating the extent of the texture
|
||||
int minX = Integer.MAX_VALUE, minY = Integer.MAX_VALUE;
|
||||
int maxX = Integer.MIN_VALUE, maxY = Integer.MIN_VALUE;
|
||||
float minUVX = Float.MAX_VALUE, minUVY = Float.MAX_VALUE;
|
||||
float maxUVX = Float.MIN_VALUE, maxUVY = Float.MIN_VALUE;
|
||||
|
||||
for (int i = 0; i < texturePosition.length; ++i) {
|
||||
minX = Math.min(texturePosition[i][0], minX);
|
||||
minY = Math.min(texturePosition[i][1], minY);
|
||||
|
||||
maxX = Math.max(texturePosition[i][0], maxX);
|
||||
maxY = Math.max(texturePosition[i][1], maxY);
|
||||
|
||||
minUVX = Math.min(uv[i].x, minUVX);
|
||||
minUVY = Math.min(uv[i].y, minUVY);
|
||||
maxUVX = Math.max(uv[i].x, maxUVX);
|
||||
maxUVY = Math.max(uv[i].y, maxUVY);
|
||||
}
|
||||
int width = maxX - minX;
|
||||
int height = maxY - minY;
|
||||
|
||||
if (width == 0) {
|
||||
width = 1;
|
||||
}
|
||||
if (height == 0) {
|
||||
height = 1;
|
||||
}
|
||||
|
||||
// copy the pixel from the texture to the result image
|
||||
PixelInputOutput pixelReader = PixelIOFactory.getPixelIO(sourceImage.getFormat());
|
||||
TexturePixel pixel = new TexturePixel();
|
||||
ByteBuffer data = BufferUtils.createByteBuffer(width * height * 4);
|
||||
for (int y = minY; y < maxY; ++y) {
|
||||
for (int x = minX; x < maxX; ++x) {
|
||||
int xPos = x >= sourceImage.getWidth() ? x - sourceImage.getWidth() : x;
|
||||
int yPos = y >= sourceImage.getHeight() ? y - sourceImage.getHeight() : y;
|
||||
pixelReader.read(sourceImage, pixel, xPos, yPos);
|
||||
data.put(pixel.getR8());
|
||||
data.put(pixel.getG8());
|
||||
data.put(pixel.getB8());
|
||||
data.put(pixel.getA8());
|
||||
}
|
||||
}
|
||||
image = new Image(Format.RGBA8, width, height, data);
|
||||
|
||||
// modify the UV values so that they fit the new image
|
||||
float heightUV = maxUVY - minUVY;
|
||||
float widthUV = maxUVX - minUVX;
|
||||
for (int i = 0; i < uv.length; ++i) {
|
||||
// first translate it to the image borders
|
||||
uv[i].x -= minUVX;
|
||||
uv[i].y -= minUVY;
|
||||
// then scale so that it fills the whole area
|
||||
uv[i].x /= widthUV;
|
||||
uv[i].y /= heightUV;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor that creates an image element from the 3D texture
|
||||
* (generated texture). It computes a flat smallest rectangle that can
|
||||
* hold a (3D) triangle defined by the given UV coordinates. Then it
|
||||
* defines the image pixels for points in 3D space that define the
|
||||
* calculated rectangle.
|
||||
*
|
||||
* @param faceIndex
|
||||
* the face index this image refers to
|
||||
* @param boundingBox
|
||||
* the bounding box of the mesh
|
||||
* @param texture
|
||||
* the texture that allows to compute a pixel value in 3D
|
||||
* space
|
||||
* @param uv
|
||||
* the UV coordinates of the mesh
|
||||
* @param blenderContext
|
||||
* the blender context
|
||||
*/
|
||||
public TriangleTextureElement(int faceIndex, BoundingBox boundingBox, GeneratedTexture texture, Vector3f[] uv, BlenderContext blenderContext) {
|
||||
this.faceIndex = faceIndex;
|
||||
|
||||
// compute the face vertices from the UV coordinates
|
||||
float width = boundingBox.getXExtent() * 2;
|
||||
float height = boundingBox.getYExtent() * 2;
|
||||
float depth = boundingBox.getZExtent() * 2;
|
||||
|
||||
int uvIndex = faceIndex * 3;
|
||||
Vector3f min = boundingBox.getMin(null);
|
||||
Vector3f v1 = min.add(uv[uvIndex].x * width, uv[uvIndex].y * height, uv[uvIndex].z * depth);
|
||||
Vector3f v2 = min.add(uv[uvIndex + 1].x * width, uv[uvIndex + 1].y * height, uv[uvIndex + 1].z * depth);
|
||||
Vector3f v3 = min.add(uv[uvIndex + 2].x * width, uv[uvIndex + 2].y * height, uv[uvIndex + 2].z * depth);
|
||||
|
||||
// get the rectangle envelope for the triangle
|
||||
RectangleEnvelope envelope = this.getTriangleEnvelope(v1, v2, v3);
|
||||
|
||||
// create the result image
|
||||
Format imageFormat = texture.getImage().getFormat();
|
||||
int imageWidth = (int) (envelope.width * blenderContext.getBlenderKey().getGeneratedTexturePPU());
|
||||
int imageHeight = (int) (envelope.height * blenderContext.getBlenderKey().getGeneratedTexturePPU());
|
||||
ByteBuffer data = BufferUtils.createByteBuffer(imageWidth * imageHeight * (imageFormat.getBitsPerPixel() >> 3));
|
||||
image = new Image(texture.getImage().getFormat(), imageWidth, imageHeight, data);
|
||||
|
||||
// computing the pixels
|
||||
PixelInputOutput pixelWriter = PixelIOFactory.getPixelIO(imageFormat);
|
||||
TexturePixel pixel = new TexturePixel();
|
||||
float[] uvs = new float[3];
|
||||
Vector3f point = new Vector3f(envelope.min);
|
||||
Vector3f vecY = new Vector3f();
|
||||
Vector3f wDelta = new Vector3f(envelope.w).multLocal(1.0f / imageWidth);
|
||||
Vector3f hDelta = new Vector3f(envelope.h).multLocal(1.0f / imageHeight);
|
||||
for (int x = 0; x < imageWidth; ++x) {
|
||||
for (int y = 0; y < imageHeight; ++y) {
|
||||
this.toTextureUV(boundingBox, point, uvs);
|
||||
texture.getPixel(pixel, uvs[0], uvs[1], uvs[2]);
|
||||
pixelWriter.write(image, pixel, x, y);
|
||||
point.addLocal(hDelta);
|
||||
}
|
||||
|
||||
vecY.addLocal(wDelta);
|
||||
point.set(envelope.min).addLocal(vecY);
|
||||
}
|
||||
|
||||
// preparing UV coordinates for the flatted texture
|
||||
this.uv = new Vector2f[3];
|
||||
this.uv[0] = new Vector2f(FastMath.clamp(v1.subtract(envelope.min).length(), 0, Float.MAX_VALUE) / envelope.height, 0);
|
||||
Vector3f heightDropPoint = v2.subtract(envelope.w);// w is directed from the base to v2
|
||||
this.uv[1] = new Vector2f(1, heightDropPoint.subtractLocal(envelope.min).length() / envelope.height);
|
||||
this.uv[2] = new Vector2f(0, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method computes the final UV coordinates for the image (after it
|
||||
* is combined with other images and drawed on the result image).
|
||||
*
|
||||
* @param totalImageWidth
|
||||
* the result image width
|
||||
* @param totalImageHeight
|
||||
* the result image height
|
||||
* @param xPos
|
||||
* the most left x coordinate of the image
|
||||
* @param yPos
|
||||
* the most top y coordinate of the image
|
||||
* @param result
|
||||
* a vector where the result is stored
|
||||
*/
|
||||
public void computeFinalUVCoordinates(int totalImageWidth, int totalImageHeight, int xPos, int yPos, Vector2f[] result) {
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
result[i] = new Vector2f();
|
||||
result[i].x = xPos / (float) totalImageWidth + this.uv[i].x * (this.image.getWidth() / (float) totalImageWidth);
|
||||
result[i].y = yPos / (float) totalImageHeight + this.uv[i].y * (this.image.getHeight() / (float) totalImageHeight);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method converts the given point into 3D UV coordinates.
|
||||
*
|
||||
* @param boundingBox
|
||||
* the bounding box of the mesh
|
||||
* @param point
|
||||
* the point to be transformed
|
||||
* @param uvs
|
||||
* the result UV coordinates
|
||||
*/
|
||||
private void toTextureUV(BoundingBox boundingBox, Vector3f point, float[] uvs) {
|
||||
uvs[0] = (point.x - boundingBox.getCenter().x)/(boundingBox.getXExtent() == 0 ? 1 : boundingBox.getXExtent());
|
||||
uvs[1] = (point.y - boundingBox.getCenter().y)/(boundingBox.getYExtent() == 0 ? 1 : boundingBox.getYExtent());
|
||||
uvs[2] = (point.z - boundingBox.getCenter().z)/(boundingBox.getZExtent() == 0 ? 1 : boundingBox.getZExtent());
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns an envelope of a minimal rectangle, that is set
|
||||
* in 3D space, and contains the given triangle.
|
||||
*
|
||||
* @param triangle
|
||||
* the triangle
|
||||
* @return a rectangle minimum and maximum point and height and width
|
||||
*/
|
||||
private RectangleEnvelope getTriangleEnvelope(Vector3f v1, Vector3f v2, Vector3f v3) {
|
||||
Vector3f h = v3.subtract(v1);// the height of the resulting rectangle
|
||||
Vector3f temp = v2.subtract(v1);
|
||||
|
||||
float field = 0.5f * h.cross(temp).length();// the field of the rectangle: Field = 0.5 * ||h x temp||
|
||||
if (field <= 0.0f) {
|
||||
return new RectangleEnvelope(v1);// return single point envelope
|
||||
}
|
||||
|
||||
float cosAlpha = h.dot(temp) / (h.length() * temp.length());// the cosinus of angle betweenh and temp
|
||||
|
||||
float triangleHeight = 2 * field / h.length();// the base of the height is the h vector
|
||||
// now calculate the distance between v1 vertex and the point where
|
||||
// the above calculated height 'touches' the base line (it can be
|
||||
// settled outside the h vector)
|
||||
float x = Math.abs((float) Math.sqrt(FastMath.clamp(temp.lengthSquared() - triangleHeight * triangleHeight, 0, Float.MAX_VALUE))) * Math.signum(cosAlpha);
|
||||
// now get the height base point
|
||||
Vector3f xPoint = v1.add(h.normalize().multLocal(x));
|
||||
|
||||
// get the minimum point of the envelope
|
||||
Vector3f min = x < 0 ? xPoint : v1;
|
||||
if (x < 0) {
|
||||
h = v3.subtract(min);
|
||||
} else if (x > h.length()) {
|
||||
h = xPoint.subtract(min);
|
||||
}
|
||||
|
||||
Vector3f envelopeWidth = v2.subtract(xPoint);
|
||||
return new RectangleEnvelope(min, envelopeWidth, h);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A class that represents a flat rectangle in 3D space that is built on a
|
||||
* triangle in 3D space.
|
||||
*
|
||||
* @author Marcin Roguski (Kaelthas)
|
||||
*/
|
||||
private static class RectangleEnvelope {
|
||||
/** The minimum point of the rectangle. */
|
||||
public final Vector3f min;
|
||||
/** The width vector. */
|
||||
public final Vector3f w;
|
||||
/** The height vector. */
|
||||
public final Vector3f h;
|
||||
/** The width of the rectangle. */
|
||||
public final float width;
|
||||
/** The height of the rectangle. */
|
||||
public final float height;
|
||||
|
||||
/**
|
||||
* Constructs a rectangle that actually holds a point, not a triangle.
|
||||
* This is a special case that is sometimes used when generating a
|
||||
* texture where UV coordinates are defined by normals instead of
|
||||
* vertices.
|
||||
*
|
||||
* @param pointPosition
|
||||
* a position in 3D space
|
||||
*/
|
||||
public RectangleEnvelope(Vector3f pointPosition) {
|
||||
this.min = pointPosition;
|
||||
this.h = this.w = Vector3f.ZERO;
|
||||
this.width = this.height = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a rectangle envelope.
|
||||
*
|
||||
* @param min
|
||||
* the minimum rectangle point
|
||||
* @param w
|
||||
* the width vector
|
||||
* @param h
|
||||
* the height vector
|
||||
*/
|
||||
public RectangleEnvelope(Vector3f min, Vector3f w, Vector3f h) {
|
||||
this.min = min;
|
||||
this.h = h;
|
||||
this.w = w;
|
||||
this.width = w.length();
|
||||
this.height = h.length();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Envelope[min = " + min + ", w = " + w + ", h = " + h + "]";
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWrap(WrapAxis axis, WrapMode mode) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWrap(WrapMode mode) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public WrapMode getWrap(WrapAxis axis) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType() {
|
||||
return Type.TwoDimensional;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Texture createSimpleClone() {
|
||||
return null;
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @param texco
|
||||
* texture 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
|
||||
* @param geometries
|
||||
* a list of geometries the UV coordinates will be applied to
|
||||
* @return created UV-coordinates buffer
|
||||
*/
|
||||
public static VertexBuffer generateUVCoordinates(int texco, int projection, int textureDimension, int[] coordinatesSwappingIndexes, List<Geometry> geometries) {
|
||||
if (textureDimension != 2 && textureDimension != 3) {
|
||||
throw new IllegalStateException("Unsupported texture dimension: " + textureDimension);
|
||||
private UVCoordinatesType(int blenderValue) {
|
||||
this.blenderValue = blenderValue;
|
||||
}
|
||||
|
||||
VertexBuffer result = new VertexBuffer(VertexBuffer.Type.TexCoord);
|
||||
Mesh mesh = geometries.get(0).getMesh();
|
||||
public static UVCoordinatesType valueOf(int blenderValue) {
|
||||
for (UVCoordinatesType coordinatesType : UVCoordinatesType.values()) {
|
||||
if (coordinatesType.blenderValue == blenderValue) {
|
||||
return coordinatesType;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a UV coordinates for 2D texture.
|
||||
*
|
||||
* @param mesh
|
||||
* the mesh we generate UV's for
|
||||
* @param texco
|
||||
* UV coordinates type
|
||||
* @param projection
|
||||
* projection type
|
||||
* @param geometries
|
||||
* the geometris the given mesh belongs to (required to compute
|
||||
* bounding box)
|
||||
* @return UV coordinates for the given mesh
|
||||
*/
|
||||
public static List<Vector2f> generateUVCoordinatesFor2DTexture(Mesh mesh, UVCoordinatesType texco, UVProjectionType projection, List<Geometry> geometries) {
|
||||
List<Vector2f> result = new ArrayList<Vector2f>();
|
||||
BoundingBox bb = UVCoordinatesGenerator.getBoundingBox(geometries);
|
||||
float[] inputData = null;// positions, normals, reflection vectors, etc.
|
||||
|
||||
switch (texco) {
|
||||
case TEXCO_ORCO:
|
||||
inputData = BufferUtils.getFloatArray(mesh.getFloatBuffer(VertexBuffer.Type.Position));
|
||||
break;
|
||||
case TEXCO_UV:// this should be used if not defined by user explicitly
|
||||
Vector2f[] data = new Vector2f[] { new Vector2f(0, 1), new Vector2f(0, 0), new Vector2f(1, 0) };
|
||||
for (int i = 0; i < mesh.getVertexCount(); ++i) {
|
||||
result.add(data[i % 3]);
|
||||
}
|
||||
break;
|
||||
case TEXCO_NORM:
|
||||
inputData = BufferUtils.getFloatArray(mesh.getFloatBuffer(VertexBuffer.Type.Normal));
|
||||
break;
|
||||
case TEXCO_REFL:
|
||||
case TEXCO_GLOB:
|
||||
case TEXCO_TANGENT:
|
||||
case TEXCO_STRESS:
|
||||
case TEXCO_LAVECTOR:
|
||||
case TEXCO_OBJECT:
|
||||
case TEXCO_OSA:
|
||||
case TEXCO_PARTICLE_OR_STRAND:
|
||||
case TEXCO_SPEED:
|
||||
case TEXCO_STICKY:
|
||||
case TEXCO_VIEW:
|
||||
case TEXCO_WINDOW:
|
||||
LOGGER.warning("Texture coordinates type not currently supported: " + texco);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("Unknown texture coordinates value: " + texco);
|
||||
}
|
||||
|
||||
if (inputData != null) {// make projection calculations
|
||||
switch (projection) {
|
||||
case PROJECTION_FLAT:
|
||||
inputData = UVProjectionGenerator.flatProjection(inputData, bb);
|
||||
break;
|
||||
case PROJECTION_CUBE:
|
||||
inputData = UVProjectionGenerator.cubeProjection(inputData, bb);
|
||||
break;
|
||||
case PROJECTION_TUBE:
|
||||
BoundingTube bt = UVCoordinatesGenerator.getBoundingTube(geometries);
|
||||
inputData = UVProjectionGenerator.tubeProjection(inputData, bt);
|
||||
break;
|
||||
case PROJECTION_SPHERE:
|
||||
BoundingSphere bs = UVCoordinatesGenerator.getBoundingSphere(geometries);
|
||||
inputData = UVProjectionGenerator.sphereProjection(inputData, bs);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("Unknown projection type: " + projection);
|
||||
}
|
||||
for (int i = 0; i < inputData.length; i += 2) {
|
||||
result.add(new Vector2f(inputData[i], inputData[i + 1]));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a UV coordinates for 3D texture.
|
||||
*
|
||||
* @param mesh
|
||||
* the mesh we generate UV's for
|
||||
* @param texco
|
||||
* UV coordinates type
|
||||
* @param coordinatesSwappingIndexes
|
||||
* coordinates swapping indexes
|
||||
* @param geometries
|
||||
* the geometris the given mesh belongs to (required to compute
|
||||
* bounding box)
|
||||
* @return UV coordinates for the given mesh
|
||||
*/
|
||||
public static List<Vector3f> generateUVCoordinatesFor3DTexture(Mesh mesh, UVCoordinatesType texco, int[] coordinatesSwappingIndexes, List<Geometry> geometries) {
|
||||
List<Vector3f> result = new ArrayList<Vector3f>();
|
||||
BoundingBox bb = UVCoordinatesGenerator.getBoundingBox(geometries);
|
||||
float[] inputData = null;// positions, normals, reflection vectors, etc.
|
||||
|
||||
@ -110,17 +190,11 @@ public class UVCoordinatesGenerator {
|
||||
inputData = BufferUtils.getFloatArray(mesh.getFloatBuffer(VertexBuffer.Type.Position));
|
||||
break;
|
||||
case TEXCO_UV:
|
||||
FloatBuffer uvCoordinatesBuffer = BufferUtils.createFloatBuffer(mesh.getVertexCount() * textureDimension);
|
||||
Vector2f[] data = new Vector2f[] { new Vector2f(0, 1), new Vector2f(0, 0), new Vector2f(1, 0) };
|
||||
for (int i = 0; i < mesh.getVertexCount(); ++i) {
|
||||
Vector2f uv = data[i % 3];
|
||||
uvCoordinatesBuffer.put(uv.x);
|
||||
uvCoordinatesBuffer.put(uv.y);
|
||||
if(textureDimension == 3) {
|
||||
uvCoordinatesBuffer.put(0);
|
||||
}
|
||||
result.add(new Vector3f(uv.x, uv.y, 0));
|
||||
}
|
||||
result.setupData(Usage.Static, textureDimension, Format.Float, uvCoordinatesBuffer);
|
||||
break;
|
||||
case TEXCO_NORM:
|
||||
inputData = BufferUtils.getFloatArray(mesh.getFloatBuffer(VertexBuffer.Type.Normal));
|
||||
@ -144,62 +218,34 @@ public class UVCoordinatesGenerator {
|
||||
}
|
||||
|
||||
if (inputData != null) {// make calculations
|
||||
if (textureDimension == 2) {
|
||||
switch (projection) {
|
||||
case PROJECTION_FLAT:
|
||||
inputData = UVProjectionGenerator.flatProjection(mesh, bb);
|
||||
break;
|
||||
case PROJECTION_CUBE:
|
||||
inputData = UVProjectionGenerator.cubeProjection(mesh, bb);
|
||||
break;
|
||||
case PROJECTION_TUBE:
|
||||
BoundingTube bt = UVCoordinatesGenerator.getBoundingTube(geometries);
|
||||
inputData = UVProjectionGenerator.tubeProjection(mesh, bt);
|
||||
break;
|
||||
case PROJECTION_SPHERE:
|
||||
BoundingSphere bs = UVCoordinatesGenerator.getBoundingSphere(geometries);
|
||||
inputData = UVProjectionGenerator.sphereProjection(mesh, bs);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("Unknown projection type: " + projection);
|
||||
}
|
||||
} else {
|
||||
Vector3f min = bb.getMin(null);
|
||||
float[] uvCoordsResults = new float[4];//used for coordinates swapping
|
||||
float[] ext = new float[] { bb.getXExtent() * 2, bb.getYExtent() * 2, bb.getZExtent() * 2 };
|
||||
|
||||
// now transform the coordinates so that they are in the range of <0; 1>
|
||||
for (int i = 0; i < inputData.length; i += 3) {
|
||||
uvCoordsResults[1] = (inputData[i] - min.x) / ext[0];
|
||||
uvCoordsResults[2] = (inputData[i + 1] - min.y) / ext[1];
|
||||
uvCoordsResults[3] = (inputData[i + 2] - min.z) / ext[2];
|
||||
|
||||
|
||||
inputData[i] = uvCoordsResults[coordinatesSwappingIndexes[0]];
|
||||
inputData[i + 1] = uvCoordsResults[coordinatesSwappingIndexes[1]];
|
||||
inputData[i + 2] = uvCoordsResults[coordinatesSwappingIndexes[2]];
|
||||
Vector3f min = bb.getMin(null);
|
||||
float[] uvCoordsResults = new float[4];// used for coordinates swapping
|
||||
float[] ext = new float[] { bb.getXExtent() * 2, bb.getYExtent() * 2, bb.getZExtent() * 2 };
|
||||
for (int i = 0; i < ext.length; ++i) {
|
||||
if (ext[i] == 0) {
|
||||
ext[i] = 1;
|
||||
}
|
||||
}
|
||||
result.setupData(Usage.Static, textureDimension, Format.Float, BufferUtils.createFloatBuffer(inputData));
|
||||
// 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]]));
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns the bounding box of the given geometries.
|
||||
*
|
||||
* @param geometries
|
||||
* the list of geometries
|
||||
* the list of geometries
|
||||
* @return bounding box of the given geometries
|
||||
*/
|
||||
/* package */static BoundingBox getBoundingBox(List<Geometry> geometries) {
|
||||
public static BoundingBox getBoundingBox(List<Geometry> geometries) {
|
||||
BoundingBox result = null;
|
||||
for (Geometry geometry : geometries) {
|
||||
BoundingBox bb = UVCoordinatesGenerator.getBoundingBox(geometry.getMesh());
|
||||
@ -214,8 +260,9 @@ public class UVCoordinatesGenerator {
|
||||
|
||||
/**
|
||||
* This method returns the bounding box of the given mesh.
|
||||
*
|
||||
* @param mesh
|
||||
* the mesh
|
||||
* the mesh
|
||||
* @return bounding box of the given mesh
|
||||
*/
|
||||
/* package */static BoundingBox getBoundingBox(Mesh mesh) {
|
||||
@ -234,8 +281,9 @@ public class UVCoordinatesGenerator {
|
||||
|
||||
/**
|
||||
* This method returns the bounding sphere of the given geometries.
|
||||
*
|
||||
* @param geometries
|
||||
* the list of geometries
|
||||
* the list of geometries
|
||||
* @return bounding sphere of the given geometries
|
||||
*/
|
||||
/* package */static BoundingSphere getBoundingSphere(List<Geometry> geometries) {
|
||||
@ -253,8 +301,9 @@ public class UVCoordinatesGenerator {
|
||||
|
||||
/**
|
||||
* This method returns the bounding sphere of the given mesh.
|
||||
*
|
||||
* @param mesh
|
||||
* the mesh
|
||||
* the mesh
|
||||
* @return bounding sphere of the given mesh
|
||||
*/
|
||||
/* package */static BoundingSphere getBoundingSphere(Mesh mesh) {
|
||||
@ -274,8 +323,9 @@ public class UVCoordinatesGenerator {
|
||||
|
||||
/**
|
||||
* This method returns the bounding tube of the given mesh.
|
||||
*
|
||||
* @param mesh
|
||||
* the mesh
|
||||
* the mesh
|
||||
* @return bounding tube of the given mesh
|
||||
*/
|
||||
/* package */static BoundingTube getBoundingTube(Mesh mesh) {
|
||||
@ -303,11 +353,12 @@ public class UVCoordinatesGenerator {
|
||||
float radius = Math.max(maxx - minx, maxy - miny) * 0.5f;
|
||||
return new BoundingTube(radius, maxz - minz, center);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This method returns the bounding tube of the given geometries.
|
||||
*
|
||||
* @param geometries
|
||||
* the list of geometries
|
||||
* the list of geometries
|
||||
* @return bounding tube of the given geometries
|
||||
*/
|
||||
/* package */static BoundingTube getBoundingTube(List<Geometry> geometries) {
|
||||
@ -324,9 +375,11 @@ public class UVCoordinatesGenerator {
|
||||
}
|
||||
|
||||
/**
|
||||
* A very simple bounding tube. Id holds only the basic data bout the bounding tube
|
||||
* and does not provide full functionality of a BoundingVolume.
|
||||
* Should be replaced with a bounding tube that extends the BoundingVolume if it is ever created.
|
||||
* A very simple bounding tube. Id holds only the basic data bout the
|
||||
* bounding tube and does not provide full functionality of a
|
||||
* BoundingVolume. Should be replaced with a bounding tube that extends the
|
||||
* BoundingVolume if it is ever created.
|
||||
*
|
||||
* @author Marcin Roguski (Kaelthas)
|
||||
*/
|
||||
/* package */static class BoundingTube {
|
||||
@ -336,12 +389,13 @@ public class UVCoordinatesGenerator {
|
||||
|
||||
/**
|
||||
* Constructor creates the tube with the given params.
|
||||
*
|
||||
* @param radius
|
||||
* the radius of the tube
|
||||
* the radius of the tube
|
||||
* @param height
|
||||
* the height of the tube
|
||||
* the height of the tube
|
||||
* @param center
|
||||
* the center of the tube
|
||||
* the center of the tube
|
||||
*/
|
||||
public BoundingTube(float radius, float height, Vector3f center) {
|
||||
this.radius = radius;
|
||||
@ -351,8 +405,9 @@ public class UVCoordinatesGenerator {
|
||||
|
||||
/**
|
||||
* This method merges two bounding tubes.
|
||||
*
|
||||
* @param boundingTube
|
||||
* bounding tube to be merged woth the current one
|
||||
* bounding tube to be merged woth the current one
|
||||
* @return new instance of bounding tube representing the tubes' merge
|
||||
*/
|
||||
public BoundingTube merge(BoundingTube boundingTube) {
|
||||
@ -375,14 +430,14 @@ public class UVCoordinatesGenerator {
|
||||
Vector3f center = tube1.center.add(distance.mult(0.5f));
|
||||
distance.z = 0;// projecting this vector on XY plane
|
||||
float d = distance.length();
|
||||
// d <= r1 - r2: tube2 is inside tube1 or touches tube1 from the inside
|
||||
// d <= r1 - r2: tube2 is inside tube1 or touches tube1 from the
|
||||
// inside
|
||||
// d > r1 - r2: tube2 is outside or touches tube1 or crosses tube1
|
||||
float radius = d <= r1 - r2 ? tube1.radius : (d + r1 + r2) * 0.5f;
|
||||
return new BoundingTube(radius, height, center);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns the radius of the tube.
|
||||
* @return the radius of the tube
|
||||
*/
|
||||
public float getRadius() {
|
||||
@ -390,7 +445,6 @@ public class UVCoordinatesGenerator {
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns the height of the tube.
|
||||
* @return the height of the tube
|
||||
*/
|
||||
public float getHeight() {
|
||||
@ -398,7 +452,6 @@ public class UVCoordinatesGenerator {
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns the center of the tube.
|
||||
* @return the center of the tube
|
||||
*/
|
||||
public Vector3f getCenter() {
|
||||
|
@ -5,10 +5,7 @@ import com.jme3.bounding.BoundingSphere;
|
||||
import com.jme3.math.FastMath;
|
||||
import com.jme3.math.Triangle;
|
||||
import com.jme3.math.Vector3f;
|
||||
import com.jme3.scene.Mesh;
|
||||
import com.jme3.scene.VertexBuffer;
|
||||
import com.jme3.scene.plugins.blender.textures.UVCoordinatesGenerator.BoundingTube;
|
||||
import java.nio.FloatBuffer;
|
||||
|
||||
/**
|
||||
* This class helps with projection calculations.
|
||||
@ -16,6 +13,32 @@ import java.nio.FloatBuffer;
|
||||
* @author Marcin Roguski (Kaelthas)
|
||||
*/
|
||||
/* package */class UVProjectionGenerator {
|
||||
/**
|
||||
* 2D texture mapping (projection)
|
||||
* @author Marcin Roguski (Kaelthas)
|
||||
*/
|
||||
public static enum UVProjectionType {
|
||||
PROJECTION_FLAT(0),
|
||||
PROJECTION_CUBE(1),
|
||||
PROJECTION_TUBE(2),
|
||||
PROJECTION_SPHERE(3);
|
||||
|
||||
public final int blenderValue;
|
||||
|
||||
private UVProjectionType(int blenderValue) {
|
||||
this.blenderValue = blenderValue;
|
||||
}
|
||||
|
||||
public static UVProjectionType valueOf(int blenderValue) {
|
||||
for(UVProjectionType projectionType : UVProjectionType.values()) {
|
||||
if(projectionType.blenderValue == blenderValue) {
|
||||
return projectionType;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Flat projection for 2D textures.
|
||||
*
|
||||
@ -25,18 +48,14 @@ import java.nio.FloatBuffer;
|
||||
* the bounding box for projecting
|
||||
* @return UV coordinates after the projection
|
||||
*/
|
||||
public static float[] flatProjection(Mesh mesh, BoundingBox bb) {
|
||||
if (bb == null) {
|
||||
bb = UVCoordinatesGenerator.getBoundingBox(mesh);
|
||||
}
|
||||
public static float[] flatProjection(float[] positions, BoundingBox bb) {
|
||||
Vector3f min = bb.getMin(null);
|
||||
float[] ext = new float[] { bb.getXExtent() * 2.0f, bb.getYExtent() * 2.0f };
|
||||
FloatBuffer positions = mesh.getFloatBuffer(VertexBuffer.Type.Position);
|
||||
float[] uvCoordinates = new float[positions.limit() / 3 * 2];
|
||||
for (int i = 0, j = 0; i < positions.limit(); i += 3, j += 2) {
|
||||
uvCoordinates[j] = (positions.get(i) - min.x) / ext[0];
|
||||
uvCoordinates[j + 1] = (positions.get(i + 1) - min.y) / ext[1];
|
||||
// skip the Z-coordinate
|
||||
float[] ext = new float[] { bb.getXExtent() * 2.0f, bb.getZExtent() * 2.0f };
|
||||
float[] uvCoordinates = new float[positions.length / 3 * 2];
|
||||
for (int i = 0, j = 0; i < positions.length; i += 3, j += 2) {
|
||||
uvCoordinates[j] = (positions[i] - min.x) / ext[0];
|
||||
// skip the Y-coordinate
|
||||
uvCoordinates[j + 1] = (positions[i + 2] - min.z) / ext[1];
|
||||
}
|
||||
return uvCoordinates;
|
||||
}
|
||||
@ -44,13 +63,13 @@ import java.nio.FloatBuffer;
|
||||
/**
|
||||
* Cube projection for 2D textures.
|
||||
*
|
||||
* @param mesh
|
||||
* mesh that is to be projected
|
||||
* @param positions
|
||||
* points to be projected
|
||||
* @param bb
|
||||
* the bounding box for projecting
|
||||
* @return UV coordinates after the projection
|
||||
*/
|
||||
public static float[] cubeProjection(Mesh mesh, BoundingBox bb) {
|
||||
public static float[] cubeProjection(float[] positions, BoundingBox bb) {
|
||||
Triangle triangle = new Triangle();
|
||||
Vector3f x = new Vector3f(1, 0, 0);
|
||||
Vector3f y = new Vector3f(0, 1, 0);
|
||||
@ -58,10 +77,12 @@ import java.nio.FloatBuffer;
|
||||
Vector3f min = bb.getMin(null);
|
||||
float[] ext = new float[] { bb.getXExtent() * 2.0f, bb.getYExtent() * 2.0f, bb.getZExtent() * 2.0f };
|
||||
|
||||
float[] uvCoordinates = new float[mesh.getTriangleCount() * 6];// 6 == 3 * 2
|
||||
float[] uvCoordinates = new float[positions.length/3*2];
|
||||
float borderAngle = (float) Math.sqrt(2.0f) / 2.0f;
|
||||
for (int i = 0, pointIndex = 0; i < mesh.getTriangleCount(); ++i) {
|
||||
mesh.getTriangle(i, triangle);
|
||||
for (int i = 0, pointIndex = 0; i < positions.length; i+=9) {
|
||||
triangle.set(0, positions[i], positions[i + 1], positions[i + 2]);
|
||||
triangle.set(1, positions[i + 3], positions[i + 4], positions[i + 5]);
|
||||
triangle.set(2, positions[i + 6], positions[i + 7], positions[i + 8]);
|
||||
Vector3f n = triangle.getNormal();
|
||||
float dotNX = Math.abs(n.dot(x));
|
||||
float dorNY = Math.abs(n.dot(y));
|
||||
@ -107,23 +128,22 @@ import java.nio.FloatBuffer;
|
||||
/**
|
||||
* Tube projection for 2D textures.
|
||||
*
|
||||
* @param mesh
|
||||
* mesh that is to be projected
|
||||
* @param positions
|
||||
* points to be projected
|
||||
* @param bt
|
||||
* the bounding tube for projecting
|
||||
* @return UV coordinates after the projection
|
||||
*/
|
||||
public static float[] tubeProjection(Mesh mesh, BoundingTube bt) {
|
||||
FloatBuffer positions = mesh.getFloatBuffer(VertexBuffer.Type.Position);
|
||||
float[] uvCoordinates = new float[positions.limit() / 3 * 2];
|
||||
public static float[] tubeProjection(float[] positions, BoundingTube bt) {
|
||||
float[] uvCoordinates = new float[positions.length / 3 * 2];
|
||||
Vector3f v = new Vector3f();
|
||||
float cx = bt.getCenter().x, cy = bt.getCenter().y;
|
||||
Vector3f uBase = new Vector3f(0, -1, 0);
|
||||
float cx = bt.getCenter().x, cz = bt.getCenter().z;
|
||||
Vector3f uBase = new Vector3f(0, 0, -1);
|
||||
|
||||
float vBase = bt.getCenter().z - bt.getHeight() * 0.5f;
|
||||
for (int i = 0, j = 0; i < positions.limit(); i += 3, j += 2) {
|
||||
float vBase = bt.getCenter().y - bt.getHeight() * 0.5f;
|
||||
for (int i = 0, j = 0; i < positions.length; i += 3, j += 2) {
|
||||
// calculating U
|
||||
v.set(positions.get(i)-cx, positions.get(i + 1)-cy, 0);
|
||||
v.set(positions[i]-cx, 0, positions[i + 2]-cz);
|
||||
v.normalizeLocal();
|
||||
float angle = v.angleBetween(uBase);// result between [0; PI]
|
||||
if (v.x < 0) {// the angle should be greater than PI, we're on the other part of the image then
|
||||
@ -132,31 +152,32 @@ import java.nio.FloatBuffer;
|
||||
uvCoordinates[j] = angle / FastMath.TWO_PI;
|
||||
|
||||
// calculating V
|
||||
float z = positions.get(i + 2);
|
||||
uvCoordinates[j + 1] = (z - vBase) / bt.getHeight();
|
||||
float y = positions[i + 1];
|
||||
uvCoordinates[j + 1] = (y - vBase) / bt.getHeight();
|
||||
}
|
||||
|
||||
//looking for splitted triangles
|
||||
Triangle triangle = new Triangle();
|
||||
for(int i=0;i<mesh.getTriangleCount();++i) {
|
||||
mesh.getTriangle(i, triangle);
|
||||
for (int i = 0; i < positions.length; i+=9) {
|
||||
triangle.set(0, positions[i], positions[i + 1], positions[i + 2]);
|
||||
triangle.set(1, positions[i + 3], positions[i + 4], positions[i + 5]);
|
||||
triangle.set(2, positions[i + 6], positions[i + 7], positions[i + 8]);
|
||||
float sgn1 = Math.signum(triangle.get1().x-cx);
|
||||
float sgn2 = Math.signum(triangle.get2().x-cx);
|
||||
float sgn3 = Math.signum(triangle.get3().x-cx);
|
||||
float xSideFactor = sgn1 + sgn2 + sgn3;
|
||||
float ySideFactor = Math.signum(triangle.get1().y-cy)+
|
||||
Math.signum(triangle.get2().y-cy)+
|
||||
Math.signum(triangle.get3().y-cy);
|
||||
float ySideFactor = Math.signum(triangle.get1().z-cz)+
|
||||
Math.signum(triangle.get2().z-cz)+
|
||||
Math.signum(triangle.get3().z-cz);
|
||||
if((xSideFactor>-3 || xSideFactor<3) && ySideFactor<0) {//the triangle is on the splitting plane
|
||||
//indexOfUcoord = (indexOfTriangle*3 + indexOfTrianglesVertex)*2
|
||||
if(sgn1==1.0f) {
|
||||
uvCoordinates[i*3*2] += 1.0f;
|
||||
uvCoordinates[i/3*2] += 1.0f;
|
||||
}
|
||||
if(sgn2==1.0f) {
|
||||
uvCoordinates[(i*3+1)*2] += 1.0f;
|
||||
uvCoordinates[(i/3+1)*2] += 1.0f;
|
||||
}
|
||||
if(sgn3==1.0f) {
|
||||
uvCoordinates[(i*3+2)*2] += 1.0f;
|
||||
uvCoordinates[(i/3+2)*2] += 1.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -166,23 +187,22 @@ import java.nio.FloatBuffer;
|
||||
/**
|
||||
* Sphere projection for 2D textures.
|
||||
*
|
||||
* @param mesh
|
||||
* mesh that is to be projected
|
||||
* @param positions
|
||||
* points to be projected
|
||||
* @param bb
|
||||
* the bounding box for projecting
|
||||
* @return UV coordinates after the projection
|
||||
*/
|
||||
public static float[] sphereProjection(Mesh mesh, BoundingSphere bs) {
|
||||
FloatBuffer positions = mesh.getFloatBuffer(VertexBuffer.Type.Position);
|
||||
float[] uvCoordinates = new float[positions.limit() / 3 * 2];
|
||||
public static float[] sphereProjection(float[] positions, BoundingSphere bs) {//TODO: rotate it to be vertical
|
||||
float[] uvCoordinates = new float[positions.length / 3 * 2];
|
||||
Vector3f v = new Vector3f();
|
||||
float cx = bs.getCenter().x, cy = bs.getCenter().y, cz = bs.getCenter().z;
|
||||
Vector3f uBase = new Vector3f(0, -1, 0);
|
||||
Vector3f vBase = new Vector3f(0, 0, -1);
|
||||
|
||||
for (int i = 0, j = 0; i < positions.limit(); i += 3, j += 2) {
|
||||
for (int i = 0, j = 0; i < positions.length; i += 3, j += 2) {
|
||||
// calculating U
|
||||
v.set(positions.get(i)-cx, positions.get(i + 1)-cy, 0);
|
||||
v.set(positions[i]-cx, positions[i + 1] - cy, 0);
|
||||
v.normalizeLocal();
|
||||
float angle = v.angleBetween(uBase);// result between [0; PI]
|
||||
if (v.x < 0) {// the angle should be greater than PI, we're on the other part of the image then
|
||||
@ -191,7 +211,7 @@ import java.nio.FloatBuffer;
|
||||
uvCoordinates[j] = angle / FastMath.TWO_PI;
|
||||
|
||||
// calculating V
|
||||
v.set(positions.get(i)-cx, positions.get(i + 1)-cy, positions.get(i + 2)-cz);
|
||||
v.set(positions[i]-cx, positions[i + 1]-cy, positions[i + 2]-cz);
|
||||
v.normalizeLocal();
|
||||
angle = v.angleBetween(vBase);// result between [0; PI]
|
||||
uvCoordinates[j+1] = angle / FastMath.PI;
|
||||
@ -199,8 +219,10 @@ import java.nio.FloatBuffer;
|
||||
|
||||
//looking for splitted triangles
|
||||
Triangle triangle = new Triangle();
|
||||
for(int i=0;i<mesh.getTriangleCount();++i) {
|
||||
mesh.getTriangle(i, triangle);
|
||||
for (int i = 0; i < positions.length; i+=9) {
|
||||
triangle.set(0, positions[i], positions[i + 1], positions[i + 2]);
|
||||
triangle.set(1, positions[i + 3], positions[i + 4], positions[i + 5]);
|
||||
triangle.set(2, positions[i + 6], positions[i + 7], positions[i + 8]);
|
||||
float sgn1 = Math.signum(triangle.get1().x-cx);
|
||||
float sgn2 = Math.signum(triangle.get2().x-cx);
|
||||
float sgn3 = Math.signum(triangle.get3().x-cx);
|
||||
@ -209,15 +231,14 @@ import java.nio.FloatBuffer;
|
||||
Math.signum(triangle.get2().y-cy)+
|
||||
Math.signum(triangle.get3().y-cy);
|
||||
if((xSideFactor>-3 || xSideFactor<3) && ySideFactor<0) {//the triangle is on the splitting plane
|
||||
//indexOfUcoord = (indexOfTriangle*3 + indexOfTrianglesVertex)*2
|
||||
if(sgn1==1.0f) {
|
||||
uvCoordinates[i*3*2] += 1.0f;
|
||||
uvCoordinates[i/3*2] += 1.0f;
|
||||
}
|
||||
if(sgn2==1.0f) {
|
||||
uvCoordinates[(i*3+1)*2] += 1.0f;
|
||||
uvCoordinates[(i/3+1)*2] += 1.0f;
|
||||
}
|
||||
if(sgn3==1.0f) {
|
||||
uvCoordinates[(i*3+2)*2] += 1.0f;
|
||||
uvCoordinates[(i/3+2)*2] += 1.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -2,14 +2,13 @@ package com.jme3.scene.plugins.blender.textures.blending;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import com.jme3.math.FastMath;
|
||||
import com.jme3.scene.plugins.blender.BlenderContext;
|
||||
import com.jme3.scene.plugins.blender.textures.TexturePixel;
|
||||
import com.jme3.scene.plugins.blender.textures.io.PixelIOFactory;
|
||||
import com.jme3.scene.plugins.blender.textures.io.PixelInputOutput;
|
||||
import com.jme3.texture.Image;
|
||||
import com.jme3.texture.Texture;
|
||||
import com.jme3.texture.Texture2D;
|
||||
import com.jme3.texture.Texture3D;
|
||||
import com.jme3.texture.Image.Format;
|
||||
import com.jme3.util.BufferUtils;
|
||||
|
||||
@ -36,127 +35,178 @@ import com.jme3.util.BufferUtils;
|
||||
* @author Marcin Roguski (Kaelthas)
|
||||
*/
|
||||
public class TextureBlenderAWT extends AbstractTextureBlender {
|
||||
private static final Logger LOGGER = Logger.getLogger(TextureBlenderAWT.class.getName());
|
||||
|
||||
public TextureBlenderAWT(int flag, boolean negateTexture, int blendType, float[] materialColor, float[] color, float blendFactor) {
|
||||
super(flag, negateTexture, blendType, materialColor, color, blendFactor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Texture blend(float[] materialColor, Texture texture, float[] color, float affectFactor, int blendType, boolean neg, BlenderContext blenderContext) {
|
||||
public Image blend(Image image, Image baseImage, BlenderContext blenderContext) {
|
||||
float[] pixelColor = new float[] { color[0], color[1], color[2], 1.0f };
|
||||
Format format = texture.getImage().getFormat();
|
||||
ByteBuffer data = texture.getImage().getData(0);
|
||||
data.rewind();
|
||||
|
||||
int width = texture.getImage().getWidth();
|
||||
int height = texture.getImage().getHeight();
|
||||
int depth = texture.getImage().getDepth();
|
||||
Format format = image.getFormat();
|
||||
ByteBuffer data = image.getData(0);
|
||||
|
||||
PixelInputOutput basePixelIO = null, pixelReader = PixelIOFactory.getPixelIO(format);
|
||||
TexturePixel basePixel = null, pixel = new TexturePixel();
|
||||
float[] materialColor = this.materialColor;
|
||||
if(baseImage != null) {
|
||||
basePixelIO = PixelIOFactory.getPixelIO(baseImage.getFormat());
|
||||
materialColor = new float[this.materialColor.length];
|
||||
basePixel = new TexturePixel();
|
||||
}
|
||||
|
||||
int width = image.getWidth();
|
||||
int height = image.getHeight();
|
||||
int depth = image.getDepth();
|
||||
if (depth == 0) {
|
||||
depth = 1;
|
||||
}
|
||||
ByteBuffer newData = BufferUtils.createByteBuffer(width * height * depth * 4);
|
||||
|
||||
|
||||
float[] resultPixel = new float[4];
|
||||
int dataIndex = 0;
|
||||
while (data.hasRemaining()) {
|
||||
this.setupMaterialColor(data, format, neg, pixelColor);
|
||||
this.blendPixel(resultPixel, materialColor, pixelColor, affectFactor, blendType, blenderContext);
|
||||
int dataIndex = 0, x = 0, y = 0, index = 0;
|
||||
while (index < data.limit()) {
|
||||
//getting the proper material color if the base texture is applied
|
||||
if(basePixelIO != null) {
|
||||
basePixelIO.read(baseImage, basePixel, x, y);
|
||||
basePixel.toRGBA(materialColor);
|
||||
}
|
||||
|
||||
//reading the current texture's pixel
|
||||
pixelReader.read(image, pixel, index);
|
||||
index += image.getFormat().getBitsPerPixel() >> 3;
|
||||
pixel.toRGBA(pixelColor);
|
||||
if (negateTexture) {
|
||||
pixel.negate();
|
||||
}
|
||||
|
||||
this.blendPixel(resultPixel, materialColor, pixelColor, blenderContext);
|
||||
newData.put(dataIndex++, (byte) (resultPixel[0] * 255.0f));
|
||||
newData.put(dataIndex++, (byte) (resultPixel[1] * 255.0f));
|
||||
newData.put(dataIndex++, (byte) (resultPixel[2] * 255.0f));
|
||||
newData.put(dataIndex++, (byte) (pixelColor[3] * 255.0f));
|
||||
|
||||
++x;
|
||||
if(x >= width) {
|
||||
x = 0;
|
||||
++y;
|
||||
}
|
||||
}
|
||||
if (texture.getType() == Texture.Type.TwoDimensional) {
|
||||
return new Texture2D(new Image(Format.RGBA8, width, height, newData));
|
||||
} else {
|
||||
if(depth > 1) {
|
||||
ArrayList<ByteBuffer> dataArray = new ArrayList<ByteBuffer>(1);
|
||||
dataArray.add(newData);
|
||||
return new Texture3D(new Image(Format.RGBA8, width, height, depth, dataArray));
|
||||
return new Image(Format.RGBA8, width, height, depth, dataArray);
|
||||
} else {
|
||||
return new Image(Format.RGBA8, width, height, newData);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method alters the material color in a way dependent on the type of
|
||||
* the image. For example the color remains untouched if the texture is of
|
||||
* Luminance type. The luminance defines the interaction between the
|
||||
* material color and color defined for texture blending. If the type has 3
|
||||
* or more color channels then the material color is replaced with the
|
||||
* texture's color and later blended with the defined blend color. All alpha
|
||||
* values (if present) are ignored and not used during blending.
|
||||
* This method blends the single pixel depending on the blending type.
|
||||
*
|
||||
* @param data
|
||||
* the image data
|
||||
* @param imageFormat
|
||||
* the format of the image
|
||||
* @param neg
|
||||
* defines it the result color should be nagated
|
||||
* @param result
|
||||
* the result pixel
|
||||
* @param materialColor
|
||||
* the material's color (value may be changed)
|
||||
* @return texture intensity for the current pixel
|
||||
* the material color
|
||||
* @param pixelColor
|
||||
* the pixel color
|
||||
* @param blendFactor
|
||||
* the blending factor
|
||||
* @param blendtype
|
||||
* the blending type
|
||||
* @param blenderContext
|
||||
* the blender context
|
||||
*/
|
||||
protected float setupMaterialColor(ByteBuffer data, Format imageFormat, boolean neg, float[] materialColor) {
|
||||
float tin = 0.0f;
|
||||
byte pixelValue = data.get();// at least one byte is always taken :)
|
||||
float firstPixelValue = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
|
||||
switch (imageFormat) {
|
||||
case RGBA8:
|
||||
materialColor[0] = firstPixelValue;
|
||||
pixelValue = data.get();
|
||||
materialColor[1] = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
|
||||
pixelValue = data.get();
|
||||
materialColor[2] = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
|
||||
pixelValue = data.get();
|
||||
materialColor[3] = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
|
||||
protected void blendPixel(float[] result, float[] materialColor, float[] pixelColor, BlenderContext blenderContext) {
|
||||
float blendFactor = this.blendFactor * pixelColor[3];
|
||||
float oneMinusFactor = 1.0f - blendFactor, col;
|
||||
|
||||
switch (blendType) {
|
||||
case MTEX_BLEND:
|
||||
result[0] = blendFactor * pixelColor[0] + oneMinusFactor * materialColor[0];
|
||||
result[1] = blendFactor * pixelColor[1] + oneMinusFactor * materialColor[1];
|
||||
result[2] = blendFactor * pixelColor[2] + oneMinusFactor * materialColor[2];
|
||||
break;
|
||||
case ABGR8:
|
||||
materialColor[3] = firstPixelValue;
|
||||
pixelValue = data.get();
|
||||
materialColor[2] = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
|
||||
pixelValue = data.get();
|
||||
materialColor[1] = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
|
||||
pixelValue = data.get();
|
||||
materialColor[0] = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
|
||||
case MTEX_MUL:
|
||||
result[0] = (oneMinusFactor + blendFactor * materialColor[0]) * pixelColor[0];
|
||||
result[1] = (oneMinusFactor + blendFactor * materialColor[1]) * pixelColor[1];
|
||||
result[2] = (oneMinusFactor + blendFactor * materialColor[2]) * pixelColor[2];
|
||||
break;
|
||||
case BGR8:
|
||||
materialColor[2] = firstPixelValue;
|
||||
pixelValue = data.get();
|
||||
materialColor[1] = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
|
||||
pixelValue = data.get();
|
||||
materialColor[0] = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
|
||||
materialColor[3] = 1.0f;
|
||||
case MTEX_DIV:
|
||||
if (pixelColor[0] != 0.0) {
|
||||
result[0] = (oneMinusFactor * materialColor[0] + blendFactor * materialColor[0] / pixelColor[0]) * 0.5f;
|
||||
}
|
||||
if (pixelColor[1] != 0.0) {
|
||||
result[1] = (oneMinusFactor * materialColor[1] + blendFactor * materialColor[1] / pixelColor[1]) * 0.5f;
|
||||
}
|
||||
if (pixelColor[2] != 0.0) {
|
||||
result[2] = (oneMinusFactor * materialColor[2] + blendFactor * materialColor[2] / pixelColor[2]) * 0.5f;
|
||||
}
|
||||
break;
|
||||
case RGB8:
|
||||
materialColor[0] = firstPixelValue;
|
||||
pixelValue = data.get();
|
||||
materialColor[1] = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
|
||||
pixelValue = data.get();
|
||||
materialColor[2] = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
|
||||
materialColor[3] = 1.0f;
|
||||
case MTEX_SCREEN:
|
||||
result[0] = 1.0f - (oneMinusFactor + blendFactor * (1.0f - materialColor[0])) * (1.0f - pixelColor[0]);
|
||||
result[1] = 1.0f - (oneMinusFactor + blendFactor * (1.0f - materialColor[1])) * (1.0f - pixelColor[1]);
|
||||
result[2] = 1.0f - (oneMinusFactor + blendFactor * (1.0f - materialColor[2])) * (1.0f - pixelColor[2]);
|
||||
break;
|
||||
case ARGB4444:
|
||||
case RGB10:
|
||||
case RGB111110F:
|
||||
case RGB16:
|
||||
case RGB16F:
|
||||
case RGB16F_to_RGB111110F:
|
||||
case RGB16F_to_RGB9E5:
|
||||
case RGB32F:
|
||||
case RGB565:
|
||||
case RGB5A1:
|
||||
case RGB9E5:
|
||||
case RGBA16:
|
||||
case RGBA16F:
|
||||
case RGBA32F:// TODO: implement these textures
|
||||
LOGGER.log(Level.WARNING, "Image type not yet supported for blending: {0}", imageFormat);
|
||||
case MTEX_OVERLAY:
|
||||
if (materialColor[0] < 0.5f) {
|
||||
result[0] = pixelColor[0] * (oneMinusFactor + 2.0f * blendFactor * materialColor[0]);
|
||||
} else {
|
||||
result[0] = 1.0f - (oneMinusFactor + 2.0f * blendFactor * (1.0f - materialColor[0])) * (1.0f - pixelColor[0]);
|
||||
}
|
||||
if (materialColor[1] < 0.5f) {
|
||||
result[1] = pixelColor[1] * (oneMinusFactor + 2.0f * blendFactor * materialColor[1]);
|
||||
} else {
|
||||
result[1] = 1.0f - (oneMinusFactor + 2.0f * blendFactor * (1.0f - materialColor[1])) * (1.0f - pixelColor[1]);
|
||||
}
|
||||
if (materialColor[2] < 0.5f) {
|
||||
result[2] = pixelColor[2] * (oneMinusFactor + 2.0f * blendFactor * materialColor[2]);
|
||||
} else {
|
||||
result[2] = 1.0f - (oneMinusFactor + 2.0f * blendFactor * (1.0f - materialColor[2])) * (1.0f - pixelColor[2]);
|
||||
}
|
||||
break;
|
||||
case MTEX_SUB:
|
||||
result[0] = materialColor[0] - blendFactor * pixelColor[0];
|
||||
result[1] = materialColor[1] - blendFactor * pixelColor[1];
|
||||
result[2] = materialColor[2] - blendFactor * pixelColor[2];
|
||||
result[0] = FastMath.clamp(result[0], 0.0f, 1.0f);
|
||||
result[1] = FastMath.clamp(result[1], 0.0f, 1.0f);
|
||||
result[2] = FastMath.clamp(result[2], 0.0f, 1.0f);
|
||||
break;
|
||||
case MTEX_ADD:
|
||||
result[0] = (blendFactor * pixelColor[0] + materialColor[0]) * 0.5f;
|
||||
result[1] = (blendFactor * pixelColor[1] + materialColor[1]) * 0.5f;
|
||||
result[2] = (blendFactor * pixelColor[2] + materialColor[2]) * 0.5f;
|
||||
break;
|
||||
case MTEX_DIFF:
|
||||
result[0] = oneMinusFactor * materialColor[0] + blendFactor * Math.abs(materialColor[0] - pixelColor[0]);
|
||||
result[1] = oneMinusFactor * materialColor[1] + blendFactor * Math.abs(materialColor[1] - pixelColor[1]);
|
||||
result[2] = oneMinusFactor * materialColor[2] + blendFactor * Math.abs(materialColor[2] - pixelColor[2]);
|
||||
break;
|
||||
case MTEX_DARK:
|
||||
col = blendFactor * pixelColor[0];
|
||||
result[0] = col < materialColor[0] ? col : materialColor[0];
|
||||
col = blendFactor * pixelColor[1];
|
||||
result[1] = col < materialColor[1] ? col : materialColor[1];
|
||||
col = blendFactor * pixelColor[2];
|
||||
result[2] = col < materialColor[2] ? col : materialColor[2];
|
||||
break;
|
||||
case MTEX_LIGHT:
|
||||
col = blendFactor * pixelColor[0];
|
||||
result[0] = col > materialColor[0] ? col : materialColor[0];
|
||||
col = blendFactor * pixelColor[1];
|
||||
result[1] = col > materialColor[1] ? col : materialColor[1];
|
||||
col = blendFactor * pixelColor[2];
|
||||
result[2] = col > materialColor[2] ? col : materialColor[2];
|
||||
break;
|
||||
case MTEX_BLEND_HUE:
|
||||
case MTEX_BLEND_SAT:
|
||||
case MTEX_BLEND_VAL:
|
||||
case MTEX_BLEND_COLOR:
|
||||
System.arraycopy(materialColor, 0, result, 0, 3);
|
||||
this.blendHSV(blendType, result, blendFactor, pixelColor, blenderContext);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("Invalid image format type for AWT texture blender: " + imageFormat);
|
||||
throw new IllegalStateException("Unknown blend type: " + blendType);
|
||||
}
|
||||
if (neg) {
|
||||
materialColor[0] = 1.0f - materialColor[0];
|
||||
materialColor[1] = 1.0f - materialColor[1];
|
||||
materialColor[2] = 1.0f - materialColor[2];
|
||||
}
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
@ -11,9 +11,6 @@ import com.jme3.scene.plugins.blender.BlenderContext;
|
||||
import com.jme3.scene.plugins.blender.textures.TexturePixel;
|
||||
import com.jme3.texture.Image;
|
||||
import com.jme3.texture.Image.Format;
|
||||
import com.jme3.texture.Texture;
|
||||
import com.jme3.texture.Texture2D;
|
||||
import com.jme3.texture.Texture3D;
|
||||
import com.jme3.util.BufferUtils;
|
||||
|
||||
/**
|
||||
@ -25,18 +22,23 @@ import com.jme3.util.BufferUtils;
|
||||
* <li> DXT1A:
|
||||
* @author Marcin Roguski (Kaelthas)
|
||||
*/
|
||||
public class TextureBlenderDDS extends AbstractTextureBlender {
|
||||
public class TextureBlenderDDS extends TextureBlenderAWT {
|
||||
private static final Logger LOGGER = Logger.getLogger(TextureBlenderDDS.class.getName());
|
||||
|
||||
public TextureBlenderDDS(int flag, boolean negateTexture, int blendType, float[] materialColor, float[] color, float blendFactor) {
|
||||
super(flag, negateTexture, blendType, materialColor, color, blendFactor);
|
||||
}
|
||||
|
||||
//TODO: implement using base texture
|
||||
@Override
|
||||
public Texture blend(float[] materialColor, Texture texture, float[] color, float affectFactor, int blendType, boolean neg, BlenderContext blenderContext) {
|
||||
Format format = texture.getImage().getFormat();
|
||||
ByteBuffer data = texture.getImage().getData(0);
|
||||
public Image blend(Image image, Image baseImage, BlenderContext blenderContext) {
|
||||
Format format = image.getFormat();
|
||||
ByteBuffer data = image.getData(0);
|
||||
data.rewind();
|
||||
|
||||
int width = texture.getImage().getWidth();
|
||||
int height = texture.getImage().getHeight();
|
||||
int depth = texture.getImage().getDepth();
|
||||
int width = image.getWidth();
|
||||
int height = image.getHeight();
|
||||
int depth = image.getDepth();
|
||||
if (depth == 0) {
|
||||
depth = 1;
|
||||
}
|
||||
@ -50,9 +52,7 @@ public class TextureBlenderDDS extends AbstractTextureBlender {
|
||||
switch (format) {
|
||||
case DXT3:
|
||||
case DXT5:
|
||||
newData.putLong(dataIndex, data.getLong());// just copy the
|
||||
// 8 bytes of
|
||||
// alphas
|
||||
newData.putLong(dataIndex, data.getLong());// just copy the 8 bytes of alphas
|
||||
dataIndex += 8;
|
||||
case DXT1:
|
||||
int col0 = RGB565.RGB565_to_ARGB8(data.getShort());
|
||||
@ -69,11 +69,11 @@ public class TextureBlenderDDS extends AbstractTextureBlender {
|
||||
|
||||
// blending colors
|
||||
for (int i = 0; i < colors.length; ++i) {
|
||||
if (neg) {
|
||||
if (negateTexture) {
|
||||
colors[i].negate();
|
||||
}
|
||||
colors[i].toRGBA(pixelColor);
|
||||
this.blendPixel(resultPixel, materialColor, pixelColor, affectFactor, blendType, blenderContext);
|
||||
this.blendPixel(resultPixel, materialColor, pixelColor, blenderContext);
|
||||
colors[i].fromARGB8(1, resultPixel[0], resultPixel[1], resultPixel[2]);
|
||||
int argb8 = colors[i].toARGB8();
|
||||
short rgb565 = RGB565.ARGB8_to_RGB565(argb8);
|
||||
@ -85,12 +85,13 @@ public class TextureBlenderDDS extends AbstractTextureBlender {
|
||||
newData.putInt(dataIndex, data.getInt());
|
||||
dataIndex += 4;
|
||||
}
|
||||
if (texture.getType() == Texture.Type.TwoDimensional) {
|
||||
return new Texture2D(new Image(format, width, height, newData));
|
||||
} else {
|
||||
|
||||
if(depth > 1) {
|
||||
ArrayList<ByteBuffer> dataArray = new ArrayList<ByteBuffer>(1);
|
||||
dataArray.add(newData);
|
||||
return new Texture3D(new Image(format, width, height, depth, dataArray));
|
||||
return new Image(format, width, height, depth, dataArray);
|
||||
} else {
|
||||
return new Image(format, width, height, newData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -7,10 +7,10 @@ import java.util.logging.Logger;
|
||||
|
||||
import com.jme3.math.FastMath;
|
||||
import com.jme3.scene.plugins.blender.BlenderContext;
|
||||
import com.jme3.scene.plugins.blender.textures.TexturePixel;
|
||||
import com.jme3.scene.plugins.blender.textures.io.PixelIOFactory;
|
||||
import com.jme3.scene.plugins.blender.textures.io.PixelInputOutput;
|
||||
import com.jme3.texture.Image;
|
||||
import com.jme3.texture.Texture;
|
||||
import com.jme3.texture.Texture2D;
|
||||
import com.jme3.texture.Texture3D;
|
||||
import com.jme3.texture.Image.Format;
|
||||
import com.jme3.util.BufferUtils;
|
||||
|
||||
@ -29,15 +29,28 @@ import com.jme3.util.BufferUtils;
|
||||
public class TextureBlenderLuminance extends AbstractTextureBlender {
|
||||
private static final Logger LOGGER = Logger.getLogger(TextureBlenderLuminance.class.getName());
|
||||
|
||||
public TextureBlenderLuminance(int flag, boolean negateTexture, int blendType, float[] materialColor, float[] color, float blendFactor) {
|
||||
super(flag, negateTexture, blendType, materialColor, color, blendFactor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Texture blend(float[] materialColor, Texture texture, float[] color, float affectFactor, int blendType, boolean neg, BlenderContext blenderContext) {
|
||||
Format format = texture.getImage().getFormat();
|
||||
ByteBuffer data = texture.getImage().getData(0);
|
||||
public Image blend(Image image, Image baseImage, BlenderContext blenderContext) {
|
||||
Format format = image.getFormat();
|
||||
ByteBuffer data = image.getData(0);
|
||||
data.rewind();
|
||||
|
||||
int width = texture.getImage().getWidth();
|
||||
int height = texture.getImage().getHeight();
|
||||
int depth = texture.getImage().getDepth();
|
||||
PixelInputOutput basePixelIO = null;
|
||||
TexturePixel basePixel = null;
|
||||
float[] materialColor = this.materialColor;
|
||||
if(baseImage != null) {
|
||||
basePixelIO = PixelIOFactory.getPixelIO(baseImage.getFormat());
|
||||
materialColor = new float[this.materialColor.length];
|
||||
basePixel = new TexturePixel();
|
||||
}
|
||||
|
||||
int width = image.getWidth();
|
||||
int height = image.getHeight();
|
||||
int depth = image.getDepth();
|
||||
if (depth == 0) {
|
||||
depth = 1;
|
||||
}
|
||||
@ -45,21 +58,34 @@ public class TextureBlenderLuminance extends AbstractTextureBlender {
|
||||
|
||||
float[] resultPixel = new float[4];
|
||||
float[] tinAndAlpha = new float[2];
|
||||
int dataIndex = 0;
|
||||
int dataIndex = 0, x = 0, y = 0;
|
||||
while (data.hasRemaining()) {
|
||||
this.getTinAndAlpha(data, format, neg, tinAndAlpha);
|
||||
this.blendPixel(resultPixel, materialColor, color, tinAndAlpha[0], affectFactor, blendType, blenderContext);
|
||||
//getting the proper material color if the base texture is applied
|
||||
if(basePixelIO != null) {
|
||||
basePixelIO.read(baseImage, basePixel, x, y);
|
||||
basePixel.toRGBA(materialColor);
|
||||
}
|
||||
|
||||
this.getTinAndAlpha(data, format, negateTexture, tinAndAlpha);
|
||||
this.blendPixel(resultPixel, materialColor, color, tinAndAlpha[0], blendFactor, blendType, blenderContext);
|
||||
newData.put(dataIndex++, (byte) (resultPixel[0] * 255.0f));
|
||||
newData.put(dataIndex++, (byte) (resultPixel[1] * 255.0f));
|
||||
newData.put(dataIndex++, (byte) (resultPixel[2] * 255.0f));
|
||||
newData.put(dataIndex++, (byte) (tinAndAlpha[1] * 255.0f));
|
||||
|
||||
++x;
|
||||
if(x >= width) {
|
||||
x = 0;
|
||||
++y;
|
||||
}
|
||||
}
|
||||
if (texture.getType() == Texture.Type.TwoDimensional) {
|
||||
return new Texture2D(new Image(Format.RGBA8, width, height, newData));
|
||||
} else {
|
||||
|
||||
if(depth > 1) {
|
||||
ArrayList<ByteBuffer> dataArray = new ArrayList<ByteBuffer>(1);
|
||||
dataArray.add(newData);
|
||||
return new Texture3D(new Image(Format.RGBA8, width, height, depth, dataArray));
|
||||
return new Image(Format.RGBA8, width, height, depth, dataArray);
|
||||
} else {
|
||||
return new Image(Format.RGBA8, width, height, newData);
|
||||
}
|
||||
}
|
||||
|
||||
@ -77,7 +103,7 @@ public class TextureBlenderLuminance extends AbstractTextureBlender {
|
||||
*/
|
||||
protected void getTinAndAlpha(ByteBuffer data, Format imageFormat, boolean neg, float[] result) {
|
||||
byte pixelValue = data.get();// at least one byte is always taken
|
||||
float firstPixelValue = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
|
||||
float firstPixelValue = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - ~pixelValue / 255.0f;
|
||||
switch (imageFormat) {
|
||||
case Luminance8:
|
||||
result[0] = neg ? 1.0f - firstPixelValue : firstPixelValue;
|
||||
@ -86,7 +112,7 @@ public class TextureBlenderLuminance extends AbstractTextureBlender {
|
||||
case Luminance8Alpha8:
|
||||
result[0] = neg ? 1.0f - firstPixelValue : firstPixelValue;
|
||||
pixelValue = data.get();
|
||||
result[1] = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
|
||||
result[1] = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - ~pixelValue / 255.0f;
|
||||
break;
|
||||
case Luminance16:
|
||||
case Luminance16Alpha16:
|
||||
@ -96,7 +122,7 @@ public class TextureBlenderLuminance extends AbstractTextureBlender {
|
||||
LOGGER.log(Level.WARNING, "Image type not yet supported for blending: {0}", imageFormat);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("Invalid image format type for DDS texture blender: " + imageFormat);
|
||||
throw new IllegalStateException("Invalid image format type for Luminance texture blender: " + imageFormat);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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) {
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
@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;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ArrayList<ByteBuffer> dataArray = new ArrayList<ByteBuffer>(1);
|
||||
dataArray.add(BufferUtils.createByteBuffer(data));
|
||||
return new Texture3D(new Image(format, width, height, depth, dataArray));
|
||||
super(noiseGenerator, Format.Luminance8);
|
||||
}
|
||||
|
||||
protected int stype;
|
||||
|
||||
@Override
|
||||
public void readData(Structure tex, BlenderContext blenderContext) {
|
||||
super.readData(tex, blenderContext);
|
||||
stype = ((Number) tex.getFieldValue("stype")).intValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getPixel(TexturePixel pixel, float x, float y, float z) {
|
||||
pixel.intensity = INTENSITY_FUNCTION[stype].getIntensity(x, y, z);
|
||||
|
||||
if (colorBand != null) {
|
||||
int colorbandIndex = (int) (pixel.intensity * 1000.0f);
|
||||
pixel.red = colorBand[colorbandIndex][0];
|
||||
pixel.green = colorBand[colorbandIndex][1];
|
||||
pixel.blue = colorBand[colorbandIndex][2];
|
||||
|
||||
this.applyBrightnessAndContrast(bacd, pixel);
|
||||
} else {
|
||||
this.applyBrightnessAndContrast(pixel, bacd.contrast, bacd.brightness);
|
||||
}
|
||||
}
|
||||
|
||||
private static interface IntensityFunction {
|
||||
float getIntensity(float x, float y, float z);
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
@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];
|
||||
|
||||
this.applyBrightnessAndContrast(bacd, texres);
|
||||
data[index++] = (byte) (texres.red * 255.0f);
|
||||
data[index++] = (byte) (texres.green * 255.0f);
|
||||
data[index++] = (byte) (texres.blue * 255.0f);
|
||||
data[index++] = (byte) (colorBand[colorbandIndex][3] * 255.0f);
|
||||
} else {
|
||||
this.applyBrightnessAndContrast(texres, bacd.contrast, bacd.brightness);
|
||||
data[index++] = (byte) (texres.intensity * 255.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ArrayList<ByteBuffer> dataArray = new ArrayList<ByteBuffer>(1);
|
||||
dataArray.add(BufferUtils.createByteBuffer(data));
|
||||
return new Texture3D(new Image(format, width, height, depth, dataArray));
|
||||
super(noiseGenerator, Format.Luminance8);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readData(Structure tex, BlenderContext blenderContext) {
|
||||
super.readData(tex, blenderContext);
|
||||
noisesize = ((Number) tex.getFieldValue("noisesize")).floatValue();
|
||||
distAmount = ((Number) tex.getFieldValue("dist_amount")).floatValue();
|
||||
noisebasis = ((Number) tex.getFieldValue("noisebasis")).intValue();
|
||||
noisebasis2 = ((Number) tex.getFieldValue("noisebasis2")).intValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getPixel(TexturePixel pixel, float x, float y, float z) {
|
||||
pixel.intensity = this.musgraveVariableLunacrityNoise(x * 4, y * 4, z * 4, distAmount, noisebasis, noisebasis2);
|
||||
pixel.intensity = FastMath.clamp(pixel.intensity, 0.0f, 1.0f);
|
||||
if (colorBand != null) {
|
||||
int colorbandIndex = (int) (pixel.intensity * 1000.0f);
|
||||
pixel.red = colorBand[colorbandIndex][0];
|
||||
pixel.green = colorBand[colorbandIndex][1];
|
||||
pixel.blue = colorBand[colorbandIndex][2];
|
||||
|
||||
this.applyBrightnessAndContrast(bacd, pixel);
|
||||
} else {
|
||||
this.applyBrightnessAndContrast(pixel, bacd.contrast, bacd.brightness);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* "Variable Lacunarity Noise" A distorted variety of Perlin noise. This method is used to calculate distorted noise
|
||||
* texture.
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
public void readData(Structure tex, BlenderContext blenderContext) {
|
||||
super.readData(tex, blenderContext);
|
||||
noisedepth = ((Number) tex.getFieldValue("noisedepth")).intValue();
|
||||
turbul = ((Number) tex.getFieldValue("turbul")).floatValue() / 5.0f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getPixel(TexturePixel pixel, float x, float y, float z) {
|
||||
float turb = turbul;
|
||||
xyz[0] = (float) Math.sin((x + y + z) * 5.0f);
|
||||
xyz[1] = (float) Math.cos((-x + y - z) * 5.0f);
|
||||
xyz[2] = -(float) Math.cos((-x - y + z) * 5.0f);
|
||||
|
||||
if (colorBand != null) {
|
||||
texres.intensity = FastMath.clamp(0.3333f * (xyz[0] + xyz[1] + xyz[2]), 0.0f, 1.0f);
|
||||
int colorbandIndex = (int) (texres.intensity * 1000.0f);
|
||||
texres.red = colorBand[colorbandIndex][0];
|
||||
texres.green = colorBand[colorbandIndex][1];
|
||||
texres.blue = colorBand[colorbandIndex][2];
|
||||
texres.alpha = colorBand[colorbandIndex][3];
|
||||
} else {
|
||||
if (noisedepth > 0) {
|
||||
xyz[0] *= turb;
|
||||
xyz[1] *= turb;
|
||||
xyz[2] *= turb;
|
||||
for (int m=0;m<noisedepth;++m) {
|
||||
noiseDepthFunctions[m].compute(xyz, turb);
|
||||
}
|
||||
}
|
||||
|
||||
if (turb != 0.0f) {
|
||||
turb *= 2.0f;
|
||||
xyz[0] /= turb;
|
||||
xyz[1] /= turb;
|
||||
xyz[2] /= turb;
|
||||
}
|
||||
texres.red = 0.5f - xyz[0];
|
||||
texres.green = 0.5f - xyz[1];
|
||||
texres.blue = 0.5f - xyz[2];
|
||||
texres.alpha = 1.0f;
|
||||
}
|
||||
this.applyBrightnessAndContrast(bacd, texres);
|
||||
data[index++] = (byte) (texres.red * 255.0f);
|
||||
data[index++] = (byte) (texres.green * 255.0f);
|
||||
data[index++] = (byte) (texres.blue * 255.0f);
|
||||
data[index++] = (byte) (texres.alpha * 255.0f);
|
||||
if (colorBand != null) {
|
||||
pixel.intensity = FastMath.clamp(0.3333f * (xyz[0] + xyz[1] + xyz[2]), 0.0f, 1.0f);
|
||||
int colorbandIndex = (int) (pixel.intensity * 1000.0f);
|
||||
pixel.red = colorBand[colorbandIndex][0];
|
||||
pixel.green = colorBand[colorbandIndex][1];
|
||||
pixel.blue = colorBand[colorbandIndex][2];
|
||||
pixel.alpha = colorBand[colorbandIndex][3];
|
||||
} else {
|
||||
if (noisedepth > 0) {
|
||||
xyz[0] *= turb;
|
||||
xyz[1] *= turb;
|
||||
xyz[2] *= turb;
|
||||
for (int m=0;m<noisedepth;++m) {
|
||||
noiseDepthFunctions[m].compute(xyz, turb);
|
||||
}
|
||||
}
|
||||
|
||||
if (turb != 0.0f) {
|
||||
turb *= 2.0f;
|
||||
xyz[0] /= turb;
|
||||
xyz[1] /= turb;
|
||||
xyz[2] /= turb;
|
||||
}
|
||||
pixel.red = 0.5f - xyz[0];
|
||||
pixel.green = 0.5f - xyz[1];
|
||||
pixel.blue = 0.5f - xyz[2];
|
||||
pixel.alpha = 1.0f;
|
||||
}
|
||||
ArrayList<ByteBuffer> dataArray = new ArrayList<ByteBuffer>(1);
|
||||
dataArray.add(BufferUtils.createByteBuffer(data));
|
||||
return new Texture3D(new Image(Format.RGBA8, width, height, depth, dataArray));
|
||||
this.applyBrightnessAndContrast(bacd, pixel);
|
||||
}
|
||||
|
||||
private static interface NoiseDepthFunction {
|
@ -29,17 +29,11 @@
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package com.jme3.scene.plugins.blender.textures;
|
||||
package com.jme3.scene.plugins.blender.textures.generating;
|
||||
|
||||
import com.jme3.scene.plugins.blender.BlenderContext;
|
||||
import com.jme3.scene.plugins.blender.file.Structure;
|
||||
import com.jme3.texture.Image;
|
||||
import com.jme3.texture.Image.Format;
|
||||
import com.jme3.texture.Texture;
|
||||
import com.jme3.texture.Texture3D;
|
||||
import com.jme3.util.BufferUtils;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import com.jme3.scene.plugins.blender.textures.TexturePixel;
|
||||
|
||||
/**
|
||||
* This class generates the 'marble' texture.
|
||||
@ -51,6 +45,8 @@ public class TextureGeneratorMarble extends TextureGeneratorWood {
|
||||
protected static final int TEX_SHARP = 1;
|
||||
protected static final int TEX_SHARPER = 2;
|
||||
|
||||
protected MarbleData marbleData;
|
||||
|
||||
/**
|
||||
* Constructor stores the given noise generator.
|
||||
* @param noiseGenerator
|
||||
@ -59,50 +55,29 @@ public class TextureGeneratorMarble extends TextureGeneratorWood {
|
||||
public TextureGeneratorMarble(NoiseGenerator noiseGenerator) {
|
||||
super(noiseGenerator);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected Texture generate(Structure tex, int width, int height, int depth, BlenderContext blenderContext) {
|
||||
float[] texvec = new float[] { 0, 0, 0 };
|
||||
TexturePixel texres = new TexturePixel();
|
||||
int halfW = width >> 1, halfH = height >> 1, halfD = depth >> 1, index = 0;
|
||||
float wDelta = 1.0f / halfW, hDelta = 1.0f / halfH, dDelta = 1.0f / halfD;
|
||||
float[][] colorBand = this.computeColorband(tex, blenderContext);
|
||||
Format format = colorBand != null ? Format.RGBA8 : Format.Luminance8;
|
||||
int bytesPerPixel = colorBand != null ? 4 : 1;
|
||||
BrightnessAndContrastData bacd = new BrightnessAndContrastData(tex);
|
||||
MarbleData marbleData = new MarbleData(tex);
|
||||
|
||||
byte[] data = new byte[width * height * depth * bytesPerPixel];
|
||||
for (int i = -halfW; i < halfW; ++i) {
|
||||
texvec[0] = wDelta * i;
|
||||
for (int j = -halfH; j < halfH; ++j) {
|
||||
texvec[1] = hDelta * j;
|
||||
for (int k = -halfD; k < halfD; ++k) {
|
||||
texvec[2] = dDelta * k;
|
||||
texres.intensity = this.marbleInt(marbleData, texvec[0], texvec[1], texvec[2]);
|
||||
if (colorBand != null) {
|
||||
int colorbandIndex = (int) (texres.intensity * 1000.0f);
|
||||
texres.red = colorBand[colorbandIndex][0];
|
||||
texres.green = colorBand[colorbandIndex][1];
|
||||
texres.blue = colorBand[colorbandIndex][2];
|
||||
|
||||
this.applyBrightnessAndContrast(bacd, texres);
|
||||
data[index++] = (byte) (texres.red * 255.0f);
|
||||
data[index++] = (byte) (texres.green * 255.0f);
|
||||
data[index++] = (byte) (texres.blue * 255.0f);
|
||||
data[index++] = (byte) (colorBand[colorbandIndex][3] * 255.0f);
|
||||
} else {
|
||||
this.applyBrightnessAndContrast(texres, bacd.contrast, bacd.brightness);
|
||||
data[index++] = (byte) (texres.intensity * 255.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ArrayList<ByteBuffer> dataArray = new ArrayList<ByteBuffer>(1);
|
||||
dataArray.add(BufferUtils.createByteBuffer(data));
|
||||
return new Texture3D(new Image(format, width, height, depth, dataArray));
|
||||
public void readData(Structure tex, BlenderContext blenderContext) {
|
||||
super.readData(tex, blenderContext);
|
||||
marbleData = new MarbleData(tex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getPixel(TexturePixel pixel, float x, float y, float z) {
|
||||
pixel.intensity = this.marbleInt(marbleData, x, y, z);
|
||||
if (colorBand != null) {
|
||||
int colorbandIndex = (int) (pixel.intensity * 1000.0f);
|
||||
pixel.red = colorBand[colorbandIndex][0];
|
||||
pixel.green = colorBand[colorbandIndex][1];
|
||||
pixel.blue = colorBand[colorbandIndex][2];
|
||||
|
||||
this.applyBrightnessAndContrast(bacd, pixel);
|
||||
pixel.alpha = colorBand[colorbandIndex][3];
|
||||
} else {
|
||||
this.applyBrightnessAndContrast(pixel, bacd.contrast, bacd.brightness);
|
||||
}
|
||||
}
|
||||
|
||||
public float marbleInt(MarbleData marbleData, float x, float y, float z) {
|
||||
int waveform;
|
||||
if (marbleData.waveform > TEX_TRI || marbleData.waveform < TEX_SIN) {
|
@ -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);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
if (colorBand != null) {
|
||||
int colorbandIndex = (int) (pixel.intensity * 1000.0f);
|
||||
pixel.red = colorBand[colorbandIndex][0];
|
||||
pixel.green = colorBand[colorbandIndex][1];
|
||||
pixel.blue = colorBand[colorbandIndex][2];
|
||||
|
||||
this.applyBrightnessAndContrast(pixel, bacd.contrast, bacd.brightness);
|
||||
pixel.alpha = colorBand[colorbandIndex][3];
|
||||
} else {
|
||||
this.applyBrightnessAndContrast(bacd, pixel);
|
||||
}
|
||||
ArrayList<ByteBuffer> dataArray = new ArrayList<ByteBuffer>(1);
|
||||
dataArray.add(BufferUtils.createByteBuffer(data));
|
||||
return new Texture3D(new Image(format, width, height, depth, dataArray));
|
||||
}
|
||||
|
||||
protected static class MusgraveData {
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
int index = 0;
|
||||
byte[] data = new byte[width * height * depth * bytesPerPixel];
|
||||
for (int i = -halfW; i < halfW; ++i) {
|
||||
texvec[0] = wDelta * i;
|
||||
for (int j = -halfH; j < halfH; ++j) {
|
||||
texvec[1] = hDelta * j;
|
||||
for(int k = -halfD; k < halfD; ++k) {
|
||||
texvec[2] = dDelta * k;
|
||||
texres.intensity = this.woodIntensity(woodIntensityData, texvec[0], texvec[1], texvec[2]);
|
||||
|
||||
if (colorBand != null) {
|
||||
int colorbandIndex = (int) (texres.intensity * 1000.0f);
|
||||
texres.red = colorBand[colorbandIndex][0];
|
||||
texres.green = colorBand[colorbandIndex][1];
|
||||
texres.blue = colorBand[colorbandIndex][2];
|
||||
|
||||
this.applyBrightnessAndContrast(bacd, texres);
|
||||
|
||||
data[index++] = (byte) (texres.red * 255.0f);
|
||||
data[index++] = (byte) (texres.green * 255.0f);
|
||||
data[index++] = (byte) (texres.blue * 255.0f);
|
||||
data[index++] = (byte) (colorBand[colorbandIndex][3] * 255.0f);
|
||||
} else {
|
||||
this.applyBrightnessAndContrast(texres, bacd.contrast, bacd.brightness);
|
||||
data[index++] = (byte) (texres.intensity * 255.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (colorBand != null) {
|
||||
int colorbandIndex = (int) (pixel.intensity * 1000.0f);
|
||||
pixel.red = colorBand[colorbandIndex][0];
|
||||
pixel.green = colorBand[colorbandIndex][1];
|
||||
pixel.blue = colorBand[colorbandIndex][2];
|
||||
|
||||
this.applyBrightnessAndContrast(bacd, pixel);
|
||||
pixel.alpha = colorBand[colorbandIndex][3];
|
||||
} else {
|
||||
this.applyBrightnessAndContrast(pixel, bacd.contrast, bacd.brightness);
|
||||
}
|
||||
|
||||
ArrayList<ByteBuffer> dataArray = new ArrayList<ByteBuffer>(1);
|
||||
dataArray.add(BufferUtils.createByteBuffer(data));
|
||||
return new Texture3D(new Image(format, width, height, depth, dataArray));
|
||||
}
|
||||
|
||||
protected static WaveForm[] waveformFunctions = new WaveForm[3];
|
@ -0,0 +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);
|
||||
}
|
||||
}
|
@ -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!");
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
package com.jme3.scene.plugins.blender.textures.io;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.jme3.texture.Image.Format;
|
||||
|
||||
/**
|
||||
* This class creates a pixel IO object for the specified image format.
|
||||
*
|
||||
* @author Marcin Roguski (Kaelthas)
|
||||
*/
|
||||
public class PixelIOFactory {
|
||||
private static final Map<Format, PixelInputOutput> PIXEL_INPUT_OUTPUT = new HashMap<Format, PixelInputOutput>();
|
||||
|
||||
/**
|
||||
* This method returns pixel IO object for the specified format.
|
||||
*
|
||||
* @param format
|
||||
* the format of the image
|
||||
* @return pixel IO object
|
||||
*/
|
||||
public static PixelInputOutput getPixelIO(Format format) {
|
||||
PixelInputOutput result = PIXEL_INPUT_OUTPUT.get(format);
|
||||
if (result == null) {
|
||||
switch (format) {
|
||||
case ABGR8:
|
||||
case RGBA8:
|
||||
case BGR8:
|
||||
result = new AWTPixelInputOutput();
|
||||
break;
|
||||
case Luminance8:
|
||||
result = new LuminancePixelInputOutput();
|
||||
break;
|
||||
case DXT1:
|
||||
case DXT1A:
|
||||
case DXT3:
|
||||
case DXT5:
|
||||
result = new DDSPixelInputOutput();
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("Unsupported image format for IO operations: " + format);
|
||||
}
|
||||
synchronized (PIXEL_INPUT_OUTPUT) {
|
||||
PIXEL_INPUT_OUTPUT.put(format, result);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user