Image: support for RGTC format

experimental
Kirill Vainer 9 years ago
parent f9ce9e246c
commit 6db1d15045
  1. 7
      jme3-core/src/main/java/com/jme3/renderer/Caps.java
  2. 2
      jme3-core/src/main/java/com/jme3/renderer/opengl/GLExt.java
  3. 5
      jme3-core/src/main/java/com/jme3/renderer/opengl/GLImageFormats.java
  4. 4
      jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java
  5. 16
      jme3-core/src/main/java/com/jme3/texture/Image.java
  6. 76
      jme3-core/src/plugins/java/com/jme3/texture/plugins/DDSLoader.java
  7. 8
      jme3-core/src/plugins/java/com/jme3/texture/plugins/DXTFlipper.java

@ -349,7 +349,12 @@ public enum Caps {
/** /**
* GPU can provide and accept binary shaders. * GPU can provide and accept binary shaders.
*/ */
BinaryShader; BinaryShader,
/**
* Supports {@link Format#RGTC} and {@link Format#RTC} texture compression.
*/
TextureCompressionRGTC;
/** /**
* Returns true if given the renderer capabilities, the texture * Returns true if given the renderer capabilities, the texture

@ -44,6 +44,8 @@ import java.nio.IntBuffer;
public interface GLExt { public interface GLExt {
public static final int GL_ALREADY_SIGNALED = 0x911A; public static final int GL_ALREADY_SIGNALED = 0x911A;
public static final int GL_COMPRESSED_RED_RGTC1 = 0x8DBB;
public static final int GL_COMPRESSED_RG_RGTC2 = 0x8DBD;
public static final int GL_COMPRESSED_RGB8_ETC2 = 0x9274; public static final int GL_COMPRESSED_RGB8_ETC2 = 0x9274;
public static final int GL_COMPRESSED_RGBA_S3TC_DXT1_EXT = 0x83F1; public static final int GL_COMPRESSED_RGBA_S3TC_DXT1_EXT = 0x83F1;
public static final int GL_COMPRESSED_RGBA_S3TC_DXT3_EXT = 0x83F2; public static final int GL_COMPRESSED_RGBA_S3TC_DXT3_EXT = 0x83F2;

@ -233,6 +233,11 @@ public final class GLImageFormats {
formatComp(formatToGL, Format.ETC1, GLExt.GL_ETC1_RGB8_OES, GL.GL_RGB, GL.GL_UNSIGNED_BYTE); formatComp(formatToGL, Format.ETC1, GLExt.GL_ETC1_RGB8_OES, GL.GL_RGB, GL.GL_UNSIGNED_BYTE);
} }
if (caps.contains(Caps.TextureCompressionRGTC)) {
formatComp(formatToGL, Format.RGTC, GLExt.GL_COMPRESSED_RG_RGTC2, GL.GL_RGB, GL.GL_UNSIGNED_BYTE);
formatComp(formatToGL, Format.RTC, GLExt.GL_COMPRESSED_RED_RGTC1, GL.GL_RED, GL.GL_UNSIGNED_BYTE);
}
return formatToGL; return formatToGL;
} }
} }

@ -349,6 +349,10 @@ public final class GLRenderer implements Renderer {
caps.add(Caps.TextureCompressionETC1); caps.add(Caps.TextureCompressionETC1);
} }
if (hasExtension("GL_ARB_texture_compression_rgtc")) {
caps.add(Caps.TextureCompressionRGTC);
}
// == end texture format extensions == // == end texture format extensions ==
if (hasExtension("GL_ARB_vertex_array_object") || caps.contains(Caps.OpenGL30)) { if (hasExtension("GL_ARB_vertex_array_object") || caps.contains(Caps.OpenGL30)) {

@ -299,7 +299,21 @@ public class Image extends NativeObject implements Savable /*, Cloneable*/ {
* *
* Requires {@link Caps#TextureCompressionETC1}. * Requires {@link Caps#TextureCompressionETC1}.
*/ */
ETC1(4, false, true, false); ETC1(4, false, true, false),
/**
* RGTC with red channel only.
*
* Requires {@link Caps#TextureCompressionRGTC}.
*/
RTC(4, false, true, false),
/**
* RGTC with red and green channels.
*
* Requires {@link Caps#TextureCompressionRGTC}.
*/
RGTC(8, false, true, false);
private int bpp; private int bpp;
private boolean isDepth; private boolean isDepth;

@ -85,6 +85,8 @@ public class DDSLoader implements AssetLoader {
private static final int PF_DXT1 = 0x31545844; private static final int PF_DXT1 = 0x31545844;
private static final int PF_DXT3 = 0x33545844; private static final int PF_DXT3 = 0x33545844;
private static final int PF_DXT5 = 0x35545844; private static final int PF_DXT5 = 0x35545844;
private static final int PF_ETC1 = 0x31435445;
private static final int PF_ETC_ = 0x20435445; // the underscore represents a space
private static final int PF_ATI1 = 0x31495441; private static final int PF_ATI1 = 0x31495441;
private static final int PF_ATI2 = 0x32495441; // 0x41544932; private static final int PF_ATI2 = 0x32495441; // 0x41544932;
private static final int PF_DX10 = 0x30315844; // a DX10 format private static final int PF_DX10 = 0x30315844; // a DX10 format
@ -94,6 +96,9 @@ public class DDSLoader implements AssetLoader {
DX10DIM_TEXTURE3D = 0x4; DX10DIM_TEXTURE3D = 0x4;
private static final int DX10MISC_GENERATE_MIPS = 0x1, private static final int DX10MISC_GENERATE_MIPS = 0x1,
DX10MISC_TEXTURECUBE = 0x4; DX10MISC_TEXTURECUBE = 0x4;
private static final int DXGI_FORMAT_BC4_TYPELESS = 79;
private static final int DXGI_FORMAT_BC4_UNORM = 80;
private static final int DXGI_FORMAT_BC4_SNORM = 81;
private static final double LOG2 = Math.log(2); private static final double LOG2 = Math.log(2);
private int width; private int width;
private int height; private int height;
@ -105,9 +110,11 @@ public class DDSLoader implements AssetLoader {
private int caps2; private int caps2;
private boolean directx10; private boolean directx10;
private boolean compressed; private boolean compressed;
private boolean dxtOrRgtc;
private boolean texture3D; private boolean texture3D;
private boolean grayscaleOrAlpha; private boolean grayscaleOrAlpha;
private boolean normal; private boolean normal;
private ColorSpace colorSpace;
private Format pixelFormat; private Format pixelFormat;
private int bpp; private int bpp;
private int[] sizes; private int[] sizes;
@ -133,7 +140,8 @@ public class DDSLoader implements AssetLoader {
((TextureKey) info.getKey()).setTextureTypeHint(Texture.Type.CubeMap); ((TextureKey) info.getKey()).setTextureTypeHint(Texture.Type.CubeMap);
} }
ArrayList<ByteBuffer> data = readData(((TextureKey) info.getKey()).isFlipY()); ArrayList<ByteBuffer> data = readData(((TextureKey) info.getKey()).isFlipY());
return new Image(pixelFormat, width, height, depth, data, sizes, ColorSpace.sRGB);
return new Image(pixelFormat, width, height, depth, data, sizes, colorSpace);
} finally { } finally {
if (stream != null){ if (stream != null){
stream.close(); stream.close();
@ -145,18 +153,24 @@ public class DDSLoader implements AssetLoader {
in = new LittleEndien(stream); in = new LittleEndien(stream);
loadHeader(); loadHeader();
ArrayList<ByteBuffer> data = readData(false); ArrayList<ByteBuffer> data = readData(false);
return new Image(pixelFormat, width, height, depth, data, sizes, ColorSpace.sRGB); return new Image(pixelFormat, width, height, depth, data, sizes, colorSpace);
} }
private void loadDX10Header() throws IOException { private void loadDX10Header() throws IOException {
int dxgiFormat = in.readInt(); int dxgiFormat = in.readInt();
if (dxgiFormat == 0) { if (dxgiFormat == 0) {
pixelFormat = Format.ETC1; pixelFormat = Format.ETC1;
compressed = true;
bpp = 4; bpp = 4;
} else { } else {
pixelFormat = DXGIFormat.getJmeFormat(dxgiFormat);
if (pixelFormat == null) {
throw new IOException("Unsupported DX10 format: " + dxgiFormat); throw new IOException("Unsupported DX10 format: " + dxgiFormat);
} }
compressed = true; bpp = pixelFormat.getBitsPerPixel();
compressed = pixelFormat.isCompressed();
}
int resDim = in.readInt(); int resDim = in.readInt();
if (resDim == DX10DIM_TEXTURE3D) { if (resDim == DX10DIM_TEXTURE3D) {
@ -201,6 +215,7 @@ public class DDSLoader implements AssetLoader {
caps2 = in.readInt(); caps2 = in.readInt();
in.skipBytes(12); in.skipBytes(12);
texture3D = false; texture3D = false;
colorSpace = ColorSpace.sRGB;
if (!directx10) { if (!directx10) {
if (!is(caps1, DDSCAPS_TEXTURE)) { if (!is(caps1, DDSCAPS_TEXTURE)) {
@ -268,10 +283,12 @@ public class DDSLoader implements AssetLoader {
} else { } else {
pixelFormat = Image.Format.DXT1; pixelFormat = Image.Format.DXT1;
} }
dxtOrRgtc = true;
break; break;
case PF_DXT3: case PF_DXT3:
bpp = 8; bpp = 8;
pixelFormat = Image.Format.DXT3; pixelFormat = Image.Format.DXT3;
dxtOrRgtc = true;
break; break;
case PF_DXT5: case PF_DXT5:
bpp = 8; bpp = 8;
@ -279,17 +296,24 @@ public class DDSLoader implements AssetLoader {
if (swizzle == SWIZZLE_xGxR) { if (swizzle == SWIZZLE_xGxR) {
normal = true; normal = true;
} }
dxtOrRgtc = true;
break; break;
/*
case PF_ATI1: case PF_ATI1:
bpp = 4; bpp = 4;
pixelFormat = Image.Format.LTC; pixelFormat = Image.Format.RTC;
dxtOrRgtc = true;
break; break;
case PF_ATI2: case PF_ATI2:
bpp = 8; bpp = 8;
pixelFormat = Image.Format.LATC; pixelFormat = Image.Format.RGTC;
dxtOrRgtc = true;
break;
case PF_ETC1:
case PF_ETC_:
bpp = 4;
pixelFormat = Image.Format.ETC1;
dxtOrRgtc = false;
break; break;
*/
case PF_DX10: case PF_DX10:
compressed = false; compressed = false;
directx10 = true; directx10 = true;
@ -530,6 +554,30 @@ public class DDSLoader implements AssetLoader {
return dataBuffer; return dataBuffer;
} }
public ByteBuffer readCompressed2Dor3D(boolean flip, int totalSize) throws IOException {
logger.log(Level.FINEST, "Source image format: {0}", pixelFormat);
ByteBuffer buffer = BufferUtils.createByteBuffer(totalSize * depth);
// TODO: add support for flipping ETC1
for (int i = 0; i < depth; i++) {
int mipWidth = width;
int mipHeight = height;
for (int mip = 0; mip < mipMapCount; mip++) {
byte[] data = new byte[sizes[mip]];
in.readFully(data);
buffer.put(data);
mipWidth = Math.max(mipWidth / 2, 1);
mipHeight = Math.max(mipHeight / 2, 1);
}
}
buffer.rewind();
return buffer;
}
/** /**
* Reads a DXT compressed image from the InputStream * Reads a DXT compressed image from the InputStream
* *
@ -738,8 +786,10 @@ public class DDSLoader implements AssetLoader {
ArrayList<ByteBuffer> allMaps = new ArrayList<ByteBuffer>(); ArrayList<ByteBuffer> allMaps = new ArrayList<ByteBuffer>();
if (depth > 1 && !texture3D) { if (depth > 1 && !texture3D) {
for (int i = 0; i < depth; i++) { for (int i = 0; i < depth; i++) {
if (compressed) { if (compressed && dxtOrRgtc) {
allMaps.add(readDXT2D(flip, totalSize)); allMaps.add(readDXT2D(flip, totalSize));
} else if (compressed) {
allMaps.add(readCompressed2Dor3D(flip, totalSize));
} else if (grayscaleOrAlpha) { } else if (grayscaleOrAlpha) {
allMaps.add(readGrayscale2D(flip, totalSize)); allMaps.add(readGrayscale2D(flip, totalSize));
} else { } else {
@ -747,8 +797,10 @@ public class DDSLoader implements AssetLoader {
} }
} }
} else if (texture3D) { } else if (texture3D) {
if (compressed) { if (compressed && dxtOrRgtc) {
allMaps.add(readDXT3D(flip, totalSize)); allMaps.add(readDXT3D(flip, totalSize));
} else if (compressed) {
allMaps.add(readCompressed2Dor3D(flip, totalSize));
} else if (grayscaleOrAlpha) { } else if (grayscaleOrAlpha) {
allMaps.add(readGrayscale3D(flip, totalSize)); allMaps.add(readGrayscale3D(flip, totalSize));
} else { } else {
@ -756,8 +808,10 @@ public class DDSLoader implements AssetLoader {
} }
} else { } else {
if (compressed) { if (compressed && dxtOrRgtc) {
allMaps.add(readDXT2D(flip, totalSize)); allMaps.add(readDXT2D(flip, totalSize));
} else if (compressed) {
allMaps.add(readCompressed2Dor3D(flip, totalSize));
} else if (grayscaleOrAlpha) { } else if (grayscaleOrAlpha) {
allMaps.add(readGrayscale2D(flip, totalSize)); allMaps.add(readGrayscale2D(flip, totalSize));
} else { } else {
@ -822,7 +876,7 @@ public class DDSLoader implements AssetLoader {
buf.append((char) (value & 0xFF)); buf.append((char) (value & 0xFF));
buf.append((char) ((value & 0xFF00) >> 8)); buf.append((char) ((value & 0xFF00) >> 8));
buf.append((char) ((value & 0xFF0000) >> 16)); buf.append((char) ((value & 0xFF0000) >> 16));
buf.append((char) ((value & 0xFF00000) >> 24)); buf.append((char) ((value & 0xFF000000) >> 24));
return buf.toString(); return buf.toString();
} }

@ -213,20 +213,18 @@ public class DXTFlipper {
case DXT5: case DXT5:
type = 3; type = 3;
break; break;
/* case RGTC:
case LATC:
type = 4; type = 4;
break; break;
case LTC: case RTC:
type = 5; type = 5;
break; break;
*/
default: default:
throw new IllegalArgumentException(); throw new IllegalArgumentException();
} }
// DXT1 uses 8 bytes per block, // DXT1 uses 8 bytes per block,
// DXT3, DXT5, LATC use 16 bytes per block // DXT3, DXT5, RGTC use 16 bytes per block
int bpb = type == 1 || type == 5 ? 8 : 16; int bpb = type == 1 || type == 5 ? 8 : 16;
ByteBuffer retImg = BufferUtils.createByteBuffer(blocksX * blocksY * bpb); ByteBuffer retImg = BufferUtils.createByteBuffer(blocksX * blocksY * bpb);

Loading…
Cancel
Save