diff --git a/jme3-android/src/main/java/com/jme3/renderer/android/OGLESShaderRenderer.java b/jme3-android/src/main/java/com/jme3/renderer/android/OGLESShaderRenderer.java index 632df4a1b..7c90e0969 100644 --- a/jme3-android/src/main/java/com/jme3/renderer/android/OGLESShaderRenderer.java +++ b/jme3-android/src/main/java/com/jme3/renderer/android/OGLESShaderRenderer.java @@ -2536,4 +2536,8 @@ public class OGLESShaderRenderer implements Renderer { public void setMainFrameBufferSrgb(boolean srgb) { //TODO once opglES3.0 is supported maybe.... } + + public void setLinearizeSrgbImages(boolean linearize) { + //TODO once opglES3.0 is supported maybe.... + } } diff --git a/jme3-core/src/main/java/com/jme3/asset/DesktopAssetManager.java b/jme3-core/src/main/java/com/jme3/asset/DesktopAssetManager.java index 083f85e25..9006e8c39 100644 --- a/jme3-core/src/main/java/com/jme3/asset/DesktopAssetManager.java +++ b/jme3-core/src/main/java/com/jme3/asset/DesktopAssetManager.java @@ -342,7 +342,7 @@ public class DesktopAssetManager implements AssetManager { return loadAsset(new AssetKey(name)); } - public Texture loadTexture(TextureKey key){ + public Texture loadTexture(TextureKey key){ return (Texture) loadAsset(key); } diff --git a/jme3-core/src/main/java/com/jme3/renderer/Caps.java b/jme3-core/src/main/java/com/jme3/renderer/Caps.java index 55a94c6ee..80c5b976a 100644 --- a/jme3-core/src/main/java/com/jme3/renderer/Caps.java +++ b/jme3-core/src/main/java/com/jme3/renderer/Caps.java @@ -231,7 +231,12 @@ public enum Caps { /** * Supports FBO with Depth24Stencil8 image format */ - PackedDepthStencilBuffer; + PackedDepthStencilBuffer, + + /** + * Supports sRGB framebuffers and sRGB texture format + */ + Srgb; /** * Returns true if given the renderer capabilities, the texture diff --git a/jme3-core/src/main/java/com/jme3/renderer/Renderer.java b/jme3-core/src/main/java/com/jme3/renderer/Renderer.java index 8480da616..bef528f51 100644 --- a/jme3-core/src/main/java/com/jme3/renderer/Renderer.java +++ b/jme3-core/src/main/java/com/jme3/renderer/Renderer.java @@ -335,4 +335,31 @@ public interface Renderer { * @seealso Caps#Srgb */ public void setMainFrameBufferSrgb(boolean srgb); + + /** + * If enabled, all {@link Image images} with the {@link Image#setSRGB(boolean) sRGB flag} + * set shall undergo an sRGB to linear RGB color conversion when read by a shader. + * + * The conversion is performed for the following formats: + * - {@link Format#RGB8} + * - {@link Format#RGBA8} + * - {@link Format#Luminance8} + * - {@link Format#LuminanceAlpha8} + * - {@link Format#DXT1} + * - {@link Format#DXT1a} + * - {@link Format#DXT3} + * - {@link Format#DXT5} + * + * For all other formats, no conversion is performed. + * + * If this option is toggled at runtime, textures must be reloaded for the change to take effect. + * + * @throws RendererException If the GPU hardware does not support sRGB. + * + * @param linearize If sRGB images undergo sRGB -> linear conversion prior to rendering. + * + * @seealso Caps#Srgb + */ + public void setLinearizeSrgbImages(boolean linearize); + } diff --git a/jme3-core/src/main/java/com/jme3/system/AppSettings.java b/jme3-core/src/main/java/com/jme3/system/AppSettings.java index e9a5ca553..69cbb8760 100644 --- a/jme3-core/src/main/java/com/jme3/system/AppSettings.java +++ b/jme3-core/src/main/java/com/jme3/system/AppSettings.java @@ -143,7 +143,7 @@ public final class AppSettings extends HashMap { defaults.put("SettingsDialogImage", "/com/jme3/app/Monkey.png"); defaults.put("MinHeight", 0); defaults.put("MinWidth", 0); - defaults.put("GammaCorrection", true); + defaults.put("GammaCorrection", false); // defaults.put("Icons", null); } diff --git a/jme3-core/src/main/java/com/jme3/system/JmeVersion.java b/jme3-core/src/main/java/com/jme3/system/JmeVersion.java index 98cfbc354..9acdf3655 100644 --- a/jme3-core/src/main/java/com/jme3/system/JmeVersion.java +++ b/jme3-core/src/main/java/com/jme3/system/JmeVersion.java @@ -32,7 +32,7 @@ package com.jme3.system; public class JmeVersion { - private static final String FULL_NAME = "jMonkeyEngine 3.x"; + private static final String FULL_NAME = "jMonkeyEngine 3.0.10 (pre-alpha-svn)"; public static String getFullName() { return FULL_NAME; diff --git a/jme3-core/src/main/java/com/jme3/system/NullRenderer.java b/jme3-core/src/main/java/com/jme3/system/NullRenderer.java index ce8e1a6c1..6a93c8d34 100644 --- a/jme3-core/src/main/java/com/jme3/system/NullRenderer.java +++ b/jme3-core/src/main/java/com/jme3/system/NullRenderer.java @@ -155,4 +155,7 @@ public class NullRenderer implements Renderer { public void setMainFrameBufferSrgb(boolean srgb) { } + public void setLinearizeSrgbImages(boolean linearize) { + } + } diff --git a/jme3-core/src/main/java/com/jme3/texture/FrameBuffer.java b/jme3-core/src/main/java/com/jme3/texture/FrameBuffer.java index bb5317b34..ca00de89d 100644 --- a/jme3-core/src/main/java/com/jme3/texture/FrameBuffer.java +++ b/jme3-core/src/main/java/com/jme3/texture/FrameBuffer.java @@ -79,6 +79,7 @@ public class FrameBuffer extends NativeObject { private ArrayList colorBufs = new ArrayList(); private RenderBuffer depthBuf = null; private int colorBufIndex = 0; + private boolean srgb; /** * RenderBuffer represents either a texture or a @@ -506,4 +507,42 @@ public class FrameBuffer extends NativeObject { public long getUniqueId() { return ((long)OBJTYPE_FRAMEBUFFER << 32) | ((long)id); } + + /** + * Specifies that the color values stored in this framebuffer are in SRGB + * format. + * + * The FrameBuffer must have a texture attached with the flag + * {@link Image#isSrgb()} set to true. + * + * The Renderer must expose the {@link Caps#Srgb sRGB pipeline} capability + * for this option to take any effect. + * + * Rendering operations performed on this framebuffer shall undergo a linear + * -> sRGB color space conversion when this flag is enabled. If + * {@link RenderState#getBlendMode() blending} is enabled, it will be + * performed in linear space by first decoding the stored sRGB pixel values + * into linear, combining with the shader result, and then converted back to + * sRGB upon being written into the framebuffer. + * + * @param srgb If the framebuffer color values should be stored in sRGB + * color space. + * + * @throws InvalidStateException If the texture attached to this framebuffer + * is not sRGB. + */ + public void setSrgb(boolean srgb) { + this.srgb = srgb; + } + + /** + * Determines if this framebuffer contains SRGB data. + * + * @returns True if the framebuffer color values are in SRGB space, false if + * in linear space. + */ + public boolean isSrgb() { + return srgb; + } + } diff --git a/jme3-core/src/main/java/com/jme3/texture/Image.java b/jme3-core/src/main/java/com/jme3/texture/Image.java index 5ffd92b88..63f25ffb3 100644 --- a/jme3-core/src/main/java/com/jme3/texture/Image.java +++ b/jme3-core/src/main/java/com/jme3/texture/Image.java @@ -334,6 +334,7 @@ public class Image extends NativeObject implements Savable /*, Cloneable*/ { protected ArrayList data; protected transient Object efficientData; protected int multiSamples = 1; + protected boolean srgb; // protected int mipOffset = 0; // attributes relating to GL object @@ -447,9 +448,12 @@ public class Image extends NativeObject implements Savable /*, Cloneable*/ { * the image data. * @param mipMapSizes * the array of mipmap sizes, or null for no mipmaps. + * @param isSrgb + * true if the image in is sRGB color space false for linear + *color space */ public Image(Format format, int width, int height, int depth, ArrayList data, - int[] mipMapSizes) { + int[] mipMapSizes, boolean isSrgb) { this(); @@ -466,8 +470,25 @@ public class Image extends NativeObject implements Savable /*, Cloneable*/ { this.data = data; this.depth = depth; this.mipMapSizes = mipMapSizes; + this.srgb = isSrgb; } + /** + * @see {@link #Image(com.jme3.texture.Image.Format, int, int, int, java.util.ArrayList, int[], boolean)} + * @param format + * @param width + * @param height + * @param depth + * @param data + * @param mipMapSizes + * @deprecated use {@link #Image(com.jme3.texture.Image.Format, int, int, int, java.util.ArrayList, int[], boolean)} + */ + @Deprecated + public Image(Format format, int width, int height, int depth, ArrayList data, + int[] mipMapSizes) { + this(format, width, height, depth, data, mipMapSizes, false); + } + /** * Constructor instantiates a new Image object. The * attributes of the image are defined during construction. @@ -482,9 +503,12 @@ public class Image extends NativeObject implements Savable /*, Cloneable*/ { * the image data. * @param mipMapSizes * the array of mipmap sizes, or null for no mipmaps. + * @param isSrgb + * true if the image in is sRGB color space false for linear + * color space */ public Image(Format format, int width, int height, ByteBuffer data, - int[] mipMapSizes) { + int[] mipMapSizes, boolean isSrgb) { this(); @@ -503,8 +527,24 @@ public class Image extends NativeObject implements Savable /*, Cloneable*/ { this.data.add(data); } this.mipMapSizes = mipMapSizes; + this.srgb = isSrgb; } - + + /** + * @see {@link #Image(com.jme3.texture.Image.Format, int, int, java.nio.ByteBuffer, int[], boolean)} + * @param format + * @param width + * @param height + * @param data + * @param mipMapSizes + * @deprecated use {@link #Image(com.jme3.texture.Image.Format, int, int, java.nio.ByteBuffer, int[], boolean)} + */ + @Deprecated + public Image(Format format, int width, int height, ByteBuffer data, + int[] mipMapSizes) { + this(format, width, height, data, mipMapSizes, false); + } + /** * Constructor instantiates a new Image object. The * attributes of the image are defined during construction. @@ -517,9 +557,26 @@ public class Image extends NativeObject implements Savable /*, Cloneable*/ { * the height of the image. * @param data * the image data. + * @param isSrgb + * true if the image in is sRGB color space false for linear + * color space + */ + public Image(Format format, int width, int height, int depth, ArrayList data, boolean isSrgb) { + this(format, width, height, depth, data, null, isSrgb); + } + + /** + * @see {@link #Image(com.jme3.texture.Image.Format, int, int, int, java.util.ArrayList, boolean)} + * @param format + * @param width + * @param height + * @param depth + * @param data + * @deprecated use {@link #Image(com.jme3.texture.Image.Format, int, int, int, java.util.ArrayList, boolean)} */ + @Deprecated public Image(Format format, int width, int height, int depth, ArrayList data) { - this(format, width, height, depth, data, null); + this(format, width, height, depth, data, false); } /** @@ -534,11 +591,29 @@ public class Image extends NativeObject implements Savable /*, Cloneable*/ { * the height of the image. * @param data * the image data. + * @param isSrgb + * true if the image in is sRGB color space false for linear + * color space + */ + public Image(Format format, int width, int height, ByteBuffer data, boolean isSrgb) { + this(format, width, height, data, null, isSrgb); + } + + + /** + * @see {@link #Image(com.jme3.texture.Image.Format, int, int, java.nio.ByteBuffer, boolean)} + * @param format + * @param width + * @param height + * @param data + * @deprecated use {@link #Image(com.jme3.texture.Image.Format, int, int, java.nio.ByteBuffer, boolean)} */ + @Deprecated public Image(Format format, int width, int height, ByteBuffer data) { - this(format, width, height, data, null); + this(format, width, height, data, null, false); } + /** * @return The number of samples (for multisampled textures). * @see Image#setMultiSamples(int) @@ -788,6 +863,44 @@ public class Image extends NativeObject implements Savable /*, Cloneable*/ { public int[] getMipMapSizes() { return mipMapSizes; } + + /** + * image loader is responsible for setting this flag based on the color + * space in which the image has been encoded with. In the majority of cases, + * this flag will be on by default since many image formats do not contain + * any color space information. + * + * The material loader may override this flag to false if it determines that + * such conversion must not be performed, for example, when loading normal + * maps. + * + * @param srgb True to enable SRGB -> linear conversion, false otherwise. + * + * @seealso Renderer#setLinearizeSrgbImages(boolean) + * + * @throws InvalidStateException If the image format does not support SRGB + * -> linear conversion. + */ + public void setSrgb(boolean srgb) { + this.srgb = srgb; + } + + /** + * Specifies that this image is an SRGB image and therefore must undergo an + * sRGB -> linear RGB color conversion prior to being read by a shader and + * with the {@link Renderer#setLinearizeSrgbImages(boolean)} option is + * enabled. + * + * This option is only supported for the 8-bit color and grayscale image + * formats. Determines if the image is in SRGB color space or not. + * + * @return True, if the image is an SRGB image, false if it is linear RGB. + * + * @seealso Renderer#setLinearizeSrgbImages(boolean) + */ + public boolean isSrgb() { + return srgb; + } @Override public String toString(){ diff --git a/jme3-core/src/main/java/com/jme3/texture/Texture2D.java b/jme3-core/src/main/java/com/jme3/texture/Texture2D.java index 2533d2ce0..55be82277 100644 --- a/jme3-core/src/main/java/com/jme3/texture/Texture2D.java +++ b/jme3-core/src/main/java/com/jme3/texture/Texture2D.java @@ -76,7 +76,7 @@ public class Texture2D extends Texture { * @param format */ public Texture2D(int width, int height, Image.Format format){ - this(new Image(format, width, height, null)); + this(new Image(format, width, height, null, false)); } /** @@ -91,7 +91,7 @@ public class Texture2D extends Texture { * @param numSamples */ public Texture2D(int width, int height, int numSamples, Image.Format format){ - this(new Image(format, width, height, null)); + this(new Image(format, width, height, null, false)); getImage().setMultiSamples(numSamples); } diff --git a/jme3-core/src/main/java/com/jme3/texture/Texture3D.java b/jme3-core/src/main/java/com/jme3/texture/Texture3D.java index 106351ece..583e546ee 100644 --- a/jme3-core/src/main/java/com/jme3/texture/Texture3D.java +++ b/jme3-core/src/main/java/com/jme3/texture/Texture3D.java @@ -78,7 +78,7 @@ public class Texture3D extends Texture { * @param format */ public Texture3D(int width, int height, int depth, Image.Format format) { - this(new Image(format, width, height, depth, null)); + this(new Image(format, width, height, depth, null, false)); } /** @@ -93,7 +93,7 @@ public class Texture3D extends Texture { * @param numSamples */ public Texture3D(int width, int height, int depth, int numSamples, Image.Format format) { - this(new Image(format, width, height, depth, null)); + this(new Image(format, width, height, depth, null, false)); getImage().setMultiSamples(numSamples); } diff --git a/jme3-core/src/main/java/com/jme3/texture/TextureArray.java b/jme3-core/src/main/java/com/jme3/texture/TextureArray.java index 32cf376dd..b1251eb42 100644 --- a/jme3-core/src/main/java/com/jme3/texture/TextureArray.java +++ b/jme3-core/src/main/java/com/jme3/texture/TextureArray.java @@ -70,7 +70,8 @@ public class TextureArray extends Texture { int width = images.get(0).getWidth(); int height = images.get(0).getHeight(); Format format = images.get(0).getFormat(); - Image arrayImage = new Image(format, width, height, null); + boolean isSRGB = images.get(0).isSrgb(); + Image arrayImage = new Image(format, width, height, null, isSRGB); for (Image img : images) { if (img.getHeight() != height || img.getWidth() != width) { diff --git a/jme3-core/src/main/java/com/jme3/texture/TextureCubeMap.java b/jme3-core/src/main/java/com/jme3/texture/TextureCubeMap.java index 22debeeb1..e1cc52b73 100644 --- a/jme3-core/src/main/java/com/jme3/texture/TextureCubeMap.java +++ b/jme3-core/src/main/java/com/jme3/texture/TextureCubeMap.java @@ -88,7 +88,7 @@ public class TextureCubeMap extends Texture { for(int i = 0; i < layerCount; i++) { layers.add(null); } - Image image = new Image(format, width, height, 0, layers); + Image image = new Image(format, width, height, 0, layers, false); return image; } diff --git a/jme3-core/src/main/java/com/jme3/util/PlaceholderAssets.java b/jme3-core/src/main/java/com/jme3/util/PlaceholderAssets.java index 8a2630e62..f34bb1ff0 100644 --- a/jme3-core/src/main/java/com/jme3/util/PlaceholderAssets.java +++ b/jme3-core/src/main/java/com/jme3/util/PlaceholderAssets.java @@ -73,7 +73,7 @@ public class PlaceholderAssets { public static Image getPlaceholderImage(){ ByteBuffer tempData = BufferUtils.createByteBuffer(3 * 4 * 4); tempData.put(imageData).flip(); - return new Image(Format.RGB8, 4, 4, tempData); + return new Image(Format.RGB8, 4, 4, tempData, null, false); } public static Material getPlaceholderMaterial(AssetManager assetManager){ diff --git a/jme3-core/src/main/java/com/jme3/util/SkyFactory.java b/jme3-core/src/main/java/com/jme3/util/SkyFactory.java index 9d31ebba0..20d9fc39f 100644 --- a/jme3-core/src/main/java/com/jme3/util/SkyFactory.java +++ b/jme3-core/src/main/java/com/jme3/util/SkyFactory.java @@ -243,7 +243,7 @@ public class SkyFactory { checkImagesForCubeMap(westImg, eastImg, northImg, southImg, upImg, downImg); - Image cubeImage = new Image(westImg.getFormat(), westImg.getWidth(), westImg.getHeight(), null); + Image cubeImage = new Image(westImg.getFormat(), westImg.getWidth(), westImg.getHeight(), null, westImg.isSrgb()); cubeImage.addData(westImg.getData(0)); cubeImage.addData(eastImg.getData(0)); diff --git a/jme3-core/src/plugins/java/com/jme3/material/plugins/J3MLoader.java b/jme3-core/src/plugins/java/com/jme3/material/plugins/J3MLoader.java index e12a0759f..46e26ac51 100644 --- a/jme3-core/src/plugins/java/com/jme3/material/plugins/J3MLoader.java +++ b/jme3-core/src/plugins/java/com/jme3/material/plugins/J3MLoader.java @@ -152,14 +152,14 @@ public class J3MLoader implements AssetLoader { if (tex != null){ if (repeat){ tex.setWrap(WrapMode.Repeat); - } + } }else{ tex = new Texture2D(PlaceholderAssets.getPlaceholderImage()); if (repeat){ tex.setWrap(WrapMode.Repeat); } tex.setKey(texKey); - } + } return tex; }else{ String[] split = value.trim().split(whitespacePattern); diff --git a/jme3-core/src/plugins/java/com/jme3/texture/plugins/DDSLoader.java b/jme3-core/src/plugins/java/com/jme3/texture/plugins/DDSLoader.java index 1feb42eb7..1bae738ae 100644 --- a/jme3-core/src/plugins/java/com/jme3/texture/plugins/DDSLoader.java +++ b/jme3-core/src/plugins/java/com/jme3/texture/plugins/DDSLoader.java @@ -132,7 +132,7 @@ public class DDSLoader implements AssetLoader { ((TextureKey) info.getKey()).setTextureTypeHint(Type.CubeMap); } ArrayList data = readData(((TextureKey) info.getKey()).isFlipY()); - return new Image(pixelFormat, width, height, depth, data, sizes); + return new Image(pixelFormat, width, height, depth, data, sizes, true); } finally { if (stream != null){ stream.close(); @@ -144,7 +144,7 @@ public class DDSLoader implements AssetLoader { in = new LittleEndien(stream); loadHeader(); ArrayList data = readData(false); - return new Image(pixelFormat, width, height, depth, data, sizes); + return new Image(pixelFormat, width, height, depth, data, sizes, true); } private void loadDX10Header() throws IOException { diff --git a/jme3-core/src/plugins/java/com/jme3/texture/plugins/HDRLoader.java b/jme3-core/src/plugins/java/com/jme3/texture/plugins/HDRLoader.java index 8a88e2d7f..ae2205e29 100644 --- a/jme3-core/src/plugins/java/com/jme3/texture/plugins/HDRLoader.java +++ b/jme3-core/src/plugins/java/com/jme3/texture/plugins/HDRLoader.java @@ -308,7 +308,8 @@ public class HDRLoader implements AssetLoader { in.close(); dataStore.rewind(); - return new Image(pixelFormat, width, height, dataStore); + //TODO, HDR color space? considered linear here + return new Image(pixelFormat, width, height, dataStore, false); } public Object load(AssetInfo info) throws IOException { diff --git a/jme3-core/src/plugins/java/com/jme3/texture/plugins/PFMLoader.java b/jme3-core/src/plugins/java/com/jme3/texture/plugins/PFMLoader.java index a2a454206..d641a5bc4 100644 --- a/jme3-core/src/plugins/java/com/jme3/texture/plugins/PFMLoader.java +++ b/jme3-core/src/plugins/java/com/jme3/texture/plugins/PFMLoader.java @@ -129,7 +129,7 @@ public class PFMLoader implements AssetLoader { } imageData.rewind(); - return new Image(format, width, height, imageData); + return new Image(format, width, height, imageData, null, false); } public Object load(AssetInfo info) throws IOException { diff --git a/jme3-core/src/tools/java/jme3tools/optimize/TextureAtlas.java b/jme3-core/src/tools/java/jme3tools/optimize/TextureAtlas.java index c7a032414..55000eefc 100644 --- a/jme3-core/src/tools/java/jme3tools/optimize/TextureAtlas.java +++ b/jme3-core/src/tools/java/jme3tools/optimize/TextureAtlas.java @@ -273,6 +273,9 @@ public class TextureAtlas { images = new HashMap(); } byte[] image = images.get(mapName); + + //FIXME this is not accounting for color space. + //Texture Atlas should linearize the data if the source image isSRGB if (image == null) { image = new byte[atlasWidth * atlasHeight * 4]; images.put(mapName, image); @@ -351,7 +354,7 @@ public class TextureAtlas { if (clazz == null) { return null; } - Image newImage = new Image(format, source.getWidth(), source.getHeight(), BufferUtils.createByteBuffer(source.getWidth() * source.getHeight() * 4)); + Image newImage = new Image(format, source.getWidth(), source.getHeight(), BufferUtils.createByteBuffer(source.getWidth() * source.getHeight() * 4), null, false); clazz.getMethod("convert", Image.class, Image.class).invoke(clazz.newInstance(), source, newImage); return newImage; } catch (InstantiationException ex) { @@ -398,7 +401,7 @@ public class TextureAtlas { } byte[] image = images.get(mapName); if (image != null) { - Texture2D tex = new Texture2D(new Image(format, atlasWidth, atlasHeight, BufferUtils.createByteBuffer(image))); + Texture2D tex = new Texture2D(new Image(format, atlasWidth, atlasHeight, BufferUtils.createByteBuffer(image), null, true)); tex.setMagFilter(Texture.MagFilter.Bilinear); tex.setMinFilter(Texture.MinFilter.BilinearNearestMipMap); tex.setWrap(Texture.WrapMode.Clamp); diff --git a/jme3-desktop/src/main/java/com/jme3/texture/plugins/AWTLoader.java b/jme3-desktop/src/main/java/com/jme3/texture/plugins/AWTLoader.java index 9b5c8f1da..d0d598875 100644 --- a/jme3-desktop/src/main/java/com/jme3/texture/plugins/AWTLoader.java +++ b/jme3-desktop/src/main/java/com/jme3/texture/plugins/AWTLoader.java @@ -112,7 +112,7 @@ public class AWTLoader implements AssetLoader { ByteBuffer data1 = BufferUtils.createByteBuffer(img.getWidth()*img.getHeight()*4); data1.put(dataBuf1); - return new Image(Format.ABGR8, width, height, data1); + return new Image(Format.ABGR8, width, height, data1, null, true); case BufferedImage.TYPE_3BYTE_BGR: // most common in JPEG images byte[] dataBuf2 = (byte[]) extractImageData(img); if (flipY) @@ -120,14 +120,14 @@ public class AWTLoader implements AssetLoader { ByteBuffer data2 = BufferUtils.createByteBuffer(img.getWidth()*img.getHeight()*3); data2.put(dataBuf2); - return new Image(Format.BGR8, width, height, data2); + return new Image(Format.BGR8, width, height, data2, null, true); case BufferedImage.TYPE_BYTE_GRAY: // grayscale fonts byte[] dataBuf3 = (byte[]) extractImageData(img); if (flipY) flipImage(dataBuf3, width, height, 8); ByteBuffer data3 = BufferUtils.createByteBuffer(img.getWidth()*img.getHeight()); data3.put(dataBuf3); - return new Image(Format.Luminance8, width, height, data3); + return new Image(Format.Luminance8, width, height, data3, null, true); case BufferedImage.TYPE_USHORT_GRAY: // grayscale heightmap short[] dataBuf4 = (short[]) extractImageData(img); if (flipY) @@ -135,7 +135,7 @@ public class AWTLoader implements AssetLoader { ByteBuffer data4 = BufferUtils.createByteBuffer(img.getWidth()*img.getHeight()*2); data4.asShortBuffer().put(dataBuf4); - return new Image(Format.Luminance16, width, height, data4); + return new Image(Format.Luminance16, width, height, data4, null, true); default: break; } @@ -158,10 +158,10 @@ public class AWTLoader implements AssetLoader { } } data.flip(); - return new Image(Format.RGB8, width, height, data); + return new Image(Format.RGB8, width, height, data, null, true); }else{ ByteBuffer data = BufferUtils.createByteBuffer(img.getWidth()*img.getHeight()*4); - // no alpha + // alpha for (int y = 0; y < height; y++){ for (int x = 0; x < width; x++){ int ny = y; @@ -178,7 +178,7 @@ public class AWTLoader implements AssetLoader { } } data.flip(); - return new Image(Format.RGBA8, width, height, data); + return new Image(Format.RGBA8, width, height, data, null, true); } } diff --git a/jme3-examples/src/main/java/jme3test/bullet/TestFancyCar.java b/jme3-examples/src/main/java/jme3test/bullet/TestFancyCar.java index 6249f601b..ca64536de 100644 --- a/jme3-examples/src/main/java/jme3test/bullet/TestFancyCar.java +++ b/jme3-examples/src/main/java/jme3test/bullet/TestFancyCar.java @@ -31,6 +31,7 @@ */ package jme3test.bullet; +import com.jme3.app.SettingsDialog; import com.jme3.app.SimpleApplication; import com.jme3.bounding.BoundingBox; import com.jme3.bullet.BulletAppState; @@ -51,6 +52,7 @@ import com.jme3.scene.Geometry; import com.jme3.scene.Node; import com.jme3.scene.Spatial; import com.jme3.shadow.BasicShadowRenderer; +import com.jme3.system.AppSettings; public class TestFancyCar extends SimpleApplication implements ActionListener { @@ -64,7 +66,7 @@ public class TestFancyCar extends SimpleApplication implements ActionListener { private Node carNode; public static void main(String[] args) { - TestFancyCar app = new TestFancyCar(); + TestFancyCar app = new TestFancyCar(); app.start(); } @@ -91,7 +93,7 @@ public class TestFancyCar extends SimpleApplication implements ActionListener { if (settings.getRenderer().startsWith("LWJGL")) { BasicShadowRenderer bsr = new BasicShadowRenderer(assetManager, 512); bsr.setDirection(new Vector3f(-0.5f, -0.3f, -0.3f).normalizeLocal()); - viewPort.addProcessor(bsr); + // viewPort.addProcessor(bsr); } cam.setFrustumFar(150f); flyCam.setMoveSpeed(10); @@ -107,7 +109,7 @@ public class TestFancyCar extends SimpleApplication implements ActionListener { dl = new DirectionalLight(); dl.setDirection(new Vector3f(0.5f, -0.1f, 0.3f).normalizeLocal()); - rootNode.addLight(dl); + // rootNode.addLight(dl); } private PhysicsSpace getPhysicsSpace() { diff --git a/jme3-examples/src/main/java/jme3test/texture/TestImageRaster.java b/jme3-examples/src/main/java/jme3test/texture/TestImageRaster.java index da27ddf40..cc6de2153 100644 --- a/jme3-examples/src/main/java/jme3test/texture/TestImageRaster.java +++ b/jme3-examples/src/main/java/jme3test/texture/TestImageRaster.java @@ -27,7 +27,7 @@ public class TestImageRaster extends SimpleApplication { int width = image.getWidth(); int height = image.getHeight(); ByteBuffer data = BufferUtils.createByteBuffer( (int)Math.ceil(newFormat.getBitsPerPixel() / 8.0) * width * height); - Image convertedImage = new Image(newFormat, width, height, data); + Image convertedImage = new Image(newFormat, width, height, data,null, image.isSrgb()); ImageRaster sourceReader = ImageRaster.create(image); ImageRaster targetWriter = ImageRaster.create(convertedImage); @@ -66,7 +66,7 @@ public class TestImageRaster extends SimpleApplication { } private Image createTestImage() { - Image testImage = new Image(Format.BGR8, 4, 3, BufferUtils.createByteBuffer(4 * 4 * 3)); + Image testImage = new Image(Format.BGR8, 4, 3, BufferUtils.createByteBuffer(4 * 4 * 3), null, false); ImageRaster io = ImageRaster.create(testImage); io.setPixel(0, 0, ColorRGBA.Black); diff --git a/jme3-examples/src/main/java/jme3test/texture/TestTexture3D.java b/jme3-examples/src/main/java/jme3test/texture/TestTexture3D.java index 7b0ffc104..9d874de48 100644 --- a/jme3-examples/src/main/java/jme3test/texture/TestTexture3D.java +++ b/jme3-examples/src/main/java/jme3test/texture/TestTexture3D.java @@ -125,6 +125,6 @@ public class TestTexture3D extends SimpleApplication { } bb.rewind(); data.add(bb); - return new Texture3D(new Image(Format.RGB8, 10, 10, 10, data)); + return new Texture3D(new Image(Format.RGB8, 10, 10, 10, data, null, false)); } } \ No newline at end of file diff --git a/jme3-ios/src/main/java/com/jme3/renderer/ios/IGLESShaderRenderer.java b/jme3-ios/src/main/java/com/jme3/renderer/ios/IGLESShaderRenderer.java index 72e9d9608..351b92e67 100644 --- a/jme3-ios/src/main/java/com/jme3/renderer/ios/IGLESShaderRenderer.java +++ b/jme3-ios/src/main/java/com/jme3/renderer/ios/IGLESShaderRenderer.java @@ -2576,4 +2576,8 @@ public class IGLESShaderRenderer implements Renderer { public void setMainFrameBufferSrgb(boolean srgb) { } + + public void setLinearizeSrgbImages(boolean linearize) { + + } } \ No newline at end of file diff --git a/jme3-jogl/src/main/java/com/jme3/renderer/jogl/JoglGL1Renderer.java b/jme3-jogl/src/main/java/com/jme3/renderer/jogl/JoglGL1Renderer.java index 449d94b82..e31b1e0d4 100644 --- a/jme3-jogl/src/main/java/com/jme3/renderer/jogl/JoglGL1Renderer.java +++ b/jme3-jogl/src/main/java/com/jme3/renderer/jogl/JoglGL1Renderer.java @@ -856,7 +856,7 @@ public class JoglGL1Renderer implements GL1Renderer { TextureUtil.uploadTexture(img, target, i, 0, tdc); } } else {*/ - TextureUtil.uploadTexture(img, target, 0, 0); + TextureUtil.uploadTexture(img, target, 0, 0,false); //} img.clearUpdateNeeded(); @@ -908,7 +908,7 @@ public class JoglGL1Renderer implements GL1Renderer { public void modifyTexture(Texture tex, Image pixels, int x, int y) { setTexture(0, tex); - TextureUtil.uploadSubTexture(pixels, convertTextureType(tex.getType()), 0, x, y); + TextureUtil.uploadSubTexture(pixels, convertTextureType(tex.getType()), 0, x, y, false); } private void clearTextureUnits() { @@ -1261,4 +1261,7 @@ public class JoglGL1Renderer implements GL1Renderer { public void setMainFrameBufferSrgb(boolean srgb) { } + + public void setLinearizeSrgbImages(boolean linearize) { + } } diff --git a/jme3-jogl/src/main/java/com/jme3/renderer/jogl/JoglRenderer.java b/jme3-jogl/src/main/java/com/jme3/renderer/jogl/JoglRenderer.java index 89daea47a..e57c2c0c9 100644 --- a/jme3-jogl/src/main/java/com/jme3/renderer/jogl/JoglRenderer.java +++ b/jme3-jogl/src/main/java/com/jme3/renderer/jogl/JoglRenderer.java @@ -119,6 +119,7 @@ public class JoglRenderer implements Renderer { private final Statistics statistics = new Statistics(); private int vpX, vpY, vpW, vpH; private int clipX, clipY, clipW, clipH; + private boolean linearizeSrgbImages; public JoglRenderer() { } @@ -415,6 +416,11 @@ public class JoglRenderer implements Renderer { caps.add(Caps.Multisample); } + //supports sRGB pipeline + if (gl.isExtensionAvailable("GL_ARB_framebuffer_sRGB") && gl.isExtensionAvailable("GL_EXT_texture_sRGB")){ + caps.add(Caps.Srgb); + } + logger.log(Level.FINE, "Caps: {0}", caps); } @@ -1443,7 +1449,7 @@ public class JoglRenderer implements Renderer { + ":" + fb.getHeight() + " is not supported."); } - TextureUtil.GLImageFormat glFmt = TextureUtil.getImageFormatWithError(rb.getFormat()); + TextureUtil.GLImageFormat glFmt = TextureUtil.getImageFormatWithError(rb.getFormat(), fb.isSrgb()); if (fb.getSamples() > 1 && gl.isExtensionAvailable("GL_EXT_framebuffer_multisample") && gl.isGL2GL3()/*&& gl.isFunctionAvailable("glRenderbufferStorageMultisample")*/) { @@ -1995,19 +2001,19 @@ public class JoglRenderer implements Renderer { return; } for (int i = 0; i < 6; i++) { - TextureUtil.uploadTexture(img, GL.GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, i, 0); + TextureUtil.uploadTexture(img, GL.GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, i, 0, linearizeSrgbImages); } } else if (target == GL.GL_TEXTURE_2D_ARRAY) { List data = img.getData(); // -1 index specifies prepare data for 2D Array - TextureUtil.uploadTexture(img, target, -1, 0); + TextureUtil.uploadTexture(img, target, -1, 0, linearizeSrgbImages); for (int i = 0; i < data.size(); i++) { // upload each slice of 2D array in turn // this time with the appropriate index - TextureUtil.uploadTexture(img, target, i, 0); + TextureUtil.uploadTexture(img, target, i, 0, linearizeSrgbImages); } } else { - TextureUtil.uploadTexture(img, target, 0, 0); + TextureUtil.uploadTexture(img, target, 0, 0, linearizeSrgbImages); } if (img.getMultiSamples() != imageSamples) { @@ -2066,7 +2072,7 @@ public class JoglRenderer implements Renderer { public void modifyTexture(Texture tex, Image pixels, int x, int y) { setTexture(0, tex); - TextureUtil.uploadSubTexture(pixels, convertTextureType(tex.getType(), tex.getImage().getMultiSamples(), -1), 0, x, y); + TextureUtil.uploadSubTexture(pixels, convertTextureType(tex.getType(), tex.getImage().getMultiSamples(), -1), 0, x, y, linearizeSrgbImages); } public void clearTextureUnits() { @@ -2592,7 +2598,7 @@ public class JoglRenderer implements Renderer { public void setMainFrameBufferSrgb(boolean srgb) { //Gamma correction - if(srgb && GLContext.getCurrent().isExtensionAvailable("GL_ARB_framebuffer_sRGB")){ + if(srgb && caps.contains(Caps.Srgb)){ GLContext.getCurrentGL().glEnable(GL3.GL_FRAMEBUFFER_SRGB); logger.log(Level.FINER, "SRGB FrameBuffer enabled (Gamma Correction)"); }else{ @@ -2600,4 +2606,8 @@ public class JoglRenderer implements Renderer { } } + + public void setLinearizeSrgbImages(boolean linearize) { + linearizeSrgbImages = linearize; + } } diff --git a/jme3-jogl/src/main/java/com/jme3/renderer/jogl/TextureUtil.java b/jme3-jogl/src/main/java/com/jme3/renderer/jogl/TextureUtil.java index 9e891e4ec..7774c79fb 100644 --- a/jme3-jogl/src/main/java/com/jme3/renderer/jogl/TextureUtil.java +++ b/jme3-jogl/src/main/java/com/jme3/renderer/jogl/TextureUtil.java @@ -36,6 +36,8 @@ import com.jme3.renderer.RendererException; import com.jme3.texture.Image; import com.jme3.texture.Image.Format; import java.nio.ByteBuffer; +import java.util.logging.Level; +import java.util.logging.Logger; import javax.media.opengl.GL; import javax.media.opengl.GL2; import javax.media.opengl.GL2ES2; @@ -157,7 +159,26 @@ public class TextureUtil { setFormat(Format.LATC, GL2.GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT, GL.GL_LUMINANCE_ALPHA, GL.GL_UNSIGNED_BYTE, true); } - public static GLImageFormat getImageFormat(Format fmt){ + //sRGB formats + private static final GLImageFormat sRGB_RGB8 = new GLImageFormat(GL2.GL_SRGB8,GL.GL_RGB, GL.GL_UNSIGNED_BYTE, false); + private static final GLImageFormat sRGB_RGBA8 = new GLImageFormat(GL.GL_SRGB8_ALPHA8,GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, false); + private static final GLImageFormat sRGB_Luminance8 = new GLImageFormat(GL2.GL_SLUMINANCE8,GL.GL_LUMINANCE, GL.GL_UNSIGNED_BYTE, false); + private static final GLImageFormat sRGB_LuminanceAlpha8 = new GLImageFormat(GL2.GL_SLUMINANCE8_ALPHA8,GL.GL_LUMINANCE_ALPHA, GL.GL_UNSIGNED_BYTE, false); + private static final GLImageFormat sRGB_BGR8 = new GLImageFormat(GL2.GL_SRGB8, GL2GL3.GL_BGR, GL.GL_UNSIGNED_BYTE, false); + private static final GLImageFormat sRGB_ABGR8 = new GLImageFormat(GL2.GL_SRGB8_ALPHA8, GL2.GL_ABGR_EXT, GL.GL_UNSIGNED_BYTE, false); + + //FIXME cannot find GL_COMPRESSED_RGB_S3TC_DXT1,GL_COMPRESSED_RGBA_S3TC_DXT1,GL_COMPRESSED_RGB_S3TC_DXT3,GL_COMPRESSED_RGB_S3TC_DXT5 in JOGL used constants + //GL_COMPRESSED_RGB_S3TC_DXT1 = 33776; + //GL_COMPRESSED_RGBA_S3TC_DXT1_EXT = 33777; + //GL_COMPRESSED_RGBA_S3TC_DXT3_EXT = 33778; + //GL_COMPRESSED_RGBA_S3TC_DXT5_EXT = 33779; + private static final GLImageFormat sRGB_DXT1 = new GLImageFormat(33776, GL.GL_RGB, GL.GL_UNSIGNED_BYTE, true); + private static final GLImageFormat sRGB_DXT1A = new GLImageFormat( 33777, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, true); + private static final GLImageFormat sRGB_DXT3 = new GLImageFormat(33778, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, true); + private static final GLImageFormat sRGB_DXT5 = new GLImageFormat( 33779, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, true); + + + public static GLImageFormat getImageFormat(Format fmt, boolean isSrgb){ GL gl = GLContext.getCurrentGL(); switch (fmt){ case ABGR8: @@ -231,24 +252,46 @@ public class TextureUtil { } break; } + + if(isSrgb){ + return getSrgbFormat(fmt); + } return formatToGL[fmt.ordinal()]; } - - public static GLImageFormat getImageFormatWithError(Format fmt) { - GLImageFormat glFmt = getImageFormat(fmt); + + public static GLImageFormat getImageFormatWithError(Format fmt, boolean isSrgb) { + GLImageFormat glFmt = getImageFormat(fmt, isSrgb); if (glFmt == null) { throw new RendererException("Image format '" + fmt + "' is unsupported by the video hardware."); } return glFmt; } + + private static GLImageFormat getSrgbFormat(Format fmt){ + switch (fmt){ + case RGB8 : return sRGB_RGB8; + case RGBA8 : return sRGB_RGBA8; + case BGR8 : return sRGB_BGR8; + case ABGR8 : return sRGB_ABGR8; + case Luminance8 : return sRGB_Luminance8; + case Luminance8Alpha8 : return sRGB_LuminanceAlpha8; + case DXT1 : return sRGB_DXT1; + case DXT1A : return sRGB_DXT1A; + case DXT3 : return sRGB_DXT3; + case DXT5 : return sRGB_DXT5; + default : Logger.getLogger(TextureUtil.class.getName()).log(Level.WARNING, "Format {0} has no sRGB equivalent, using linear format.", fmt.toString()); + return formatToGL[fmt.ordinal()]; + } + } public static void uploadTexture(Image image, int target, int index, - int border){ + int border, + boolean linearizeSrgb){ GL gl = GLContext.getCurrentGL(); Image.Format fmt = image.getFormat(); - GLImageFormat glFmt = getImageFormatWithError(fmt); + GLImageFormat glFmt = getImageFormatWithError(fmt, image.isSrgb() && linearizeSrgb); ByteBuffer data; if (index >= 0 && image.getData() != null && image.getData().size() > 0){ @@ -427,10 +470,11 @@ public class TextureUtil { int target, int index, int x, - int y) { + int y, + boolean linearizeSrgb) { GL gl = GLContext.getCurrentGL(); Image.Format fmt = image.getFormat(); - GLImageFormat glFmt = getImageFormatWithError(fmt); + GLImageFormat glFmt = getImageFormatWithError(fmt, image.isSrgb() && linearizeSrgb); ByteBuffer data = null; if (index >= 0 && image.getData() != null && image.getData().size() > 0) { diff --git a/jme3-lwjgl/src/main/java/com/jme3/renderer/lwjgl/LwjglGL1Renderer.java b/jme3-lwjgl/src/main/java/com/jme3/renderer/lwjgl/LwjglGL1Renderer.java index e94598ef4..22c1a359c 100644 --- a/jme3-lwjgl/src/main/java/com/jme3/renderer/lwjgl/LwjglGL1Renderer.java +++ b/jme3-lwjgl/src/main/java/com/jme3/renderer/lwjgl/LwjglGL1Renderer.java @@ -803,7 +803,7 @@ public class LwjglGL1Renderer implements GL1Renderer { TextureUtil.uploadTexture(img, target, i, 0, tdc); } } else {*/ - TextureUtil.uploadTexture(img, target, 0, 0); + TextureUtil.uploadTexture(img, target, 0, 0, false); //} img.clearUpdateNeeded(); @@ -854,7 +854,7 @@ public class LwjglGL1Renderer implements GL1Renderer { public void modifyTexture(Texture tex, Image pixels, int x, int y) { setTexture(0, tex); - TextureUtil.uploadSubTexture(pixels, convertTextureType(tex.getType()), 0, x, y); + TextureUtil.uploadSubTexture(pixels, convertTextureType(tex.getType()), 0, x, y, false); } private void clearTextureUnits() { @@ -1202,4 +1202,8 @@ public class LwjglGL1Renderer implements GL1Renderer { public void setMainFrameBufferSrgb(boolean srgb) { } + + public void setLinearizeSrgbImages(boolean linearize) { + + } } diff --git a/jme3-lwjgl/src/main/java/com/jme3/renderer/lwjgl/LwjglRenderer.java b/jme3-lwjgl/src/main/java/com/jme3/renderer/lwjgl/LwjglRenderer.java index 89f74ef25..4db530ead 100644 --- a/jme3-lwjgl/src/main/java/com/jme3/renderer/lwjgl/LwjglRenderer.java +++ b/jme3-lwjgl/src/main/java/com/jme3/renderer/lwjgl/LwjglRenderer.java @@ -112,6 +112,7 @@ public class LwjglRenderer implements Renderer { private final Statistics statistics = new Statistics(); private int vpX, vpY, vpW, vpH; private int clipX, clipY, clipW, clipH; + private boolean linearizeSrgbImages; public LwjglRenderer() { } @@ -390,6 +391,11 @@ public class LwjglRenderer implements Renderer { } caps.add(Caps.Multisample); } + + //supports sRGB pipeline + if (ctxCaps.GL_ARB_framebuffer_sRGB && ctxCaps.GL_EXT_texture_sRGB){ + caps.add(Caps.Srgb); + } logger.log(Level.FINE, "Caps: {0}", caps); } @@ -1373,7 +1379,7 @@ public class LwjglRenderer implements Renderer { + ":" + fb.getHeight() + " is not supported."); } - TextureUtil.GLImageFormat glFmt = TextureUtil.getImageFormatWithError(rb.getFormat()); + TextureUtil.GLImageFormat glFmt = TextureUtil.getImageFormatWithError(rb.getFormat(), fb.isSrgb()); if (fb.getSamples() > 1 && GLContext.getCapabilities().GL_EXT_framebuffer_multisample) { int samples = fb.getSamples(); @@ -1902,7 +1908,7 @@ public class LwjglRenderer implements Renderer { return; } for (int i = 0; i < 6; i++) { - TextureUtil.uploadTexture(img, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, i, 0); + TextureUtil.uploadTexture(img, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, i, 0, linearizeSrgbImages); } } else if (target == EXTTextureArray.GL_TEXTURE_2D_ARRAY_EXT) { if (!caps.contains(Caps.TextureArray)) { @@ -1912,15 +1918,15 @@ public class LwjglRenderer implements Renderer { List data = img.getData(); // -1 index specifies prepare data for 2D Array - TextureUtil.uploadTexture(img, target, -1, 0); + TextureUtil.uploadTexture(img, target, -1, 0, linearizeSrgbImages); for (int i = 0; i < data.size(); i++) { // upload each slice of 2D array in turn // this time with the appropriate index - TextureUtil.uploadTexture(img, target, i, 0); + TextureUtil.uploadTexture(img, target, i, 0, linearizeSrgbImages); } } else { - TextureUtil.uploadTexture(img, target, 0, 0); + TextureUtil.uploadTexture(img, target, 0, 0, linearizeSrgbImages); } if (img.getMultiSamples() != imageSamples) { @@ -1978,7 +1984,7 @@ public class LwjglRenderer implements Renderer { public void modifyTexture(Texture tex, Image pixels, int x, int y) { setTexture(0, tex); - TextureUtil.uploadSubTexture(pixels, convertTextureType(tex.getType(), pixels.getMultiSamples(), -1), 0, x, y); + TextureUtil.uploadSubTexture(pixels, convertTextureType(tex.getType(), pixels.getMultiSamples(), -1), 0, x, y, linearizeSrgbImages); } public void clearTextureUnits() { @@ -2489,11 +2495,15 @@ public class LwjglRenderer implements Renderer { public void setMainFrameBufferSrgb(boolean srgb) { //Gamma correction - if(srgb && GLContext.getCapabilities().GL_ARB_framebuffer_sRGB){ + if(srgb && caps.contains(Caps.Srgb)){ glEnable(GL30.GL_FRAMEBUFFER_SRGB); logger.log(Level.FINER, "SRGB FrameBuffer enabled (Gamma Correction)"); }else{ glDisable(GL30.GL_FRAMEBUFFER_SRGB); } } + + public void setLinearizeSrgbImages(boolean linearize) { + linearizeSrgbImages = linearize; + } } diff --git a/jme3-lwjgl/src/main/java/com/jme3/renderer/lwjgl/TextureUtil.java b/jme3-lwjgl/src/main/java/com/jme3/renderer/lwjgl/TextureUtil.java index 7aebb22fb..18fe3fbe8 100644 --- a/jme3-lwjgl/src/main/java/com/jme3/renderer/lwjgl/TextureUtil.java +++ b/jme3-lwjgl/src/main/java/com/jme3/renderer/lwjgl/TextureUtil.java @@ -55,6 +55,10 @@ import org.lwjgl.opengl.GLContext; import com.jme3.renderer.RendererException; import com.jme3.texture.Image; import com.jme3.texture.Image.Format; +import static com.jme3.texture.Image.Format.RGB8; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.lwjgl.opengl.EXTTextureSRGB; class TextureUtil { @@ -104,7 +108,7 @@ class TextureUtil { // Depth stencil formats setFormat(Format.Depth24Stencil8, GL30.GL_DEPTH24_STENCIL8, GL30.GL_DEPTH_STENCIL, GL30.GL_UNSIGNED_INT_24_8, false); - + // RGB formats setFormat(Format.BGR8, GL11.GL_RGB8, EXTBgra.GL_BGR_EXT, GL11.GL_UNSIGNED_BYTE, false); setFormat(Format.ARGB8, GL11.GL_RGBA8, EXTBgra.GL_BGRA_EXT, GL12.GL_UNSIGNED_INT_8_8_8_8_REV, false); @@ -138,10 +142,23 @@ class TextureUtil { // LTC/LATC/3Dc formats setFormat(Format.LTC, EXTTextureCompressionLATC.GL_COMPRESSED_LUMINANCE_LATC1_EXT, GL11.GL_LUMINANCE, GL11.GL_UNSIGNED_BYTE, true); - setFormat(Format.LATC, EXTTextureCompressionLATC.GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT, GL11.GL_LUMINANCE_ALPHA, GL11.GL_UNSIGNED_BYTE, true); + setFormat(Format.LATC, EXTTextureCompressionLATC.GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT, GL11.GL_LUMINANCE_ALPHA, GL11.GL_UNSIGNED_BYTE, true); } - public static GLImageFormat getImageFormat(ContextCapabilities caps, Format fmt){ + //sRGB formats + private static final GLImageFormat sRGB_RGB8 = new GLImageFormat(EXTTextureSRGB.GL_SRGB8_EXT, GL11.GL_RGB, GL11.GL_UNSIGNED_BYTE, false); + private static final GLImageFormat sRGB_RGBA8 = new GLImageFormat(EXTTextureSRGB.GL_SRGB8_ALPHA8_EXT, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, false); + private static final GLImageFormat sRGB_Luminance8 = new GLImageFormat(EXTTextureSRGB.GL_SLUMINANCE8_EXT, GL11.GL_LUMINANCE, GL11.GL_UNSIGNED_BYTE, false); + private static final GLImageFormat sRGB_LuminanceAlpha8 = new GLImageFormat(EXTTextureSRGB.GL_SLUMINANCE8_ALPHA8_EXT, GL11.GL_LUMINANCE_ALPHA, GL11.GL_UNSIGNED_BYTE, false); + private static final GLImageFormat sRGB_BGR8 = new GLImageFormat(EXTTextureSRGB.GL_SRGB8_EXT, EXTBgra.GL_BGR_EXT, GL11.GL_UNSIGNED_BYTE, false); + private static final GLImageFormat sRGB_ABGR8 = new GLImageFormat(EXTTextureSRGB.GL_SRGB8_ALPHA8_EXT, EXTAbgr.GL_ABGR_EXT, GL11.GL_UNSIGNED_BYTE, false); + + private static final GLImageFormat sRGB_DXT1 = new GLImageFormat(EXTTextureSRGB.GL_COMPRESSED_SRGB_S3TC_DXT1_EXT,GL11.GL_RGB, GL11.GL_UNSIGNED_BYTE, true); + private static final GLImageFormat sRGB_DXT1A = new GLImageFormat(EXTTextureSRGB.GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, true); + private static final GLImageFormat sRGB_DXT3 = new GLImageFormat(EXTTextureSRGB.GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, true); + private static final GLImageFormat sRGB_DXT5 = new GLImageFormat(EXTTextureSRGB.GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, true); + + public static GLImageFormat getImageFormat(ContextCapabilities caps, Format fmt, boolean isSrgb){ switch (fmt){ case ABGR8: if (!caps.GL_EXT_abgr){ @@ -213,24 +230,45 @@ class TextureUtil { } break; } + if(isSrgb){ + return getSrgbFormat(fmt); + } return formatToGL[fmt.ordinal()]; } - public static GLImageFormat getImageFormatWithError(Format fmt) { - GLImageFormat glFmt = getImageFormat(GLContext.getCapabilities(), fmt); + public static GLImageFormat getImageFormatWithError(Format fmt, boolean isSrgb) { + GLImageFormat glFmt = getImageFormat(GLContext.getCapabilities(), fmt, isSrgb); if (glFmt == null) { throw new RendererException("Image format '" + fmt + "' is unsupported by the video hardware."); } return glFmt; } + private static GLImageFormat getSrgbFormat(Format fmt){ + switch (fmt){ + case RGB8 : return sRGB_RGB8; + case RGBA8 : return sRGB_RGBA8; + case BGR8 : return sRGB_BGR8; + case ABGR8 : return sRGB_ABGR8; + case Luminance8 : return sRGB_Luminance8; + case Luminance8Alpha8 : return sRGB_LuminanceAlpha8; + case DXT1 : return sRGB_DXT1; + case DXT1A : return sRGB_DXT1A; + case DXT3 : return sRGB_DXT3; + case DXT5 : return sRGB_DXT5; + default : Logger.getLogger(TextureUtil.class.getName()).log(Level.WARNING, "Format {0} has no sRGB equivalent, using linear format.", fmt.toString()); + return formatToGL[fmt.ordinal()]; + } + } + public static void uploadTexture(Image image, int target, int index, - int border){ + int border, + boolean linearizeSrgb){ Image.Format fmt = image.getFormat(); - GLImageFormat glFmt = getImageFormatWithError(fmt); + GLImageFormat glFmt = getImageFormatWithError(fmt, image.isSrgb() && linearizeSrgb); ByteBuffer data; if (index >= 0 && image.getData() != null && image.getData().size() > 0){ @@ -384,9 +422,10 @@ class TextureUtil { int target, int index, int x, - int y) { + int y, + boolean linearizeSrgb) { Image.Format fmt = image.getFormat(); - GLImageFormat glFmt = getImageFormatWithError(fmt); + GLImageFormat glFmt = getImageFormatWithError(fmt, image.isSrgb() && linearizeSrgb); ByteBuffer data = null; if (index >= 0 && image.getData() != null && image.getData().size() > 0) { diff --git a/jme3-lwjgl/src/main/java/com/jme3/system/lwjgl/LwjglContext.java b/jme3-lwjgl/src/main/java/com/jme3/system/lwjgl/LwjglContext.java index 04b8541c0..d9347f2c5 100644 --- a/jme3-lwjgl/src/main/java/com/jme3/system/lwjgl/LwjglContext.java +++ b/jme3-lwjgl/src/main/java/com/jme3/system/lwjgl/LwjglContext.java @@ -238,6 +238,7 @@ public abstract class LwjglContext implements JmeContext { assert false; } renderer.setMainFrameBufferSrgb(settings.getGammaCorrection()); + renderer.setLinearizeSrgbImages(settings.getGammaCorrection()); // Init input if (keyInput != null) {