Better support for textures blending.

- support for DDS blending (without textures decompression :) )
- support for RGBA textures blending
- support for generated textures blending
Also done blending refactoring. Blending functions moved to separated classes from TextureHelper.
It will increase code redability.

git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@9188 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
3.0
Kae..pl 13 years ago
parent b12285b8a4
commit 2b004e803d
  1. 11
      engine/src/blender/com/jme3/scene/plugins/blender/materials/MaterialContext.java
  2. 304
      engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureDecompressor.java
  3. 384
      engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureHelper.java
  4. 43
      engine/src/blender/com/jme3/scene/plugins/blender/textures/TexturePixel.java
  5. 195
      engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/AbstractTextureBlender.java
  6. 51
      engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlender.java
  7. 162
      engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderAWT.java
  8. 96
      engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderDDS.java
  9. 81
      engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderFactory.java
  10. 221
      engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderLuminance.java

@ -9,6 +9,8 @@ import com.jme3.scene.plugins.blender.file.Structure;
import com.jme3.scene.plugins.blender.materials.MaterialHelper.DiffuseShader;
import com.jme3.scene.plugins.blender.materials.MaterialHelper.SpecularShader;
import com.jme3.scene.plugins.blender.textures.TextureHelper;
import com.jme3.scene.plugins.blender.textures.blending.TextureBlender;
import com.jme3.scene.plugins.blender.textures.blending.TextureBlenderFactory;
import com.jme3.texture.Texture;
import com.jme3.texture.Texture.Type;
import com.jme3.texture.Texture.WrapMode;
@ -140,9 +142,6 @@ public final class MaterialContext {
Map<Number, List<Structure[]>> sortedTextures = this.sortAndFilterTextures();
loadedTextures = new HashMap<Number, Texture>(sortedTextures.size());
textureToMTexMap = new HashMap<Texture, Structure>();
if(sortedTextures.size() > 0) {//texutre covers the material color
diffuseColor.set(1, 1, 1, 1);
}
float[] diffuseColorArray = new float[] {diffuseColor.r, diffuseColor.g, diffuseColor.b, diffuseColor.a};
TextureHelper textureHelper = blenderContext.getHelper(TextureHelper.class);
for(Entry<Number, List<Structure[]>> entry : sortedTextures.entrySet()) {
@ -157,7 +156,8 @@ public final class MaterialContext {
((Number) mtexAndTex[0].getFieldValue("g")).floatValue(),
((Number) mtexAndTex[0].getFieldValue("b")).floatValue() };
float colfac = ((Number) mtexAndTex[0].getFieldValue("colfac")).floatValue();
texture = textureHelper.blendTexture(diffuseColorArray, texture, color, colfac, blendType, negateTexture, blenderContext);
TextureBlender textureBlender = TextureBlenderFactory.createTextureBlender(texture.getImage().getFormat());
texture = textureBlender.blend(diffuseColorArray, texture, color, colfac, blendType, negateTexture, blenderContext);
texture.setWrap(WrapMode.Repeat);
textures.add(texture);
textureToMTexMap.put(texture, mtexAndTex[0]);
@ -175,6 +175,9 @@ public final class MaterialContext {
boolean transparent = false;
if(diffuseColor != null) {
transparent = diffuseColor.a < 1.0f;
if(sortedTextures.size() > 0) {//texutre covers the material color
diffuseColor.set(1, 1, 1, 1);
}
}
if(specularColor != null) {
transparent = transparent || specularColor.a < 1.0f;

@ -1,304 +0,0 @@
package com.jme3.scene.plugins.blender.textures;
import java.nio.ByteBuffer;
import java.util.logging.Logger;
import jme3tools.converters.RGB565;
import com.jme3.math.FastMath;
import com.jme3.texture.Image;
import com.jme3.texture.Image.Format;
import com.jme3.util.BufferUtils;
/**
* This class decompresses the given image (if necessary) to the RGBA8 format.
* Currently supported compressed textures are: DXT1, DXT3, DXT5.
* @author Marcin Roguski (Kaelthas)
*/
/*package*/ class TextureDecompressor {
private static final Logger LOGGER = Logger.getLogger(TextureDecompressor.class.getName());
/**
* This method decompresses the given image. If the given image is already
* decompressed nothing happens and it is simply returned.
*
* @param image
* the image to decompress
* @return the decompressed image
*/
public static Image decompress(Image image) {//TODO: support 3D textures
byte[] bytes = null;
TexturePixel[] colors = null;
ByteBuffer data = image.getData(0);
data.rewind();
Format format = image.getFormat();
DDSTexelData texelData = new DDSTexelData(data.remaining() / (format.getBitsPerPixel() * 2), image.getWidth(), image.getHeight(), format != Format.DXT1);
switch (format) {// TODO: DXT1A
case DXT1:// BC1
bytes = new byte[image.getWidth() * image.getHeight() * 4];
colors = new TexturePixel[] { new TexturePixel(), new TexturePixel(), new TexturePixel(), new TexturePixel() };
while (data.hasRemaining()) {
short c0 = data.getShort();
short c1 = data.getShort();
int col0 = RGB565.RGB565_to_ARGB8(c0);
int col1 = RGB565.RGB565_to_ARGB8(c1);
colors[0].fromARGB8(col0);
colors[1].fromARGB8(col1);
if (col0 > col1) {
// creating color2 = 2/3color0 + 1/3color1
colors[2].fromPixel(colors[0]);
colors[2].mult(2);
colors[2].add(colors[1]);
colors[2].divide(3);
// creating color3 = 1/3color0 + 2/3color1;
colors[3].fromPixel(colors[1]);
colors[3].mult(2);
colors[3].add(colors[0]);
colors[3].divide(3);
} else {
// creating color2 = 1/2color0 + 1/2color1
colors[2].fromPixel(colors[0]);
colors[2].add(colors[1]);
colors[2].mult(0.5f);
colors[3].fromARGB8(0);
}
int indexes = data.getInt();// 4-byte table with color indexes in decompressed table
texelData.add(colors, indexes);
}
break;
case DXT3:// BC2
bytes = new byte[image.getWidth() * image.getHeight() * 4];
colors = new TexturePixel[] { new TexturePixel(), new TexturePixel(), new TexturePixel(), new TexturePixel() };
while (data.hasRemaining()) {
long alpha = data.getLong();
float[] alphas = new float[16];
long alphasIndex = 0;
for (int i = 0; i < 16; ++i) {
alphasIndex |= i << (i * 4);
byte a = (byte) (((alpha >> (i * 4)) & 0x0F) << 4);
alphas[i] = a >= 0 ? a / 255.0f : 1.0f - (~a) / 255.0f;
}
short c0 = data.getShort();
short c1 = data.getShort();
int col0 = RGB565.RGB565_to_ARGB8(c0);
int col1 = RGB565.RGB565_to_ARGB8(c1);
colors[0].fromARGB8(col0);
colors[1].fromARGB8(col1);
// creating color2 = 2/3color0 + 1/3color1
colors[2].fromPixel(colors[0]);
colors[2].mult(2);
colors[2].add(colors[1]);
colors[2].divide(3);
// creating color3 = 1/3color0 + 2/3color1;
colors[3].fromPixel(colors[1]);
colors[3].mult(2);
colors[3].add(colors[0]);
colors[3].divide(3);
int indexes = data.getInt();// 4-byte table with color indexes in decompressed table
texelData.add(colors, indexes, alphas, alphasIndex);
}
break;
case DXT5:// BC3
bytes = new byte[image.getWidth() * image.getHeight() * 4];
colors = new TexturePixel[] { new TexturePixel(), new TexturePixel(), new TexturePixel(), new TexturePixel() };
float[] alphas = new float[8];
while (data.hasRemaining()) {
alphas[0] = data.get() * 255.0f;
alphas[1] = data.get() * 255.0f;
long alphaIndices = (int) data.get() | ((int) data.get() << 8) | ((int) data.get() << 16) | ((int) data.get() << 24) | ((int) data.get() << 32) | ((int) data.get() << 40);
if (alphas[0] > alphas[1]) {// 6 interpolated alpha values.
alphas[2] = (6 * alphas[0] + alphas[1]) / 7;
alphas[3] = (5 * alphas[0] + 2 * alphas[1]) / 7;
alphas[4] = (4 * alphas[0] + 3 * alphas[1]) / 7;
alphas[5] = (3 * alphas[0] + 4 * alphas[1]) / 7;
alphas[6] = (2 * alphas[0] + 5 * alphas[1]) / 7;
alphas[7] = (alphas[0] + 6 * alphas[1]) / 7;
} else {
alphas[2] = (4 * alphas[0] + alphas[1]) * 0.2f;
alphas[3] = (3 * alphas[0] + 2 * alphas[1]) * 0.2f;
alphas[4] = (2 * alphas[0] + 3 * alphas[1]) * 0.2f;
alphas[5] = (alphas[0] + 4 * alphas[1]) * 0.2f;
alphas[6] = 0;
alphas[7] = 1;
}
short c0 = data.getShort();
short c1 = data.getShort();
int col0 = RGB565.RGB565_to_ARGB8(c0);
int col1 = RGB565.RGB565_to_ARGB8(c1);
colors[0].fromARGB8(col0);
colors[1].fromARGB8(col1);
// creating color2 = 2/3color0 + 1/3color1
colors[2].fromPixel(colors[0]);
colors[2].mult(2);
colors[2].add(colors[1]);
colors[2].divide(3);
// creating color3 = 1/3color0 + 2/3color1;
colors[3].fromPixel(colors[1]);
colors[3].mult(2);
colors[3].add(colors[0]);
colors[3].divide(3);
int indexes = data.getInt();// 4-byte table with color
// indexes in decompressed table
texelData.add(colors, indexes, alphas, alphaIndices);
}
break;
default:
LOGGER.fine("Unsupported decompression format.");
}
if (bytes != null) {// writing the data to the result table
byte[] pixelBytes = new byte[4];
for (int i = 0; i < image.getWidth(); ++i) {
for (int j = 0; j < image.getHeight(); ++j) {
texelData.getRGBA8(i, j, pixelBytes);
bytes[(j * image.getWidth() + i) * 4] = pixelBytes[0];
bytes[(j * image.getWidth() + i) * 4 + 1] = pixelBytes[1];
bytes[(j * image.getWidth() + i) * 4 + 2] = pixelBytes[2];
bytes[(j * image.getWidth() + i) * 4 + 3] = pixelBytes[3];
}
}
return new Image(Format.RGBA8, image.getWidth(), image.getHeight(), BufferUtils.createByteBuffer(bytes));
}
return image;
}
/**
* The data that helps in bytes calculations for the result image.
*
* @author Marcin Roguski (Kaelthas)
*/
private static 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);
}
}
}

@ -51,7 +51,6 @@ import com.jme3.asset.BlenderKey.FeaturesToLoad;
import com.jme3.asset.GeneratedTextureKey;
import com.jme3.asset.TextureKey;
import com.jme3.math.ColorRGBA;
import com.jme3.math.FastMath;
import com.jme3.math.Vector3f;
import com.jme3.scene.plugins.blender.AbstractBlenderHelper;
import com.jme3.scene.plugins.blender.BlenderContext;
@ -61,7 +60,6 @@ import com.jme3.scene.plugins.blender.file.FileBlockHeader;
import com.jme3.scene.plugins.blender.file.Pointer;
import com.jme3.scene.plugins.blender.file.Structure;
import com.jme3.scene.plugins.blender.materials.MaterialContext;
import com.jme3.scene.plugins.blender.materials.MaterialHelper;
import com.jme3.texture.Image;
import com.jme3.texture.Image.Format;
import com.jme3.texture.Texture;
@ -115,23 +113,6 @@ public class TextureHelper extends AbstractBlenderHelper {
public static final int MAP_WARP = 8192;
public static final int MAP_LAYER = 16384;
// blendtypes
public static final int MTEX_BLEND = 0;
public static final int MTEX_MUL = 1;
public static final int MTEX_ADD = 2;
public static final int MTEX_SUB = 3;
public static final int MTEX_DIV = 4;
public static final int MTEX_DARK = 5;
public static final int MTEX_DIFF = 6;
public static final int MTEX_LIGHT = 7;
public static final int MTEX_SCREEN = 8;
public static final int MTEX_OVERLAY = 9;
public static final int MTEX_BLEND_HUE = 10;
public static final int MTEX_BLEND_SAT = 11;
public static final int MTEX_BLEND_VAL = 12;
public static final int MTEX_BLEND_COLOR = 13;
public static final int MTEX_NUM_BLENDTYPES = 14;
protected NoiseGenerator noiseGenerator;
private Map<Integer, TextureGenerator> textureGenerators = new HashMap<Integer, TextureGenerator>();
@ -229,60 +210,6 @@ public class TextureHelper extends AbstractBlenderHelper {
return result;
}
/**
* This method blends the given texture with material color and the defined color in 'map to' panel. As a result of this method a new
* texture is created. The input texture is NOT.
*
* @param materialColor
* the material diffuse color
* @param texture
* the texture we use in blending
* @param color
* the color defined for the texture
* @param affectFactor
* the factor that the color affects the texture (value form 0.0 to 1.0)
* @param blendType
* the blending type
* @param blenderContext
* the blender context
* @return new texture that was created after the blending
*/
public Texture blendTexture(float[] materialColor, Texture texture, float[] color, float affectFactor, int blendType, boolean neg, BlenderContext blenderContext) {
float[] materialColorClone = materialColor.clone();//this array may change, so we copy it
Format format = texture.getImage().getFormat();
ByteBuffer data = texture.getImage().getData(0);
Image decompressedImage = TextureDecompressor.decompress(texture.getImage());
data = decompressedImage.getData(0);
format = decompressedImage.getFormat();
int width = texture.getImage().getWidth();
int height = texture.getImage().getHeight();
int depth = texture.getImage().getDepth();
if(depth==0) {
depth = 1;
}
ByteBuffer newData = BufferUtils.createByteBuffer(width * height * depth * 4);
float[] resultPixel = new float[4];
int dataIndex = 0;
while (data.hasRemaining()) {
float tin = this.setupMaterialColor(data, format, neg, materialColorClone);
this.blendPixel(resultPixel, materialColorClone, color, tin, affectFactor, blendType, blenderContext);
newData.put(dataIndex++, (byte) (resultPixel[0] * 255.0f));
newData.put(dataIndex++, (byte) (resultPixel[1] * 255.0f));
newData.put(dataIndex++, (byte) (resultPixel[2] * 255.0f));
newData.put(dataIndex++, (byte) (materialColorClone[3] * 255.0f));
}
if(texture.getType()==Texture.Type.TwoDimensional) {
return new Texture2D(new Image(Format.RGBA8, width, height, newData));
} else {
ArrayList<ByteBuffer> dataArray = new ArrayList<ByteBuffer>(1);
dataArray.add(newData);
return new Texture3D(new Image(Format.RGBA8, width, height, depth, dataArray));
}
}
/**
* This method merges the given textures. The result texture has no alpha
* factor (is always opaque).
@ -296,6 +223,9 @@ public class TextureHelper extends AbstractBlenderHelper {
public Texture mergeTextures(List<Texture> sources, MaterialContext materialContext) {
Texture result = null;
if(sources!=null && sources.size()>0) {
if(sources.size() == 1) {
return sources.get(0);//just return the texture
}
//checking the sizes of the textures (tehy should perfectly match)
int lastTextureWithoutAlphaIndex = 0;
int width = sources.get(0).getImage().getWidth();
@ -361,314 +291,6 @@ public class TextureHelper extends AbstractBlenderHelper {
return result;
}
/**
* This method alters the material color in a way dependent on the type of the image.
* For example the color remains untouched if the texture is of Luminance type.
* The luminance defines the interaction between the material color and color defined
* for texture blending.
* If the type has 3 or more color channels then the material color is replaced with the texture's
* color and later blended with the defined blend color.
* All alpha values (if present) are ignored and not used during blending.
* @param data
* the image data
* @param imageFormat
* the format of the image
* @param neg
* defines it the result color should be nagated
* @param materialColor
* the material's color (value may be changed)
* @return texture intensity for the current pixel
*/
protected float setupMaterialColor(ByteBuffer data, Format imageFormat, boolean neg, float[] materialColor) {
float tin = 0.0f;
byte pixelValue = data.get();// at least one byte is always taken :)
float firstPixelValue = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
switch (imageFormat) {
case Luminance8:
tin = neg ? 1.0f - firstPixelValue : firstPixelValue;
materialColor[3] = tin;
neg = false;//do not negate the materialColor, it must be unchanged
break;
case RGBA8:
materialColor[0] = firstPixelValue;
pixelValue = data.get();
materialColor[1] = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
pixelValue = data.get();
materialColor[2] = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
pixelValue = data.get();
materialColor[3] = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
break;
case ABGR8:
materialColor[3] = firstPixelValue;
pixelValue = data.get();
materialColor[2] = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
pixelValue = data.get();
materialColor[1] = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
pixelValue = data.get();
materialColor[0] = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
break;
case BGR8:
materialColor[2] = firstPixelValue;
pixelValue = data.get();
materialColor[1] = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
pixelValue = data.get();
materialColor[0] = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
materialColor[3] = 1.0f;
break;
case RGB8:
materialColor[0] = firstPixelValue;
pixelValue = data.get();
materialColor[1] = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
pixelValue = data.get();
materialColor[2] = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
materialColor[3] = 1.0f;
break;
case Luminance8Alpha8:
tin = neg ? 1.0f - firstPixelValue : firstPixelValue;
neg = false;//do not negate the materialColor, it must be unchanged
pixelValue = data.get(); // ignore alpha
materialColor[3] = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
break;
case DXT1:
break;
case DXT1A:
case DXT3:
case DXT5:
break;
case Luminance16:
case Luminance16Alpha16:
case Alpha16:
case Alpha8:
case ARGB4444:
case Depth:
case Depth16:
case Depth24:
case Depth32:
case Depth32F:
case Intensity16:
case Intensity8:
case LATC:
case LTC:
case Luminance16F:
case Luminance16FAlpha16F:
case Luminance32F:
case RGB10:
case RGB111110F:
case RGB16:
case RGB16F:
case RGB16F_to_RGB111110F:
case RGB16F_to_RGB9E5:
case RGB32F:
case RGB565:
case RGB5A1:
case RGB9E5:
case RGBA16:
case RGBA16F:
case RGBA32F:
LOGGER.log(Level.WARNING, "Image type not yet supported for blending: {0}", imageFormat);
break;
default:
throw new IllegalStateException("Unknown image format type: " + imageFormat);
}
if (neg) {
materialColor[0] = 1.0f - materialColor[0];
materialColor[1] = 1.0f - materialColor[1];
materialColor[2] = 1.0f - materialColor[2];
}
return tin;
}
/**
* This method blends the texture with an appropriate color.
*
* @param result
* the result color (variable 'in' in blender source code)
* @param materialColor
* the texture color (variable 'out' in blender source coude)
* @param color
* the previous color (variable 'tex' in blender source code)
* @param textureIntensity
* texture intensity (variable 'fact' in blender source code)
* @param textureFactor
* texture affection factor (variable 'facg' in blender source code)
* @param blendtype
* the blend type
* @param blenderContext
* the blender context
*/
protected void blendPixel(float[] result, float[] materialColor, float[] color, float textureIntensity, float textureFactor, int blendtype, BlenderContext blenderContext) {
float oneMinusFactor, col;
textureIntensity *= textureFactor;
switch (blendtype) {
case MTEX_BLEND:
oneMinusFactor = 1.0f - textureIntensity;
result[0] = textureIntensity * color[0] + oneMinusFactor * materialColor[0];
result[1] = textureIntensity * color[1] + oneMinusFactor * materialColor[1];
result[2] = textureIntensity * color[2] + oneMinusFactor * materialColor[2];
break;
case MTEX_MUL:
oneMinusFactor = 1.0f - textureFactor;
result[0] = (oneMinusFactor + textureIntensity * materialColor[0]) * color[0];
result[1] = (oneMinusFactor + textureIntensity * materialColor[1]) * color[1];
result[2] = (oneMinusFactor + textureIntensity * materialColor[2]) * color[2];
break;
case MTEX_DIV:
oneMinusFactor = 1.0f - textureIntensity;
if (color[0] != 0.0) {
result[0] = (oneMinusFactor * materialColor[0] + textureIntensity * materialColor[0] / color[0]) * 0.5f;
}
if (color[1] != 0.0) {
result[1] = (oneMinusFactor * materialColor[1] + textureIntensity * materialColor[1] / color[1]) * 0.5f;
}
if (color[2] != 0.0) {
result[2] = (oneMinusFactor * materialColor[2] + textureIntensity * materialColor[2] / color[2]) * 0.5f;
}
break;
case MTEX_SCREEN:
oneMinusFactor = 1.0f - textureFactor;
result[0] = 1.0f - (oneMinusFactor + textureIntensity * (1.0f - materialColor[0])) * (1.0f - color[0]);
result[1] = 1.0f - (oneMinusFactor + textureIntensity * (1.0f - materialColor[1])) * (1.0f - color[1]);
result[2] = 1.0f - (oneMinusFactor + textureIntensity * (1.0f - materialColor[2])) * (1.0f - color[2]);
break;
case MTEX_OVERLAY:
oneMinusFactor = 1.0f - textureFactor;
if (materialColor[0] < 0.5f) {
result[0] = color[0] * (oneMinusFactor + 2.0f * textureIntensity * materialColor[0]);
} else {
result[0] = 1.0f - (oneMinusFactor + 2.0f * textureIntensity * (1.0f - materialColor[0])) * (1.0f - color[0]);
}
if (materialColor[1] < 0.5f) {
result[1] = color[1] * (oneMinusFactor + 2.0f * textureIntensity * materialColor[1]);
} else {
result[1] = 1.0f - (oneMinusFactor + 2.0f * textureIntensity * (1.0f - materialColor[1])) * (1.0f - color[1]);
}
if (materialColor[2] < 0.5f) {
result[2] = color[2] * (oneMinusFactor + 2.0f * textureIntensity * materialColor[2]);
} else {
result[2] = 1.0f - (oneMinusFactor + 2.0f * textureIntensity * (1.0f - materialColor[2])) * (1.0f - color[2]);
}
break;
case MTEX_SUB:
result[0] = materialColor[0] - textureIntensity * color[0];
result[1] = materialColor[1] - textureIntensity * color[1];
result[2] = materialColor[2] - textureIntensity * color[2];
result[0] = FastMath.clamp(result[0], 0.0f, 1.0f);
result[1] = FastMath.clamp(result[1], 0.0f, 1.0f);
result[2] = FastMath.clamp(result[2], 0.0f, 1.0f);
break;
case MTEX_ADD:
result[0] = (textureIntensity * color[0] + materialColor[0]) * 0.5f;
result[1] = (textureIntensity * color[1] + materialColor[1]) * 0.5f;
result[2] = (textureIntensity * color[2] + materialColor[2]) * 0.5f;
break;
case MTEX_DIFF:
oneMinusFactor = 1.0f - textureIntensity;
result[0] = oneMinusFactor * materialColor[0] + textureIntensity * Math.abs(materialColor[0] - color[0]);
result[1] = oneMinusFactor * materialColor[1] + textureIntensity * Math.abs(materialColor[1] - color[1]);
result[2] = oneMinusFactor * materialColor[2] + textureIntensity * Math.abs(materialColor[2] - color[2]);
break;
case MTEX_DARK:
col = textureIntensity * color[0];
result[0] = col < materialColor[0] ? col : materialColor[0];
col = textureIntensity * color[1];
result[1] = col < materialColor[1] ? col : materialColor[1];
col = textureIntensity * color[2];
result[2] = col < materialColor[2] ? col : materialColor[2];
break;
case MTEX_LIGHT:
col = textureIntensity * color[0];
result[0] = col > materialColor[0] ? col : materialColor[0];
col = textureIntensity * color[1];
result[1] = col > materialColor[1] ? col : materialColor[1];
col = textureIntensity * color[2];
result[2] = col > materialColor[2] ? col : materialColor[2];
break;
case MTEX_BLEND_HUE:
case MTEX_BLEND_SAT:
case MTEX_BLEND_VAL:
case MTEX_BLEND_COLOR:
System.arraycopy(materialColor, 0, result, 0, 3);
this.rampBlend(blendtype, result, textureIntensity, color, blenderContext);
break;
default:
throw new IllegalStateException("Unknown blend type: " + blendtype);
}
}
/**
* The method that performs the ramp blending.
*
* @param type
* the blend type
* @param rgb
* the rgb value where the result is stored
* @param fac
* color affection factor
* @param col
* the texture color
* @param blenderContext
* the blender context
*/
protected void rampBlend(int type, float[] rgb, float fac, float[] col, BlenderContext blenderContext) {
float oneMinusFactor = 1.0f - fac;
MaterialHelper materialHelper = blenderContext.getHelper(MaterialHelper.class);
if (rgb.length >= 3) {
switch (type) {
case MTEX_BLEND_HUE: {
float[] colorTransformResult = new float[3];
materialHelper.rgbToHsv(col[0], col[1], col[2], colorTransformResult);
if (colorTransformResult[1] != 0.0f) {
float colH = colorTransformResult[0];
materialHelper.rgbToHsv(rgb[0], rgb[1], rgb[2], colorTransformResult);
materialHelper.hsvToRgb(colH, colorTransformResult[1], colorTransformResult[2], colorTransformResult);
rgb[0] = oneMinusFactor * rgb[0] + fac * colorTransformResult[0];
rgb[1] = oneMinusFactor * rgb[1] + fac * colorTransformResult[1];
rgb[2] = oneMinusFactor * rgb[2] + fac * colorTransformResult[2];
}
break;
}
case MTEX_BLEND_SAT: {
float[] colorTransformResult = new float[3];
materialHelper.rgbToHsv(rgb[0], rgb[1], rgb[2], colorTransformResult);
float h = colorTransformResult[0];
float s = colorTransformResult[1];
float v = colorTransformResult[2];
if (s != 0.0f) {
materialHelper.rgbToHsv(col[0], col[1], col[2], colorTransformResult);
materialHelper.hsvToRgb(h, (oneMinusFactor * s + fac * colorTransformResult[1]), v, rgb);
}
break;
}
case MTEX_BLEND_VAL: {
float[] rgbToHsv = new float[3];
float[] colToHsv = new float[3];
materialHelper.rgbToHsv(rgb[0], rgb[1], rgb[2], rgbToHsv);
materialHelper.rgbToHsv(col[0], col[1], col[2], colToHsv);
materialHelper.hsvToRgb(rgbToHsv[0], rgbToHsv[1], (oneMinusFactor * rgbToHsv[2] + fac * colToHsv[2]), rgb);
break;
}
case MTEX_BLEND_COLOR: {
float[] rgbToHsv = new float[3];
float[] colToHsv = new float[3];
materialHelper.rgbToHsv(col[0], col[1], col[2], colToHsv);
if (colToHsv[2] != 0) {
materialHelper.rgbToHsv(rgb[0], rgb[1], rgb[2], rgbToHsv);
materialHelper.hsvToRgb(colToHsv[0], colToHsv[1], rgbToHsv[2], rgbToHsv);
rgb[0] = oneMinusFactor * rgb[0] + fac * rgbToHsv[0];
rgb[1] = oneMinusFactor * rgb[1] + fac * rgbToHsv[1];
rgb[2] = oneMinusFactor * rgb[2] + fac * rgbToHsv[2];
}
break;
}
default:
throw new IllegalStateException("Unknown ramp type: " + type);
}
}
}
/**
* This method converts the given texture into normal-map texture.
* @param source

@ -12,7 +12,7 @@ import java.util.logging.Logger;
*
* @author Marcin Roguski (Kaelthas)
*/
/* package */class TexturePixel implements Cloneable {
public class TexturePixel implements Cloneable {
private static final Logger LOGGER = Logger.getLogger(TexturePixel.class.getName());
/** The pixel data. */
@ -147,6 +147,19 @@ import java.util.logging.Logger;
}
}
/**
* Stores RGBA values in the given array.
*
* @param result
* the array to store values
*/
public void toRGBA(float[] result) {
result[0] = this.red;
result[1] = this.green;
result[2] = this.blue;
result[3] = this.alpha;
}
/**
* Stores the data in the given table.
*
@ -160,6 +173,24 @@ import java.util.logging.Logger;
result[3] = (byte) (this.alpha * 255.0f);
}
/**
* Stores the pixel values in the integer.
*
* @return the integer that stores the pixel values
*/
public int toARGB8() {
int result = 0;
int b = (int) (this.alpha * 255.0f);
result |= b << 24;
b = (int) (this.red * 255.0f);
result |= b << 16;
b = (int) (this.green * 255.0f);
result |= b << 8;
b = (int) (this.blue * 255.0f);
result |= b;
return result;
}
/**
* Merges two pixels (adds the values of each color).
*
@ -174,6 +205,16 @@ import java.util.logging.Logger;
// alpha should be always 1.0f as a result
}
/**
* This method negates the colors.
*/
public void negate() {
this.red = 1.0f - this.red;
this.green = 1.0f - this.green;
this.blue = 1.0f - this.blue;
this.alpha = 1.0f - this.alpha;
}
/**
* This method clears the pixel values.
*/

@ -0,0 +1,195 @@
package com.jme3.scene.plugins.blender.textures.blending;
import com.jme3.math.FastMath;
import com.jme3.scene.plugins.blender.BlenderContext;
import com.jme3.scene.plugins.blender.materials.MaterialHelper;
/**
* An abstract class that contains the basic methods used by the classes that
* will derive from it.
*
* @author Marcin Roguski (Kaelthas)
*/
/* package */abstract class AbstractTextureBlender implements TextureBlender {
/**
* This method blends the single pixel depending on the blending type.
*
* @param result
* the result pixel
* @param materialColor
* the material color
* @param pixelColor
* the pixel color
* @param blendFactor
* the blending factor
* @param blendtype
* the blending type
* @param blenderContext
* the blender context
*/
protected void blendPixel(float[] result, float[] materialColor, float[] pixelColor, float blendFactor, int blendtype, BlenderContext blenderContext) {
float oneMinusFactor = 1.0f - blendFactor, col;
switch (blendtype) {
case MTEX_BLEND:
result[0] = blendFactor * pixelColor[0] + oneMinusFactor * materialColor[0];
result[1] = blendFactor * pixelColor[1] + oneMinusFactor * materialColor[1];
result[2] = blendFactor * pixelColor[2] + oneMinusFactor * materialColor[2];
break;
case MTEX_MUL:
result[0] = (oneMinusFactor + blendFactor * materialColor[0]) * pixelColor[0];
result[1] = (oneMinusFactor + blendFactor * materialColor[1]) * pixelColor[1];
result[2] = (oneMinusFactor + blendFactor * materialColor[2]) * pixelColor[2];
break;
case MTEX_DIV:
if (pixelColor[0] != 0.0) {
result[0] = (oneMinusFactor * materialColor[0] + blendFactor * materialColor[0] / pixelColor[0]) * 0.5f;
}
if (pixelColor[1] != 0.0) {
result[1] = (oneMinusFactor * materialColor[1] + blendFactor * materialColor[1] / pixelColor[1]) * 0.5f;
}
if (pixelColor[2] != 0.0) {
result[2] = (oneMinusFactor * materialColor[2] + blendFactor * materialColor[2] / pixelColor[2]) * 0.5f;
}
break;
case MTEX_SCREEN:
result[0] = 1.0f - (oneMinusFactor + blendFactor * (1.0f - materialColor[0])) * (1.0f - pixelColor[0]);
result[1] = 1.0f - (oneMinusFactor + blendFactor * (1.0f - materialColor[1])) * (1.0f - pixelColor[1]);
result[2] = 1.0f - (oneMinusFactor + blendFactor * (1.0f - materialColor[2])) * (1.0f - pixelColor[2]);
break;
case MTEX_OVERLAY:
if (materialColor[0] < 0.5f) {
result[0] = pixelColor[0] * (oneMinusFactor + 2.0f * blendFactor * materialColor[0]);
} else {
result[0] = 1.0f - (oneMinusFactor + 2.0f * blendFactor * (1.0f - materialColor[0])) * (1.0f - pixelColor[0]);
}
if (materialColor[1] < 0.5f) {
result[1] = pixelColor[1] * (oneMinusFactor + 2.0f * blendFactor * materialColor[1]);
} else {
result[1] = 1.0f - (oneMinusFactor + 2.0f * blendFactor * (1.0f - materialColor[1])) * (1.0f - pixelColor[1]);
}
if (materialColor[2] < 0.5f) {
result[2] = pixelColor[2] * (oneMinusFactor + 2.0f * blendFactor * materialColor[2]);
} else {
result[2] = 1.0f - (oneMinusFactor + 2.0f * blendFactor * (1.0f - materialColor[2])) * (1.0f - pixelColor[2]);
}
break;
case MTEX_SUB:
result[0] = materialColor[0] - blendFactor * pixelColor[0];
result[1] = materialColor[1] - blendFactor * pixelColor[1];
result[2] = materialColor[2] - blendFactor * pixelColor[2];
result[0] = FastMath.clamp(result[0], 0.0f, 1.0f);
result[1] = FastMath.clamp(result[1], 0.0f, 1.0f);
result[2] = FastMath.clamp(result[2], 0.0f, 1.0f);
break;
case MTEX_ADD:
result[0] = (blendFactor * pixelColor[0] + materialColor[0]) * 0.5f;
result[1] = (blendFactor * pixelColor[1] + materialColor[1]) * 0.5f;
result[2] = (blendFactor * pixelColor[2] + materialColor[2]) * 0.5f;
break;
case MTEX_DIFF:
result[0] = oneMinusFactor * materialColor[0] + blendFactor * Math.abs(materialColor[0] - pixelColor[0]);
result[1] = oneMinusFactor * materialColor[1] + blendFactor * Math.abs(materialColor[1] - pixelColor[1]);
result[2] = oneMinusFactor * materialColor[2] + blendFactor * Math.abs(materialColor[2] - pixelColor[2]);
break;
case MTEX_DARK:
col = blendFactor * pixelColor[0];
result[0] = col < materialColor[0] ? col : materialColor[0];
col = blendFactor * pixelColor[1];
result[1] = col < materialColor[1] ? col : materialColor[1];
col = blendFactor * pixelColor[2];
result[2] = col < materialColor[2] ? col : materialColor[2];
break;
case MTEX_LIGHT:
col = blendFactor * pixelColor[0];
result[0] = col > materialColor[0] ? col : materialColor[0];
col = blendFactor * pixelColor[1];
result[1] = col > materialColor[1] ? col : materialColor[1];
col = blendFactor * pixelColor[2];
result[2] = col > materialColor[2] ? col : materialColor[2];
break;
case MTEX_BLEND_HUE:
case MTEX_BLEND_SAT:
case MTEX_BLEND_VAL:
case MTEX_BLEND_COLOR:
System.arraycopy(materialColor, 0, result, 0, 3);
this.blendHSV(blendtype, result, blendFactor, pixelColor, blenderContext);
break;
default:
throw new IllegalStateException("Unknown blend type: " + blendtype);
}
}
/**
* The method that performs the ramp blending.
*
* @param type
* the blend type
* @param materialRGB
* the rgb value of the material, here the result is stored too
* @param fac
* color affection factor
* @param pixelColor
* the texture color
* @param blenderContext
* the blender context
*/
protected void blendHSV(int type, float[] materialRGB, float fac, float[] pixelColor, BlenderContext blenderContext) {
float oneMinusFactor = 1.0f - fac;
MaterialHelper materialHelper = blenderContext.getHelper(MaterialHelper.class);
switch (type) {
case MTEX_BLEND_HUE: {// FIXME: not working well for image textures
// (works fine for generated textures)
float[] colorTransformResult = new float[3];
materialHelper.rgbToHsv(pixelColor[0], pixelColor[1], pixelColor[2], colorTransformResult);
if (colorTransformResult[0] != 0.0f) {
float colH = colorTransformResult[0];
materialHelper.rgbToHsv(materialRGB[0], materialRGB[1], materialRGB[2], colorTransformResult);
materialHelper.hsvToRgb(colH, colorTransformResult[1], colorTransformResult[2], colorTransformResult);
materialRGB[0] = oneMinusFactor * materialRGB[0] + fac * colorTransformResult[0];
materialRGB[1] = oneMinusFactor * materialRGB[1] + fac * colorTransformResult[1];
materialRGB[2] = oneMinusFactor * materialRGB[2] + fac * colorTransformResult[2];
}
break;
}
case MTEX_BLEND_SAT: {
float[] colorTransformResult = new float[3];
materialHelper.rgbToHsv(materialRGB[0], materialRGB[1], materialRGB[2], colorTransformResult);
float h = colorTransformResult[0];
float s = colorTransformResult[1];
float v = colorTransformResult[2];
if (s != 0.0f) {
materialHelper.rgbToHsv(pixelColor[0], pixelColor[1], pixelColor[2], colorTransformResult);
materialHelper.hsvToRgb(h, (oneMinusFactor * s + fac * colorTransformResult[1]), v, materialRGB);
}
break;
}
case MTEX_BLEND_VAL: {
float[] rgbToHsv = new float[3];
float[] colToHsv = new float[3];
materialHelper.rgbToHsv(materialRGB[0], materialRGB[1], materialRGB[2], rgbToHsv);
materialHelper.rgbToHsv(pixelColor[0], pixelColor[1], pixelColor[2], colToHsv);
materialHelper.hsvToRgb(rgbToHsv[0], rgbToHsv[1], (oneMinusFactor * rgbToHsv[2] + fac * colToHsv[2]), materialRGB);
break;
}
case MTEX_BLEND_COLOR: {// FIXME: not working well for image
// textures (works fine for generated
// textures)
float[] rgbToHsv = new float[3];
float[] colToHsv = new float[3];
materialHelper.rgbToHsv(pixelColor[0], pixelColor[1], pixelColor[2], colToHsv);
if (colToHsv[2] != 0) {
materialHelper.rgbToHsv(materialRGB[0], materialRGB[1], materialRGB[2], rgbToHsv);
materialHelper.hsvToRgb(colToHsv[0], colToHsv[1], rgbToHsv[2], rgbToHsv);
materialRGB[0] = oneMinusFactor * materialRGB[0] + fac * rgbToHsv[0];
materialRGB[1] = oneMinusFactor * materialRGB[1] + fac * rgbToHsv[1];
materialRGB[2] = oneMinusFactor * materialRGB[2] + fac * rgbToHsv[2];
}
break;
}
default:
throw new IllegalStateException("Unknown ramp type: " + type);
}
}
}

@ -0,0 +1,51 @@
package com.jme3.scene.plugins.blender.textures.blending;
import com.jme3.scene.plugins.blender.BlenderContext;
import com.jme3.texture.Texture;
/**
* An interface for texture blending classes (the classes that mix the texture
* pixels with the material colors).
*
* @author Marcin Roguski (Kaelthas)
*/
public interface TextureBlender {
// types of blending
int MTEX_BLEND = 0;
int MTEX_MUL = 1;
int MTEX_ADD = 2;
int MTEX_SUB = 3;
int MTEX_DIV = 4;
int MTEX_DARK = 5;
int MTEX_DIFF = 6;
int MTEX_LIGHT = 7;
int MTEX_SCREEN = 8;
int MTEX_OVERLAY = 9;
int MTEX_BLEND_HUE = 10;
int MTEX_BLEND_SAT = 11;
int MTEX_BLEND_VAL = 12;
int MTEX_BLEND_COLOR = 13;
int MTEX_NUM_BLENDTYPES = 14;
/**
* This method blends the given texture with material color and the defined
* color in 'map to' panel. As a result of this method a new texture is
* created. The input texture is NOT.
*
* @param materialColor
* the material diffuse color
* @param texture
* the texture we use in blending
* @param color
* the color defined for the texture
* @param affectFactor
* the factor that the color affects the texture (value form 0.0
* to 1.0)
* @param blendType
* the blending type
* @param blenderContext
* the blender context
* @return new texture that was created after the blending
*/
Texture blend(float[] materialColor, Texture texture, float[] color, float affectFactor, int blendType, boolean neg, BlenderContext blenderContext);
}

@ -0,0 +1,162 @@
package com.jme3.scene.plugins.blender.textures.blending;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.jme3.scene.plugins.blender.BlenderContext;
import com.jme3.texture.Image;
import com.jme3.texture.Texture;
import com.jme3.texture.Texture2D;
import com.jme3.texture.Texture3D;
import com.jme3.texture.Image.Format;
import com.jme3.util.BufferUtils;
/**
* The class that is responsible for blending the following texture types:
* <li> RGBA8
* <li> ABGR8
* <li> BGR8
* <li> RGB8
* Not yet supported (but will be):
* <li> ARGB4444:
* <li> RGB10:
* <li> RGB111110F:
* <li> RGB16:
* <li> RGB16F:
* <li> RGB16F_to_RGB111110F:
* <li> RGB16F_to_RGB9E5:
* <li> RGB32F:
* <li> RGB565:
* <li> RGB5A1:
* <li> RGB9E5:
* <li> RGBA16:
* <li> RGBA16F
* @author Marcin Roguski (Kaelthas)
*/
public class TextureBlenderAWT extends AbstractTextureBlender {
private static final Logger LOGGER = Logger.getLogger(TextureBlenderAWT.class.getName());
@Override
public Texture blend(float[] materialColor, Texture texture, float[] color, float affectFactor, int blendType, boolean neg, BlenderContext blenderContext) {
float[] pixelColor = new float[] { color[0], color[1], color[2], 1.0f };
Format format = texture.getImage().getFormat();
ByteBuffer data = texture.getImage().getData(0);
data.rewind();
int width = texture.getImage().getWidth();
int height = texture.getImage().getHeight();
int depth = texture.getImage().getDepth();
if (depth == 0) {
depth = 1;
}
ByteBuffer newData = BufferUtils.createByteBuffer(width * height * depth * 4);
float[] resultPixel = new float[4];
int dataIndex = 0;
while (data.hasRemaining()) {
float tin = this.setupMaterialColor(data, format, neg, pixelColor);
this.blendPixel(resultPixel, materialColor, color, tin, blendType, blenderContext);
newData.put(dataIndex++, (byte) (resultPixel[0] * 255.0f));
newData.put(dataIndex++, (byte) (resultPixel[1] * 255.0f));
newData.put(dataIndex++, (byte) (resultPixel[2] * 255.0f));
newData.put(dataIndex++, (byte) (pixelColor[3] * 255.0f));
}
if (texture.getType() == Texture.Type.TwoDimensional) {
return new Texture2D(new Image(Format.RGBA8, width, height, newData));
} else {
ArrayList<ByteBuffer> dataArray = new ArrayList<ByteBuffer>(1);
dataArray.add(newData);
return new Texture3D(new Image(Format.RGBA8, width, height, depth, dataArray));
}
}
/**
* This method alters the material color in a way dependent on the type of
* the image. For example the color remains untouched if the texture is of
* Luminance type. The luminance defines the interaction between the
* material color and color defined for texture blending. If the type has 3
* or more color channels then the material color is replaced with the
* texture's color and later blended with the defined blend color. All alpha
* values (if present) are ignored and not used during blending.
*
* @param data
* the image data
* @param imageFormat
* the format of the image
* @param neg
* defines it the result color should be nagated
* @param materialColor
* the material's color (value may be changed)
* @return texture intensity for the current pixel
*/
protected float setupMaterialColor(ByteBuffer data, Format imageFormat, boolean neg, float[] materialColor) {
float tin = 0.0f;
byte pixelValue = data.get();// at least one byte is always taken :)
float firstPixelValue = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
switch (imageFormat) {
case RGBA8:
materialColor[0] = firstPixelValue;
pixelValue = data.get();
materialColor[1] = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
pixelValue = data.get();
materialColor[2] = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
pixelValue = data.get();
materialColor[3] = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
break;
case ABGR8:
materialColor[3] = firstPixelValue;
pixelValue = data.get();
materialColor[2] = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
pixelValue = data.get();
materialColor[1] = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
pixelValue = data.get();
materialColor[0] = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
break;
case BGR8:
materialColor[2] = firstPixelValue;
pixelValue = data.get();
materialColor[1] = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
pixelValue = data.get();
materialColor[0] = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
materialColor[3] = 1.0f;
break;
case RGB8:
materialColor[0] = firstPixelValue;
pixelValue = data.get();
materialColor[1] = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
pixelValue = data.get();
materialColor[2] = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
materialColor[3] = 1.0f;
break;
case ARGB4444:
case RGB10:
case RGB111110F:
case RGB16:
case RGB16F:
case RGB16F_to_RGB111110F:
case RGB16F_to_RGB9E5:
case RGB32F:
case RGB565:
case RGB5A1:
case RGB9E5:
case RGBA16:
case RGBA16F:
case RGBA32F:// TODO: implement these textures
LOGGER.log(Level.WARNING, "Image type not yet supported for blending: {0}", imageFormat);
break;
default:
throw new IllegalStateException("Invalid image format type for AWT texture blender: " + imageFormat);
}
if (neg) {
materialColor[0] = 1.0f - materialColor[0];
materialColor[1] = 1.0f - materialColor[1];
materialColor[2] = 1.0f - materialColor[2];
}
// Blender formula for texture intensity calculation:
// 0.35*texres.tr+0.45*texres.tg+0.2*texres.tb
tin = 0.35f * materialColor[0] + 0.45f * materialColor[1] + 0.2f * materialColor[2];
return tin;
}
}

@ -0,0 +1,96 @@
package com.jme3.scene.plugins.blender.textures.blending;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import jme3tools.converters.RGB565;
import com.jme3.scene.plugins.blender.BlenderContext;
import com.jme3.scene.plugins.blender.textures.TexturePixel;
import com.jme3.texture.Image;
import com.jme3.texture.Image.Format;
import com.jme3.texture.Texture;
import com.jme3.texture.Texture2D;
import com.jme3.texture.Texture3D;
import com.jme3.util.BufferUtils;
/**
* The class that is responsible for blending the following texture types:
* <li> DXT1
* <li> DXT3
* <li> DXT5
* Not yet supported (but will be):
* <li> DXT1A:
* @author Marcin Roguski (Kaelthas)
*/
public class TextureBlenderDDS extends AbstractTextureBlender {
private static final Logger LOGGER = Logger.getLogger(TextureBlenderDDS.class.getName());
@Override
public Texture blend(float[] materialColor, Texture texture, float[] color, float affectFactor, int blendType, boolean neg, BlenderContext blenderContext) {
Format format = texture.getImage().getFormat();
ByteBuffer data = texture.getImage().getData(0);
data.rewind();
int width = texture.getImage().getWidth();
int height = texture.getImage().getHeight();
int depth = texture.getImage().getDepth();
if (depth == 0) {
depth = 1;
}
ByteBuffer newData = BufferUtils.createByteBuffer(data.remaining());
float[] resultPixel = new float[4];
float[] pixelColor = new float[4];
TexturePixel[] colors = new TexturePixel[] { new TexturePixel(), new TexturePixel() };
int dataIndex = 0;
while (data.hasRemaining()) {
switch (format) {
case DXT3:
case DXT5:
newData.putLong(dataIndex, data.getLong());// just copy the
// 8 bytes of
// alphas
dataIndex += 8;
case DXT1:
int col0 = RGB565.RGB565_to_ARGB8(data.getShort());
int col1 = RGB565.RGB565_to_ARGB8(data.getShort());
colors[0].fromARGB8(col0);
colors[1].fromARGB8(col1);
break;
case DXT1A:
LOGGER.log(Level.WARNING, "Image type not yet supported for blending: {0}", format);
break;
default:
throw new IllegalStateException("Invalid image format type for DDS texture blender: " + format);
}
// blending colors
for (int i = 0; i < colors.length; ++i) {
if (neg) {
colors[i].negate();
}
colors[i].toRGBA(pixelColor);
this.blendPixel(resultPixel, materialColor, pixelColor, affectFactor, blendType, blenderContext);
colors[i].fromARGB8(1, resultPixel[0], resultPixel[1], resultPixel[2]);
int argb8 = colors[i].toARGB8();
short rgb565 = RGB565.ARGB8_to_RGB565(argb8);
newData.putShort(dataIndex, rgb565);
dataIndex += 2;
}
// just copy the remaining 4 bytes of the current texel
newData.putInt(dataIndex, data.getInt());
dataIndex += 4;
}
if (texture.getType() == Texture.Type.TwoDimensional) {
return new Texture2D(new Image(format, width, height, newData));
} else {
ArrayList<ByteBuffer> dataArray = new ArrayList<ByteBuffer>(1);
dataArray.add(newData);
return new Texture3D(new Image(format, width, height, depth, dataArray));
}
}
}

@ -0,0 +1,81 @@
package com.jme3.scene.plugins.blender.textures.blending;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.jme3.scene.plugins.blender.BlenderContext;
import com.jme3.texture.Texture;
import com.jme3.texture.Image.Format;
/**
* This class creates the texture blending class depending on the texture type.
*
* @author Marcin Roguski (Kaelthas)
*/
public class TextureBlenderFactory {
private static final Logger LOGGER = Logger.getLogger(TextureBlenderFactory.class.getName());
/**
* This method creates the blending class.
*
* @param format
* the texture format
* @returntexture blending class
*/
public static TextureBlender createTextureBlender(Format format) {
switch (format) {
case Luminance8:
case Luminance8Alpha8:
case Luminance16:
case Luminance16Alpha16:
case Luminance16F:
case Luminance16FAlpha16F:
case Luminance32F:
return new TextureBlenderLuminance();
case RGBA8:
case ABGR8:
case BGR8:
case RGB8:
case RGB10:
case RGB111110F:
case RGB16:
case RGB16F:
case RGB16F_to_RGB111110F:
case RGB16F_to_RGB9E5:
case RGB32F:
case RGB565:
case RGB5A1:
case RGB9E5:
case RGBA16:
case RGBA16F:
case RGBA32F:
return new TextureBlenderAWT();
case DXT1:
case DXT1A:
case DXT3:
case DXT5:
return new TextureBlenderDDS();
case Alpha16:
case Alpha8:
case ARGB4444:
case Depth:
case Depth16:
case Depth24:
case Depth32:
case Depth32F:
case Intensity16:
case Intensity8:
case LATC:
case LTC:
LOGGER.log(Level.WARNING, "Image type not yet supported for blending: {0}. Returning a blender that does not change the texture.", format);
return new TextureBlender() {
@Override
public Texture blend(float[] materialColor, Texture texture, float[] color, float affectFactor, int blendType, boolean neg, BlenderContext blenderContext) {
return texture;
}
};
default:
throw new IllegalStateException("Unknown image format type: " + format);
}
}
}

@ -0,0 +1,221 @@
package com.jme3.scene.plugins.blender.textures.blending;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.jme3.math.FastMath;
import com.jme3.scene.plugins.blender.BlenderContext;
import com.jme3.texture.Image;
import com.jme3.texture.Texture;
import com.jme3.texture.Texture2D;
import com.jme3.texture.Texture3D;
import com.jme3.texture.Image.Format;
import com.jme3.util.BufferUtils;
/**
* The class that is responsible for blending the following texture types:
* <li> Luminance8
* <li> Luminance8Alpha8
* Not yet supported (but will be):
* <li> Luminance16:
* <li> Luminance16Alpha16:
* <li> Luminance16F:
* <li> Luminance16FAlpha16F:
* <li> Luminance32F:
* @author Marcin Roguski (Kaelthas)
*/
public class TextureBlenderLuminance extends AbstractTextureBlender {
private static final Logger LOGGER = Logger.getLogger(TextureBlenderLuminance.class.getName());
@Override
public Texture blend(float[] materialColor, Texture texture, float[] color, float affectFactor, int blendType, boolean neg, BlenderContext blenderContext) {
Format format = texture.getImage().getFormat();
ByteBuffer data = texture.getImage().getData(0);
data.rewind();
int width = texture.getImage().getWidth();
int height = texture.getImage().getHeight();
int depth = texture.getImage().getDepth();
if (depth == 0) {
depth = 1;
}
ByteBuffer newData = BufferUtils.createByteBuffer(width * height * depth * 4);
float[] resultPixel = new float[4];
float[] tinAndAlpha = new float[2];
int dataIndex = 0;
while (data.hasRemaining()) {
this.getTinAndAlpha(data, format, neg, tinAndAlpha);
this.blendPixel(resultPixel, materialColor, color, tinAndAlpha[0], affectFactor, blendType, blenderContext);
newData.put(dataIndex++, (byte) (resultPixel[0] * 255.0f));
newData.put(dataIndex++, (byte) (resultPixel[1] * 255.0f));
newData.put(dataIndex++, (byte) (resultPixel[2] * 255.0f));
newData.put(dataIndex++, (byte) (tinAndAlpha[1] * 255.0f));
}
if (texture.getType() == Texture.Type.TwoDimensional) {
return new Texture2D(new Image(Format.RGBA8, width, height, newData));
} else {
ArrayList<ByteBuffer> dataArray = new ArrayList<ByteBuffer>(1);
dataArray.add(newData);
return new Texture3D(new Image(Format.RGBA8, width, height, depth, dataArray));
}
}
/**
* This method return texture intensity and alpha value.
*
* @param data
* the texture data
* @param imageFormat
* the image format
* @param neg
* indicates if the texture is negated
* @param result
* the table (2 elements) where the result is being stored
*/
protected void getTinAndAlpha(ByteBuffer data, Format imageFormat, boolean neg, float[] result) {
byte pixelValue = data.get();// at least one byte is always taken
float firstPixelValue = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
switch (imageFormat) {
case Luminance8:
result[0] = neg ? 1.0f - firstPixelValue : firstPixelValue;
result[1] = 1.0f;
break;
case Luminance8Alpha8:
result[0] = neg ? 1.0f - firstPixelValue : firstPixelValue;
pixelValue = data.get();
result[1] = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
break;
case Luminance16:
case Luminance16Alpha16:
case Luminance16F:
case Luminance16FAlpha16F:
case Luminance32F:
LOGGER.log(Level.WARNING, "Image type not yet supported for blending: {0}", imageFormat);
break;
default:
throw new IllegalStateException("Invalid image format type for DDS texture blender: " + imageFormat);
}
}
/**
* This method blends the texture with an appropriate color.
*
* @param result
* the result color (variable 'in' in blender source code)
* @param materialColor
* the texture color (variable 'out' in blender source coude)
* @param color
* the previous color (variable 'tex' in blender source code)
* @param textureIntensity
* texture intensity (variable 'fact' in blender source code)
* @param textureFactor
* texture affection factor (variable 'facg' in blender source
* code)
* @param blendtype
* the blend type
* @param blenderContext
* the blender context
*/
protected void blendPixel(float[] result, float[] materialColor, float[] color, float textureIntensity, float textureFactor, int blendtype, BlenderContext blenderContext) {
float oneMinusFactor, col;
textureIntensity *= textureFactor;
switch (blendtype) {
case MTEX_BLEND:
oneMinusFactor = 1.0f - textureIntensity;
result[0] = textureIntensity * color[0] + oneMinusFactor * materialColor[0];
result[1] = textureIntensity * color[1] + oneMinusFactor * materialColor[1];
result[2] = textureIntensity * color[2] + oneMinusFactor * materialColor[2];
break;
case MTEX_MUL:
oneMinusFactor = 1.0f - textureFactor;
result[0] = (oneMinusFactor + textureIntensity * materialColor[0]) * color[0];
result[1] = (oneMinusFactor + textureIntensity * materialColor[1]) * color[1];
result[2] = (oneMinusFactor + textureIntensity * materialColor[2]) * color[2];
break;
case MTEX_DIV:
oneMinusFactor = 1.0f - textureIntensity;
if (color[0] != 0.0) {
result[0] = (oneMinusFactor * materialColor[0] + textureIntensity * materialColor[0] / color[0]) * 0.5f;
}
if (color[1] != 0.0) {
result[1] = (oneMinusFactor * materialColor[1] + textureIntensity * materialColor[1] / color[1]) * 0.5f;
}
if (color[2] != 0.0) {
result[2] = (oneMinusFactor * materialColor[2] + textureIntensity * materialColor[2] / color[2]) * 0.5f;
}
break;
case MTEX_SCREEN:
oneMinusFactor = 1.0f - textureFactor;
result[0] = 1.0f - (oneMinusFactor + textureIntensity * (1.0f - materialColor[0])) * (1.0f - color[0]);
result[1] = 1.0f - (oneMinusFactor + textureIntensity * (1.0f - materialColor[1])) * (1.0f - color[1]);
result[2] = 1.0f - (oneMinusFactor + textureIntensity * (1.0f - materialColor[2])) * (1.0f - color[2]);
break;
case MTEX_OVERLAY:
oneMinusFactor = 1.0f - textureFactor;
if (materialColor[0] < 0.5f) {
result[0] = color[0] * (oneMinusFactor + 2.0f * textureIntensity * materialColor[0]);
} else {
result[0] = 1.0f - (oneMinusFactor + 2.0f * textureIntensity * (1.0f - materialColor[0])) * (1.0f - color[0]);
}
if (materialColor[1] < 0.5f) {
result[1] = color[1] * (oneMinusFactor + 2.0f * textureIntensity * materialColor[1]);
} else {
result[1] = 1.0f - (oneMinusFactor + 2.0f * textureIntensity * (1.0f - materialColor[1])) * (1.0f - color[1]);
}
if (materialColor[2] < 0.5f) {
result[2] = color[2] * (oneMinusFactor + 2.0f * textureIntensity * materialColor[2]);
} else {
result[2] = 1.0f - (oneMinusFactor + 2.0f * textureIntensity * (1.0f - materialColor[2])) * (1.0f - color[2]);
}
break;
case MTEX_SUB:
result[0] = materialColor[0] - textureIntensity * color[0];
result[1] = materialColor[1] - textureIntensity * color[1];
result[2] = materialColor[2] - textureIntensity * color[2];
result[0] = FastMath.clamp(result[0], 0.0f, 1.0f);
result[1] = FastMath.clamp(result[1], 0.0f, 1.0f);
result[2] = FastMath.clamp(result[2], 0.0f, 1.0f);
break;
case MTEX_ADD:
result[0] = (textureIntensity * color[0] + materialColor[0]) * 0.5f;
result[1] = (textureIntensity * color[1] + materialColor[1]) * 0.5f;
result[2] = (textureIntensity * color[2] + materialColor[2]) * 0.5f;
break;
case MTEX_DIFF:
oneMinusFactor = 1.0f - textureIntensity;
result[0] = oneMinusFactor * materialColor[0] + textureIntensity * Math.abs(materialColor[0] - color[0]);
result[1] = oneMinusFactor * materialColor[1] + textureIntensity * Math.abs(materialColor[1] - color[1]);
result[2] = oneMinusFactor * materialColor[2] + textureIntensity * Math.abs(materialColor[2] - color[2]);
break;
case MTEX_DARK:
col = textureIntensity * color[0];
result[0] = col < materialColor[0] ? col : materialColor[0];
col = textureIntensity * color[1];
result[1] = col < materialColor[1] ? col : materialColor[1];
col = textureIntensity * color[2];
result[2] = col < materialColor[2] ? col : materialColor[2];
break;
case MTEX_LIGHT:
col = textureIntensity * color[0];
result[0] = col > materialColor[0] ? col : materialColor[0];
col = textureIntensity * color[1];
result[1] = col > materialColor[1] ? col : materialColor[1];
col = textureIntensity * color[2];
result[2] = col > materialColor[2] ? col : materialColor[2];
break;
case MTEX_BLEND_HUE:
case MTEX_BLEND_SAT:
case MTEX_BLEND_VAL:
case MTEX_BLEND_COLOR:
System.arraycopy(materialColor, 0, result, 0, 3);
this.blendHSV(blendtype, result, textureIntensity, color, blenderContext);
break;
default:
throw new IllegalStateException("Unknown blend type: " + blendtype);
}
}
}
Loading…
Cancel
Save