- 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-0572b91ccdca3.0
parent
b12285b8a4
commit
2b004e803d
@ -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); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -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…
Reference in new issue