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-0572b91ccdca3.0
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; |
||||
} |
@ -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); |
||||
} |
||||
} |
@ -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; |
||||
} |
||||
} |
@ -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(); |
||||
} |
||||
} |
||||
} |
@ -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); |
||||
} |
||||
} |
||||
} |
@ -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); |
||||
} |
||||
} |
||||
} |
@ -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); |
||||
} |
||||
} |
||||
} |
@ -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…
Reference in new issue