diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/materials/MaterialContext.java b/engine/src/blender/com/jme3/scene/plugins/blender/materials/MaterialContext.java index f4e18a8a2..2e6b0cf28 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/materials/MaterialContext.java +++ b/engine/src/blender/com/jme3/scene/plugins/blender/materials/MaterialContext.java @@ -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> sortedTextures = this.sortAndFilterTextures(); loadedTextures = new HashMap(sortedTextures.size()); textureToMTexMap = new HashMap(); - 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> 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; diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureDecompressor.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureDecompressor.java deleted file mode 100644 index 811420c1e..000000000 --- a/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureDecompressor.java +++ /dev/null @@ -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); - } - } -} diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureHelper.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureHelper.java index 5fefc57cc..0e0861d66 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureHelper.java +++ b/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureHelper.java @@ -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 textureGenerators = new HashMap(); @@ -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 dataArray = new ArrayList(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 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(); @@ -360,314 +290,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. diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/TexturePixel.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/TexturePixel.java index 3b1f8efeb..37c0122cc 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/textures/TexturePixel.java +++ b/engine/src/blender/com/jme3/scene/plugins/blender/textures/TexturePixel.java @@ -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. */ diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/AbstractTextureBlender.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/AbstractTextureBlender.java new file mode 100644 index 000000000..505c94b56 --- /dev/null +++ b/engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/AbstractTextureBlender.java @@ -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); + } + } +} diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlender.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlender.java new file mode 100644 index 000000000..08e25d32a --- /dev/null +++ b/engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlender.java @@ -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); +} diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderAWT.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderAWT.java new file mode 100644 index 000000000..75fc0c573 --- /dev/null +++ b/engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderAWT.java @@ -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: + *
  • RGBA8 + *
  • ABGR8 + *
  • BGR8 + *
  • RGB8 + * Not yet supported (but will be): + *
  • ARGB4444: + *
  • RGB10: + *
  • RGB111110F: + *
  • RGB16: + *
  • RGB16F: + *
  • RGB16F_to_RGB111110F: + *
  • RGB16F_to_RGB9E5: + *
  • RGB32F: + *
  • RGB565: + *
  • RGB5A1: + *
  • RGB9E5: + *
  • RGBA16: + *
  • 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 dataArray = new ArrayList(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; + } +} diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderDDS.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderDDS.java new file mode 100644 index 000000000..a532cfb37 --- /dev/null +++ b/engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderDDS.java @@ -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: + *
  • DXT1 + *
  • DXT3 + *
  • DXT5 + * Not yet supported (but will be): + *
  • 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 dataArray = new ArrayList(1); + dataArray.add(newData); + return new Texture3D(new Image(format, width, height, depth, dataArray)); + } + } +} diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderFactory.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderFactory.java new file mode 100644 index 000000000..c9ad61a75 --- /dev/null +++ b/engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderFactory.java @@ -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); + } + } +} diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderLuminance.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderLuminance.java new file mode 100644 index 000000000..a616685a3 --- /dev/null +++ b/engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderLuminance.java @@ -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: + *
  • Luminance8 + *
  • Luminance8Alpha8 + * Not yet supported (but will be): + *
  • Luminance16: + *
  • Luminance16Alpha16: + *
  • Luminance16F: + *
  • Luminance16FAlpha16F: + *
  • 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 dataArray = new ArrayList(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); + } + } +}