From e4ba4e9e9edd84fbe7f5b3a78b1e14acd74c3b1c Mon Sep 17 00:00:00 2001 From: Nehon Date: Tue, 20 May 2014 23:54:43 +0200 Subject: [PATCH] - Image loaders now assume ALL images are in sRGB space and set the flag accordingly - All images constructors now take the isSrgb flag as a parameter, all engine classes has been changed to accommodate the change, old constructors has been deprecated for backward compatibility. One should always ask himself in which color space is an image if dealing with gamma correction - Gamma correction has been defaulted to false in the appSettings --- .../renderer/android/OGLESShaderRenderer.java | 4 + .../com/jme3/asset/DesktopAssetManager.java | 2 +- .../src/main/java/com/jme3/renderer/Caps.java | 7 +- .../main/java/com/jme3/renderer/Renderer.java | 27 ++++ .../java/com/jme3/system/AppSettings.java | 2 +- .../main/java/com/jme3/system/JmeVersion.java | 2 +- .../java/com/jme3/system/NullRenderer.java | 3 + .../java/com/jme3/texture/FrameBuffer.java | 39 ++++++ .../src/main/java/com/jme3/texture/Image.java | 123 +++++++++++++++++- .../main/java/com/jme3/texture/Texture2D.java | 4 +- .../main/java/com/jme3/texture/Texture3D.java | 4 +- .../java/com/jme3/texture/TextureArray.java | 3 +- .../java/com/jme3/texture/TextureCubeMap.java | 2 +- .../java/com/jme3/util/PlaceholderAssets.java | 2 +- .../main/java/com/jme3/util/SkyFactory.java | 2 +- .../com/jme3/material/plugins/J3MLoader.java | 4 +- .../com/jme3/texture/plugins/DDSLoader.java | 4 +- .../com/jme3/texture/plugins/HDRLoader.java | 3 +- .../com/jme3/texture/plugins/PFMLoader.java | 2 +- .../java/jme3tools/optimize/TextureAtlas.java | 7 +- .../com/jme3/texture/plugins/AWTLoader.java | 14 +- .../java/jme3test/bullet/TestFancyCar.java | 8 +- .../jme3test/texture/TestImageRaster.java | 4 +- .../java/jme3test/texture/TestTexture3D.java | 2 +- .../renderer/ios/IGLESShaderRenderer.java | 4 + .../jme3/renderer/jogl/JoglGL1Renderer.java | 7 +- .../com/jme3/renderer/jogl/JoglRenderer.java | 24 +++- .../com/jme3/renderer/jogl/TextureUtil.java | 60 +++++++-- .../jme3/renderer/lwjgl/LwjglGL1Renderer.java | 8 +- .../jme3/renderer/lwjgl/LwjglRenderer.java | 24 +++- .../com/jme3/renderer/lwjgl/TextureUtil.java | 57 ++++++-- .../com/jme3/system/lwjgl/LwjglContext.java | 1 + 32 files changed, 386 insertions(+), 73 deletions(-) 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) {