소스 검색

Added support for loading the following texture types: DXT1, DXT3 and DXT5.

git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@9178 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
Kae..pl 13 년 전
부모
커밋
3d22756dfc

+ 304 - 0
engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureDecompressor.java

@@ -0,0 +1,304 @@
+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);
+		}
+	}
+}

+ 31 - 18
engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureHelper.java

@@ -31,8 +31,25 @@
  */
 package com.jme3.scene.plugins.blender.textures;
 
+import java.awt.color.ColorSpace;
+import java.awt.image.BufferedImage;
+import java.awt.image.ColorConvertOp;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import jme3tools.converters.ImageToAwt;
+
+import com.jme3.asset.AssetManager;
+import com.jme3.asset.AssetNotFoundException;
+import com.jme3.asset.BlenderKey;
 import com.jme3.asset.BlenderKey.FeaturesToLoad;
-import com.jme3.asset.*;
+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;
@@ -53,17 +70,6 @@ import com.jme3.texture.Texture.WrapMode;
 import com.jme3.texture.Texture2D;
 import com.jme3.texture.Texture3D;
 import com.jme3.util.BufferUtils;
-import java.awt.color.ColorSpace;
-import java.awt.image.BufferedImage;
-import java.awt.image.ColorConvertOp;
-import java.nio.ByteBuffer;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-import jme3tools.converters.ImageToAwt;
 
 /**
  * A class that is used in texture calculations.
@@ -245,7 +251,11 @@ public class TextureHelper extends AbstractBlenderHelper {
 		float[] materialColorClone = materialColor.clone();//this array may change, so we copy it
 		Format format = texture.getImage().getFormat();
 		ByteBuffer data = texture.getImage().getData(0);
-		data.rewind();
+		
+		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();
@@ -356,7 +366,7 @@ public class TextureHelper extends AbstractBlenderHelper {
 	 * 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 replaces with the texture's
+	 * 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
@@ -419,6 +429,13 @@ public class TextureHelper extends AbstractBlenderHelper {
 			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:
@@ -429,10 +446,6 @@ public class TextureHelper extends AbstractBlenderHelper {
 		case Depth24:
 		case Depth32:
 		case Depth32F:
-		case DXT1:
-		case DXT1A:
-		case DXT3:
-		case DXT5:
 		case Intensity16:
 		case Intensity8:
 		case LATC:

+ 173 - 14
engine/src/blender/com/jme3/scene/plugins/blender/textures/TexturePixel.java

@@ -1,28 +1,100 @@
 package com.jme3.scene.plugins.blender.textures;
 
 import com.jme3.math.ColorRGBA;
+import com.jme3.math.FastMath;
 import com.jme3.texture.Image.Format;
 import java.nio.ByteBuffer;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
-/*package*/ class TexturePixel implements Cloneable {
-	private static final Logger LOGGER = Logger.getLogger(TexturePixel.class.getName());
-	
-	public float	intensity, red, green, blue, alpha;
+/**
+ * The class that stores the pixel values of a texture.
+ * 
+ * @author Marcin Roguski (Kaelthas)
+ */
+/* package */class TexturePixel implements Cloneable {
+	private static final Logger	LOGGER	= Logger.getLogger(TexturePixel.class.getName());
 
+	/** The pixel data. */
+	public float				intensity, red, green, blue, alpha;
+
+	/**
+	 * Copies the values from the given pixel.
+	 * 
+	 * @param pixel
+	 *            the pixel that we read from
+	 */
+	public void fromPixel(TexturePixel pixel) {
+		this.intensity = pixel.intensity;
+		this.red = pixel.red;
+		this.green = pixel.green;
+		this.blue = pixel.blue;
+		this.alpha = pixel.alpha;
+	}
+
+	/**
+	 * Copies the values from the given color.
+	 * 
+	 * @param colorRGBA
+	 *            the color that we read from
+	 */
 	public void fromColor(ColorRGBA colorRGBA) {
-		this.intensity = 0;
 		this.red = colorRGBA.r;
 		this.green = colorRGBA.g;
 		this.blue = colorRGBA.b;
 		this.alpha = colorRGBA.a;
 	}
-	
+
+	/**
+	 * Copies the values from the given values.
+	 * 
+	 * @param a
+	 *            the alpha value
+	 * @param r
+	 *            the red value
+	 * @param g
+	 *            the green value
+	 * @param b
+	 *            the blue value
+	 */
+	public void fromARGB8(float a, float r, float g, float b) {
+		this.alpha = a;
+		this.red = r;
+		this.green = g;
+		this.blue = b;
+	}
+
+	/**
+	 * Copies the values from the given integer that stores the ARGB8 data.
+	 * 
+	 * @param argb8
+	 *            the data stored in an integer
+	 */
+	public void fromARGB8(int argb8) {
+		byte pixelValue = (byte) ((argb8 & 0xFF000000) >> 24);
+		this.alpha = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
+		pixelValue = (byte) ((argb8 & 0xFF0000) >> 16);
+		this.red = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
+		pixelValue = (byte) ((argb8 & 0xFF00) >> 8);
+		this.green = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
+		pixelValue = (byte) (argb8 & 0xFF);
+		this.blue = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
+	}
+
+	/**
+	 * Copies the data from the given image.
+	 * 
+	 * @param imageFormat
+	 *            the image format
+	 * @param data
+	 *            the image data
+	 * @param pixelIndex
+	 *            the index of the required pixel
+	 */
 	public void fromImage(Format imageFormat, ByteBuffer data, int pixelIndex) {
 		int firstByteIndex;
 		byte pixelValue;
-		switch(imageFormat) {
+		switch (imageFormat) {
 			case ABGR8:
 				firstByteIndex = pixelIndex << 2;
 				pixelValue = data.get(firstByteIndex);
@@ -74,21 +146,108 @@ import java.util.logging.Logger;
 				this.intensity = this.blue = this.red = this.green = this.alpha = 0.0f;
 		}
 	}
-	
+
+	/**
+	 * Stores the data in the given table.
+	 * 
+	 * @param result
+	 *            the result table
+	 */
+	public void toRGBA8(byte[] result) {
+		result[0] = (byte) (this.red * 255.0f);
+		result[1] = (byte) (this.green * 255.0f);
+		result[2] = (byte) (this.blue * 255.0f);
+		result[3] = (byte) (this.alpha * 255.0f);
+	}
+
+	/**
+	 * Merges two pixels (adds the values of each color).
+	 * 
+	 * @param pixel
+	 *            the pixel we merge with
+	 */
 	public void merge(TexturePixel pixel) {
 		float oneMinusAlpha = 1 - pixel.alpha;
-		this.red = oneMinusAlpha * this.red + pixel.alpha*pixel.red;
-		this.green = oneMinusAlpha * this.green + pixel.alpha*pixel.green;
-		this.blue = oneMinusAlpha * this.blue + pixel.alpha*pixel.blue;
-		//alpha should be always 1.0f as a result
+		this.red = oneMinusAlpha * this.red + pixel.alpha * pixel.red;
+		this.green = oneMinusAlpha * this.green + pixel.alpha * pixel.green;
+		this.blue = oneMinusAlpha * this.blue + pixel.alpha * pixel.blue;
+		// alpha should be always 1.0f as a result
 	}
-	
+
+	/**
+	 * This method clears the pixel values.
+	 */
 	public void clear() {
 		this.intensity = this.blue = this.red = this.green = this.alpha = 0.0f;
 	}
-	
+
+	/**
+	 * This method adds the calues of the given pixel to the current pixel.
+	 * 
+	 * @param pixel
+	 *            the pixel we add
+	 */
+	public void add(TexturePixel pixel) {
+		this.red += pixel.red;
+		this.green += pixel.green;
+		this.blue += pixel.blue;
+		this.alpha += pixel.alpha;
+		this.intensity += pixel.intensity;
+	}
+
+	/**
+	 * This method multiplies the values of the given pixel by the given value.
+	 * 
+	 * @param value
+	 *            multiplication factor
+	 */
+	public void mult(float value) {
+		this.red *= value;
+		this.green *= value;
+		this.blue *= value;
+		this.alpha *= value;
+		this.intensity *= value;
+	}
+
+	/**
+	 * This method divides the values of the given pixel by the given value.
+	 * ATTENTION! Beware of the zero value. This will cause you NaN's in the
+	 * pixel values.
+	 * 
+	 * @param value
+	 *            division factor
+	 */
+	public void divide(float value) {
+		this.red /= value;
+		this.green /= value;
+		this.blue /= value;
+		this.alpha /= value;
+		this.intensity /= value;
+	}
+
+	/**
+	 * This method clamps the pixel values to the given borders.
+	 * 
+	 * @param min
+	 *            the minimum value
+	 * @param max
+	 *            the maximum value
+	 */
+	public void clamp(float min, float max) {
+		this.red = FastMath.clamp(this.red, min, max);
+		this.green = FastMath.clamp(this.green, min, max);
+		this.blue = FastMath.clamp(this.blue, min, max);
+		this.alpha = FastMath.clamp(this.alpha, min, max);
+		this.intensity = FastMath.clamp(this.intensity, min, max);
+	}
+
 	@Override
 	public Object clone() throws CloneNotSupportedException {
 		return super.clone();
 	}
+
+	@Override
+	public String toString() {
+		return "[" + red + ", " + green + ", " + blue + ", " + alpha + " {" + intensity + "}]";
+	}
 }