- 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
experimental
Nehon 11 years ago
parent 77a4002c3d
commit e4ba4e9e9e
  1. 4
      jme3-android/src/main/java/com/jme3/renderer/android/OGLESShaderRenderer.java
  2. 2
      jme3-core/src/main/java/com/jme3/asset/DesktopAssetManager.java
  3. 7
      jme3-core/src/main/java/com/jme3/renderer/Caps.java
  4. 27
      jme3-core/src/main/java/com/jme3/renderer/Renderer.java
  5. 2
      jme3-core/src/main/java/com/jme3/system/AppSettings.java
  6. 2
      jme3-core/src/main/java/com/jme3/system/JmeVersion.java
  7. 3
      jme3-core/src/main/java/com/jme3/system/NullRenderer.java
  8. 39
      jme3-core/src/main/java/com/jme3/texture/FrameBuffer.java
  9. 123
      jme3-core/src/main/java/com/jme3/texture/Image.java
  10. 4
      jme3-core/src/main/java/com/jme3/texture/Texture2D.java
  11. 4
      jme3-core/src/main/java/com/jme3/texture/Texture3D.java
  12. 3
      jme3-core/src/main/java/com/jme3/texture/TextureArray.java
  13. 2
      jme3-core/src/main/java/com/jme3/texture/TextureCubeMap.java
  14. 2
      jme3-core/src/main/java/com/jme3/util/PlaceholderAssets.java
  15. 2
      jme3-core/src/main/java/com/jme3/util/SkyFactory.java
  16. 4
      jme3-core/src/plugins/java/com/jme3/material/plugins/J3MLoader.java
  17. 4
      jme3-core/src/plugins/java/com/jme3/texture/plugins/DDSLoader.java
  18. 3
      jme3-core/src/plugins/java/com/jme3/texture/plugins/HDRLoader.java
  19. 2
      jme3-core/src/plugins/java/com/jme3/texture/plugins/PFMLoader.java
  20. 7
      jme3-core/src/tools/java/jme3tools/optimize/TextureAtlas.java
  21. 14
      jme3-desktop/src/main/java/com/jme3/texture/plugins/AWTLoader.java
  22. 8
      jme3-examples/src/main/java/jme3test/bullet/TestFancyCar.java
  23. 4
      jme3-examples/src/main/java/jme3test/texture/TestImageRaster.java
  24. 2
      jme3-examples/src/main/java/jme3test/texture/TestTexture3D.java
  25. 4
      jme3-ios/src/main/java/com/jme3/renderer/ios/IGLESShaderRenderer.java
  26. 7
      jme3-jogl/src/main/java/com/jme3/renderer/jogl/JoglGL1Renderer.java
  27. 24
      jme3-jogl/src/main/java/com/jme3/renderer/jogl/JoglRenderer.java
  28. 60
      jme3-jogl/src/main/java/com/jme3/renderer/jogl/TextureUtil.java
  29. 8
      jme3-lwjgl/src/main/java/com/jme3/renderer/lwjgl/LwjglGL1Renderer.java
  30. 24
      jme3-lwjgl/src/main/java/com/jme3/renderer/lwjgl/LwjglRenderer.java
  31. 57
      jme3-lwjgl/src/main/java/com/jme3/renderer/lwjgl/TextureUtil.java
  32. 1
      jme3-lwjgl/src/main/java/com/jme3/system/lwjgl/LwjglContext.java

@ -2536,4 +2536,8 @@ public class OGLESShaderRenderer implements Renderer {
public void setMainFrameBufferSrgb(boolean srgb) { public void setMainFrameBufferSrgb(boolean srgb) {
//TODO once opglES3.0 is supported maybe.... //TODO once opglES3.0 is supported maybe....
} }
public void setLinearizeSrgbImages(boolean linearize) {
//TODO once opglES3.0 is supported maybe....
}
} }

@ -342,7 +342,7 @@ public class DesktopAssetManager implements AssetManager {
return loadAsset(new AssetKey(name)); return loadAsset(new AssetKey(name));
} }
public Texture loadTexture(TextureKey key){ public Texture loadTexture(TextureKey key){
return (Texture) loadAsset(key); return (Texture) loadAsset(key);
} }

@ -231,7 +231,12 @@ public enum Caps {
/** /**
* Supports FBO with Depth24Stencil8 image format * 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 * Returns true if given the renderer capabilities, the texture

@ -335,4 +335,31 @@ public interface Renderer {
* @seealso Caps#Srgb * @seealso Caps#Srgb
*/ */
public void setMainFrameBufferSrgb(boolean 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);
} }

@ -143,7 +143,7 @@ public final class AppSettings extends HashMap<String, Object> {
defaults.put("SettingsDialogImage", "/com/jme3/app/Monkey.png"); defaults.put("SettingsDialogImage", "/com/jme3/app/Monkey.png");
defaults.put("MinHeight", 0); defaults.put("MinHeight", 0);
defaults.put("MinWidth", 0); defaults.put("MinWidth", 0);
defaults.put("GammaCorrection", true); defaults.put("GammaCorrection", false);
// defaults.put("Icons", null); // defaults.put("Icons", null);
} }

@ -32,7 +32,7 @@
package com.jme3.system; package com.jme3.system;
public class JmeVersion { 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() { public static String getFullName() {
return FULL_NAME; return FULL_NAME;

@ -155,4 +155,7 @@ public class NullRenderer implements Renderer {
public void setMainFrameBufferSrgb(boolean srgb) { public void setMainFrameBufferSrgb(boolean srgb) {
} }
public void setLinearizeSrgbImages(boolean linearize) {
}
} }

@ -79,6 +79,7 @@ public class FrameBuffer extends NativeObject {
private ArrayList<RenderBuffer> colorBufs = new ArrayList<RenderBuffer>(); private ArrayList<RenderBuffer> colorBufs = new ArrayList<RenderBuffer>();
private RenderBuffer depthBuf = null; private RenderBuffer depthBuf = null;
private int colorBufIndex = 0; private int colorBufIndex = 0;
private boolean srgb;
/** /**
* <code>RenderBuffer</code> represents either a texture or a * <code>RenderBuffer</code> represents either a texture or a
@ -506,4 +507,42 @@ public class FrameBuffer extends NativeObject {
public long getUniqueId() { public long getUniqueId() {
return ((long)OBJTYPE_FRAMEBUFFER << 32) | ((long)id); 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;
}
} }

@ -334,6 +334,7 @@ public class Image extends NativeObject implements Savable /*, Cloneable*/ {
protected ArrayList<ByteBuffer> data; protected ArrayList<ByteBuffer> data;
protected transient Object efficientData; protected transient Object efficientData;
protected int multiSamples = 1; protected int multiSamples = 1;
protected boolean srgb;
// protected int mipOffset = 0; // protected int mipOffset = 0;
// attributes relating to GL object // attributes relating to GL object
@ -447,9 +448,12 @@ public class Image extends NativeObject implements Savable /*, Cloneable*/ {
* the image data. * the image data.
* @param mipMapSizes * @param mipMapSizes
* the array of mipmap sizes, or null for no mipmaps. * 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<ByteBuffer> data, public Image(Format format, int width, int height, int depth, ArrayList<ByteBuffer> data,
int[] mipMapSizes) { int[] mipMapSizes, boolean isSrgb) {
this(); this();
@ -466,8 +470,25 @@ public class Image extends NativeObject implements Savable /*, Cloneable*/ {
this.data = data; this.data = data;
this.depth = depth; this.depth = depth;
this.mipMapSizes = mipMapSizes; 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<ByteBuffer> data,
int[] mipMapSizes) {
this(format, width, height, depth, data, mipMapSizes, false);
}
/** /**
* Constructor instantiates a new <code>Image</code> object. The * Constructor instantiates a new <code>Image</code> object. The
* attributes of the image are defined during construction. * attributes of the image are defined during construction.
@ -482,9 +503,12 @@ public class Image extends NativeObject implements Savable /*, Cloneable*/ {
* the image data. * the image data.
* @param mipMapSizes * @param mipMapSizes
* the array of mipmap sizes, or null for no mipmaps. * 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, public Image(Format format, int width, int height, ByteBuffer data,
int[] mipMapSizes) { int[] mipMapSizes, boolean isSrgb) {
this(); this();
@ -503,8 +527,24 @@ public class Image extends NativeObject implements Savable /*, Cloneable*/ {
this.data.add(data); this.data.add(data);
} }
this.mipMapSizes = mipMapSizes; 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 <code>Image</code> object. The * Constructor instantiates a new <code>Image</code> object. The
* attributes of the image are defined during construction. * 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. * the height of the image.
* @param data * @param data
* the image 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<ByteBuffer> 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<ByteBuffer> data) { public Image(Format format, int width, int height, int depth, ArrayList<ByteBuffer> 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. * the height of the image.
* @param data * @param data
* the image 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) { 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). * @return The number of samples (for multisampled textures).
* @see Image#setMultiSamples(int) * @see Image#setMultiSamples(int)
@ -788,6 +863,44 @@ public class Image extends NativeObject implements Savable /*, Cloneable*/ {
public int[] getMipMapSizes() { public int[] getMipMapSizes() {
return mipMapSizes; 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 @Override
public String toString(){ public String toString(){

@ -76,7 +76,7 @@ public class Texture2D extends Texture {
* @param format * @param format
*/ */
public Texture2D(int width, int height, Image.Format 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 * @param numSamples
*/ */
public Texture2D(int width, int height, int numSamples, Image.Format format){ 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); getImage().setMultiSamples(numSamples);
} }

@ -78,7 +78,7 @@ public class Texture3D extends Texture {
* @param format * @param format
*/ */
public Texture3D(int width, int height, int depth, Image.Format 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 * @param numSamples
*/ */
public Texture3D(int width, int height, int depth, int numSamples, Image.Format format) { 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); getImage().setMultiSamples(numSamples);
} }

@ -70,7 +70,8 @@ public class TextureArray extends Texture {
int width = images.get(0).getWidth(); int width = images.get(0).getWidth();
int height = images.get(0).getHeight(); int height = images.get(0).getHeight();
Format format = images.get(0).getFormat(); 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) { for (Image img : images) {
if (img.getHeight() != height || img.getWidth() != width) { if (img.getHeight() != height || img.getWidth() != width) {

@ -88,7 +88,7 @@ public class TextureCubeMap extends Texture {
for(int i = 0; i < layerCount; i++) { for(int i = 0; i < layerCount; i++) {
layers.add(null); layers.add(null);
} }
Image image = new Image(format, width, height, 0, layers); Image image = new Image(format, width, height, 0, layers, false);
return image; return image;
} }

@ -73,7 +73,7 @@ public class PlaceholderAssets {
public static Image getPlaceholderImage(){ public static Image getPlaceholderImage(){
ByteBuffer tempData = BufferUtils.createByteBuffer(3 * 4 * 4); ByteBuffer tempData = BufferUtils.createByteBuffer(3 * 4 * 4);
tempData.put(imageData).flip(); 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){ public static Material getPlaceholderMaterial(AssetManager assetManager){

@ -243,7 +243,7 @@ public class SkyFactory {
checkImagesForCubeMap(westImg, eastImg, northImg, southImg, upImg, downImg); 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(westImg.getData(0));
cubeImage.addData(eastImg.getData(0)); cubeImage.addData(eastImg.getData(0));

@ -152,14 +152,14 @@ public class J3MLoader implements AssetLoader {
if (tex != null){ if (tex != null){
if (repeat){ if (repeat){
tex.setWrap(WrapMode.Repeat); tex.setWrap(WrapMode.Repeat);
} }
}else{ }else{
tex = new Texture2D(PlaceholderAssets.getPlaceholderImage()); tex = new Texture2D(PlaceholderAssets.getPlaceholderImage());
if (repeat){ if (repeat){
tex.setWrap(WrapMode.Repeat); tex.setWrap(WrapMode.Repeat);
} }
tex.setKey(texKey); tex.setKey(texKey);
} }
return tex; return tex;
}else{ }else{
String[] split = value.trim().split(whitespacePattern); String[] split = value.trim().split(whitespacePattern);

@ -132,7 +132,7 @@ public class DDSLoader implements AssetLoader {
((TextureKey) info.getKey()).setTextureTypeHint(Type.CubeMap); ((TextureKey) info.getKey()).setTextureTypeHint(Type.CubeMap);
} }
ArrayList<ByteBuffer> data = readData(((TextureKey) info.getKey()).isFlipY()); ArrayList<ByteBuffer> data = readData(((TextureKey) info.getKey()).isFlipY());
return new Image(pixelFormat, width, height, depth, data, sizes); return new Image(pixelFormat, width, height, depth, data, sizes, true);
} finally { } finally {
if (stream != null){ if (stream != null){
stream.close(); stream.close();
@ -144,7 +144,7 @@ public class DDSLoader implements AssetLoader {
in = new LittleEndien(stream); in = new LittleEndien(stream);
loadHeader(); loadHeader();
ArrayList<ByteBuffer> data = readData(false); ArrayList<ByteBuffer> data = readData(false);
return new Image(pixelFormat, width, height, depth, data, sizes); return new Image(pixelFormat, width, height, depth, data, sizes, true);
} }
private void loadDX10Header() throws IOException { private void loadDX10Header() throws IOException {

@ -308,7 +308,8 @@ public class HDRLoader implements AssetLoader {
in.close(); in.close();
dataStore.rewind(); 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 { public Object load(AssetInfo info) throws IOException {

@ -129,7 +129,7 @@ public class PFMLoader implements AssetLoader {
} }
imageData.rewind(); 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 { public Object load(AssetInfo info) throws IOException {

@ -273,6 +273,9 @@ public class TextureAtlas {
images = new HashMap<String, byte[]>(); images = new HashMap<String, byte[]>();
} }
byte[] image = images.get(mapName); 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) { if (image == null) {
image = new byte[atlasWidth * atlasHeight * 4]; image = new byte[atlasWidth * atlasHeight * 4];
images.put(mapName, image); images.put(mapName, image);
@ -351,7 +354,7 @@ public class TextureAtlas {
if (clazz == null) { if (clazz == null) {
return 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); clazz.getMethod("convert", Image.class, Image.class).invoke(clazz.newInstance(), source, newImage);
return newImage; return newImage;
} catch (InstantiationException ex) { } catch (InstantiationException ex) {
@ -398,7 +401,7 @@ public class TextureAtlas {
} }
byte[] image = images.get(mapName); byte[] image = images.get(mapName);
if (image != null) { 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.setMagFilter(Texture.MagFilter.Bilinear);
tex.setMinFilter(Texture.MinFilter.BilinearNearestMipMap); tex.setMinFilter(Texture.MinFilter.BilinearNearestMipMap);
tex.setWrap(Texture.WrapMode.Clamp); tex.setWrap(Texture.WrapMode.Clamp);

@ -112,7 +112,7 @@ public class AWTLoader implements AssetLoader {
ByteBuffer data1 = BufferUtils.createByteBuffer(img.getWidth()*img.getHeight()*4); ByteBuffer data1 = BufferUtils.createByteBuffer(img.getWidth()*img.getHeight()*4);
data1.put(dataBuf1); 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 case BufferedImage.TYPE_3BYTE_BGR: // most common in JPEG images
byte[] dataBuf2 = (byte[]) extractImageData(img); byte[] dataBuf2 = (byte[]) extractImageData(img);
if (flipY) if (flipY)
@ -120,14 +120,14 @@ public class AWTLoader implements AssetLoader {
ByteBuffer data2 = BufferUtils.createByteBuffer(img.getWidth()*img.getHeight()*3); ByteBuffer data2 = BufferUtils.createByteBuffer(img.getWidth()*img.getHeight()*3);
data2.put(dataBuf2); 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 case BufferedImage.TYPE_BYTE_GRAY: // grayscale fonts
byte[] dataBuf3 = (byte[]) extractImageData(img); byte[] dataBuf3 = (byte[]) extractImageData(img);
if (flipY) if (flipY)
flipImage(dataBuf3, width, height, 8); flipImage(dataBuf3, width, height, 8);
ByteBuffer data3 = BufferUtils.createByteBuffer(img.getWidth()*img.getHeight()); ByteBuffer data3 = BufferUtils.createByteBuffer(img.getWidth()*img.getHeight());
data3.put(dataBuf3); 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 case BufferedImage.TYPE_USHORT_GRAY: // grayscale heightmap
short[] dataBuf4 = (short[]) extractImageData(img); short[] dataBuf4 = (short[]) extractImageData(img);
if (flipY) if (flipY)
@ -135,7 +135,7 @@ public class AWTLoader implements AssetLoader {
ByteBuffer data4 = BufferUtils.createByteBuffer(img.getWidth()*img.getHeight()*2); ByteBuffer data4 = BufferUtils.createByteBuffer(img.getWidth()*img.getHeight()*2);
data4.asShortBuffer().put(dataBuf4); data4.asShortBuffer().put(dataBuf4);
return new Image(Format.Luminance16, width, height, data4); return new Image(Format.Luminance16, width, height, data4, null, true);
default: default:
break; break;
} }
@ -158,10 +158,10 @@ public class AWTLoader implements AssetLoader {
} }
} }
data.flip(); data.flip();
return new Image(Format.RGB8, width, height, data); return new Image(Format.RGB8, width, height, data, null, true);
}else{ }else{
ByteBuffer data = BufferUtils.createByteBuffer(img.getWidth()*img.getHeight()*4); ByteBuffer data = BufferUtils.createByteBuffer(img.getWidth()*img.getHeight()*4);
// no alpha // alpha
for (int y = 0; y < height; y++){ for (int y = 0; y < height; y++){
for (int x = 0; x < width; x++){ for (int x = 0; x < width; x++){
int ny = y; int ny = y;
@ -178,7 +178,7 @@ public class AWTLoader implements AssetLoader {
} }
} }
data.flip(); data.flip();
return new Image(Format.RGBA8, width, height, data); return new Image(Format.RGBA8, width, height, data, null, true);
} }
} }

@ -31,6 +31,7 @@
*/ */
package jme3test.bullet; package jme3test.bullet;
import com.jme3.app.SettingsDialog;
import com.jme3.app.SimpleApplication; import com.jme3.app.SimpleApplication;
import com.jme3.bounding.BoundingBox; import com.jme3.bounding.BoundingBox;
import com.jme3.bullet.BulletAppState; import com.jme3.bullet.BulletAppState;
@ -51,6 +52,7 @@ import com.jme3.scene.Geometry;
import com.jme3.scene.Node; import com.jme3.scene.Node;
import com.jme3.scene.Spatial; import com.jme3.scene.Spatial;
import com.jme3.shadow.BasicShadowRenderer; import com.jme3.shadow.BasicShadowRenderer;
import com.jme3.system.AppSettings;
public class TestFancyCar extends SimpleApplication implements ActionListener { public class TestFancyCar extends SimpleApplication implements ActionListener {
@ -64,7 +66,7 @@ public class TestFancyCar extends SimpleApplication implements ActionListener {
private Node carNode; private Node carNode;
public static void main(String[] args) { public static void main(String[] args) {
TestFancyCar app = new TestFancyCar(); TestFancyCar app = new TestFancyCar();
app.start(); app.start();
} }
@ -91,7 +93,7 @@ public class TestFancyCar extends SimpleApplication implements ActionListener {
if (settings.getRenderer().startsWith("LWJGL")) { if (settings.getRenderer().startsWith("LWJGL")) {
BasicShadowRenderer bsr = new BasicShadowRenderer(assetManager, 512); BasicShadowRenderer bsr = new BasicShadowRenderer(assetManager, 512);
bsr.setDirection(new Vector3f(-0.5f, -0.3f, -0.3f).normalizeLocal()); bsr.setDirection(new Vector3f(-0.5f, -0.3f, -0.3f).normalizeLocal());
viewPort.addProcessor(bsr); // viewPort.addProcessor(bsr);
} }
cam.setFrustumFar(150f); cam.setFrustumFar(150f);
flyCam.setMoveSpeed(10); flyCam.setMoveSpeed(10);
@ -107,7 +109,7 @@ public class TestFancyCar extends SimpleApplication implements ActionListener {
dl = new DirectionalLight(); dl = new DirectionalLight();
dl.setDirection(new Vector3f(0.5f, -0.1f, 0.3f).normalizeLocal()); dl.setDirection(new Vector3f(0.5f, -0.1f, 0.3f).normalizeLocal());
rootNode.addLight(dl); // rootNode.addLight(dl);
} }
private PhysicsSpace getPhysicsSpace() { private PhysicsSpace getPhysicsSpace() {

@ -27,7 +27,7 @@ public class TestImageRaster extends SimpleApplication {
int width = image.getWidth(); int width = image.getWidth();
int height = image.getHeight(); int height = image.getHeight();
ByteBuffer data = BufferUtils.createByteBuffer( (int)Math.ceil(newFormat.getBitsPerPixel() / 8.0) * width * height); 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 sourceReader = ImageRaster.create(image);
ImageRaster targetWriter = ImageRaster.create(convertedImage); ImageRaster targetWriter = ImageRaster.create(convertedImage);
@ -66,7 +66,7 @@ public class TestImageRaster extends SimpleApplication {
} }
private Image createTestImage() { 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); ImageRaster io = ImageRaster.create(testImage);
io.setPixel(0, 0, ColorRGBA.Black); io.setPixel(0, 0, ColorRGBA.Black);

@ -125,6 +125,6 @@ public class TestTexture3D extends SimpleApplication {
} }
bb.rewind(); bb.rewind();
data.add(bb); 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));
} }
} }

@ -2576,4 +2576,8 @@ public class IGLESShaderRenderer implements Renderer {
public void setMainFrameBufferSrgb(boolean srgb) { public void setMainFrameBufferSrgb(boolean srgb) {
} }
public void setLinearizeSrgbImages(boolean linearize) {
}
} }

@ -856,7 +856,7 @@ public class JoglGL1Renderer implements GL1Renderer {
TextureUtil.uploadTexture(img, target, i, 0, tdc); TextureUtil.uploadTexture(img, target, i, 0, tdc);
} }
} else {*/ } else {*/
TextureUtil.uploadTexture(img, target, 0, 0); TextureUtil.uploadTexture(img, target, 0, 0,false);
//} //}
img.clearUpdateNeeded(); img.clearUpdateNeeded();
@ -908,7 +908,7 @@ public class JoglGL1Renderer implements GL1Renderer {
public void modifyTexture(Texture tex, Image pixels, int x, int y) { public void modifyTexture(Texture tex, Image pixels, int x, int y) {
setTexture(0, tex); 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() { private void clearTextureUnits() {
@ -1261,4 +1261,7 @@ public class JoglGL1Renderer implements GL1Renderer {
public void setMainFrameBufferSrgb(boolean srgb) { public void setMainFrameBufferSrgb(boolean srgb) {
} }
public void setLinearizeSrgbImages(boolean linearize) {
}
} }

@ -119,6 +119,7 @@ public class JoglRenderer implements Renderer {
private final Statistics statistics = new Statistics(); private final Statistics statistics = new Statistics();
private int vpX, vpY, vpW, vpH; private int vpX, vpY, vpW, vpH;
private int clipX, clipY, clipW, clipH; private int clipX, clipY, clipW, clipH;
private boolean linearizeSrgbImages;
public JoglRenderer() { public JoglRenderer() {
} }
@ -415,6 +416,11 @@ public class JoglRenderer implements Renderer {
caps.add(Caps.Multisample); 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); logger.log(Level.FINE, "Caps: {0}", caps);
} }
@ -1443,7 +1449,7 @@ public class JoglRenderer implements Renderer {
+ ":" + fb.getHeight() + " is not supported."); + ":" + 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") if (fb.getSamples() > 1 && gl.isExtensionAvailable("GL_EXT_framebuffer_multisample")
&& gl.isGL2GL3()/*&& gl.isFunctionAvailable("glRenderbufferStorageMultisample")*/) { && gl.isGL2GL3()/*&& gl.isFunctionAvailable("glRenderbufferStorageMultisample")*/) {
@ -1995,19 +2001,19 @@ public class JoglRenderer implements Renderer {
return; return;
} }
for (int i = 0; i < 6; i++) { 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) { } else if (target == GL.GL_TEXTURE_2D_ARRAY) {
List<ByteBuffer> data = img.getData(); List<ByteBuffer> data = img.getData();
// -1 index specifies prepare data for 2D Array // -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++) { for (int i = 0; i < data.size(); i++) {
// upload each slice of 2D array in turn // upload each slice of 2D array in turn
// this time with the appropriate index // this time with the appropriate index
TextureUtil.uploadTexture(img, target, i, 0); TextureUtil.uploadTexture(img, target, i, 0, linearizeSrgbImages);
} }
} else { } else {
TextureUtil.uploadTexture(img, target, 0, 0); TextureUtil.uploadTexture(img, target, 0, 0, linearizeSrgbImages);
} }
if (img.getMultiSamples() != imageSamples) { if (img.getMultiSamples() != imageSamples) {
@ -2066,7 +2072,7 @@ public class JoglRenderer implements Renderer {
public void modifyTexture(Texture tex, Image pixels, int x, int y) { public void modifyTexture(Texture tex, Image pixels, int x, int y) {
setTexture(0, tex); 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() { public void clearTextureUnits() {
@ -2592,7 +2598,7 @@ public class JoglRenderer implements Renderer {
public void setMainFrameBufferSrgb(boolean srgb) { public void setMainFrameBufferSrgb(boolean srgb) {
//Gamma correction //Gamma correction
if(srgb && GLContext.getCurrent().isExtensionAvailable("GL_ARB_framebuffer_sRGB")){ if(srgb && caps.contains(Caps.Srgb)){
GLContext.getCurrentGL().glEnable(GL3.GL_FRAMEBUFFER_SRGB); GLContext.getCurrentGL().glEnable(GL3.GL_FRAMEBUFFER_SRGB);
logger.log(Level.FINER, "SRGB FrameBuffer enabled (Gamma Correction)"); logger.log(Level.FINER, "SRGB FrameBuffer enabled (Gamma Correction)");
}else{ }else{
@ -2600,4 +2606,8 @@ public class JoglRenderer implements Renderer {
} }
} }
public void setLinearizeSrgbImages(boolean linearize) {
linearizeSrgbImages = linearize;
}
} }

@ -36,6 +36,8 @@ import com.jme3.renderer.RendererException;
import com.jme3.texture.Image; import com.jme3.texture.Image;
import com.jme3.texture.Image.Format; import com.jme3.texture.Image.Format;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.media.opengl.GL; import javax.media.opengl.GL;
import javax.media.opengl.GL2; import javax.media.opengl.GL2;
import javax.media.opengl.GL2ES2; 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); 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(); GL gl = GLContext.getCurrentGL();
switch (fmt){ switch (fmt){
case ABGR8: case ABGR8:
@ -231,24 +252,46 @@ public class TextureUtil {
} }
break; break;
} }
if(isSrgb){
return getSrgbFormat(fmt);
}
return formatToGL[fmt.ordinal()]; return formatToGL[fmt.ordinal()];
} }
public static GLImageFormat getImageFormatWithError(Format fmt) { public static GLImageFormat getImageFormatWithError(Format fmt, boolean isSrgb) {
GLImageFormat glFmt = getImageFormat(fmt); GLImageFormat glFmt = getImageFormat(fmt, isSrgb);
if (glFmt == null) { if (glFmt == null) {
throw new RendererException("Image format '" + fmt + "' is unsupported by the video hardware."); throw new RendererException("Image format '" + fmt + "' is unsupported by the video hardware.");
} }
return glFmt; 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, public static void uploadTexture(Image image,
int target, int target,
int index, int index,
int border){ int border,
boolean linearizeSrgb){
GL gl = GLContext.getCurrentGL(); GL gl = GLContext.getCurrentGL();
Image.Format fmt = image.getFormat(); Image.Format fmt = image.getFormat();
GLImageFormat glFmt = getImageFormatWithError(fmt); GLImageFormat glFmt = getImageFormatWithError(fmt, image.isSrgb() && linearizeSrgb);
ByteBuffer data; ByteBuffer data;
if (index >= 0 && image.getData() != null && image.getData().size() > 0){ if (index >= 0 && image.getData() != null && image.getData().size() > 0){
@ -427,10 +470,11 @@ public class TextureUtil {
int target, int target,
int index, int index,
int x, int x,
int y) { int y,
boolean linearizeSrgb) {
GL gl = GLContext.getCurrentGL(); GL gl = GLContext.getCurrentGL();
Image.Format fmt = image.getFormat(); Image.Format fmt = image.getFormat();
GLImageFormat glFmt = getImageFormatWithError(fmt); GLImageFormat glFmt = getImageFormatWithError(fmt, image.isSrgb() && linearizeSrgb);
ByteBuffer data = null; ByteBuffer data = null;
if (index >= 0 && image.getData() != null && image.getData().size() > 0) { if (index >= 0 && image.getData() != null && image.getData().size() > 0) {

@ -803,7 +803,7 @@ public class LwjglGL1Renderer implements GL1Renderer {
TextureUtil.uploadTexture(img, target, i, 0, tdc); TextureUtil.uploadTexture(img, target, i, 0, tdc);
} }
} else {*/ } else {*/
TextureUtil.uploadTexture(img, target, 0, 0); TextureUtil.uploadTexture(img, target, 0, 0, false);
//} //}
img.clearUpdateNeeded(); img.clearUpdateNeeded();
@ -854,7 +854,7 @@ public class LwjglGL1Renderer implements GL1Renderer {
public void modifyTexture(Texture tex, Image pixels, int x, int y) { public void modifyTexture(Texture tex, Image pixels, int x, int y) {
setTexture(0, tex); 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() { private void clearTextureUnits() {
@ -1202,4 +1202,8 @@ public class LwjglGL1Renderer implements GL1Renderer {
public void setMainFrameBufferSrgb(boolean srgb) { public void setMainFrameBufferSrgb(boolean srgb) {
} }
public void setLinearizeSrgbImages(boolean linearize) {
}
} }

@ -112,6 +112,7 @@ public class LwjglRenderer implements Renderer {
private final Statistics statistics = new Statistics(); private final Statistics statistics = new Statistics();
private int vpX, vpY, vpW, vpH; private int vpX, vpY, vpW, vpH;
private int clipX, clipY, clipW, clipH; private int clipX, clipY, clipW, clipH;
private boolean linearizeSrgbImages;
public LwjglRenderer() { public LwjglRenderer() {
} }
@ -390,6 +391,11 @@ public class LwjglRenderer implements Renderer {
} }
caps.add(Caps.Multisample); 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); logger.log(Level.FINE, "Caps: {0}", caps);
} }
@ -1373,7 +1379,7 @@ public class LwjglRenderer implements Renderer {
+ ":" + fb.getHeight() + " is not supported."); + ":" + 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) { if (fb.getSamples() > 1 && GLContext.getCapabilities().GL_EXT_framebuffer_multisample) {
int samples = fb.getSamples(); int samples = fb.getSamples();
@ -1902,7 +1908,7 @@ public class LwjglRenderer implements Renderer {
return; return;
} }
for (int i = 0; i < 6; i++) { 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) { } else if (target == EXTTextureArray.GL_TEXTURE_2D_ARRAY_EXT) {
if (!caps.contains(Caps.TextureArray)) { if (!caps.contains(Caps.TextureArray)) {
@ -1912,15 +1918,15 @@ public class LwjglRenderer implements Renderer {
List<ByteBuffer> data = img.getData(); List<ByteBuffer> data = img.getData();
// -1 index specifies prepare data for 2D Array // -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++) { for (int i = 0; i < data.size(); i++) {
// upload each slice of 2D array in turn // upload each slice of 2D array in turn
// this time with the appropriate index // this time with the appropriate index
TextureUtil.uploadTexture(img, target, i, 0); TextureUtil.uploadTexture(img, target, i, 0, linearizeSrgbImages);
} }
} else { } else {
TextureUtil.uploadTexture(img, target, 0, 0); TextureUtil.uploadTexture(img, target, 0, 0, linearizeSrgbImages);
} }
if (img.getMultiSamples() != imageSamples) { if (img.getMultiSamples() != imageSamples) {
@ -1978,7 +1984,7 @@ public class LwjglRenderer implements Renderer {
public void modifyTexture(Texture tex, Image pixels, int x, int y) { public void modifyTexture(Texture tex, Image pixels, int x, int y) {
setTexture(0, tex); 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() { public void clearTextureUnits() {
@ -2489,11 +2495,15 @@ public class LwjglRenderer implements Renderer {
public void setMainFrameBufferSrgb(boolean srgb) { public void setMainFrameBufferSrgb(boolean srgb) {
//Gamma correction //Gamma correction
if(srgb && GLContext.getCapabilities().GL_ARB_framebuffer_sRGB){ if(srgb && caps.contains(Caps.Srgb)){
glEnable(GL30.GL_FRAMEBUFFER_SRGB); glEnable(GL30.GL_FRAMEBUFFER_SRGB);
logger.log(Level.FINER, "SRGB FrameBuffer enabled (Gamma Correction)"); logger.log(Level.FINER, "SRGB FrameBuffer enabled (Gamma Correction)");
}else{ }else{
glDisable(GL30.GL_FRAMEBUFFER_SRGB); glDisable(GL30.GL_FRAMEBUFFER_SRGB);
} }
} }
public void setLinearizeSrgbImages(boolean linearize) {
linearizeSrgbImages = linearize;
}
} }

@ -55,6 +55,10 @@ import org.lwjgl.opengl.GLContext;
import com.jme3.renderer.RendererException; import com.jme3.renderer.RendererException;
import com.jme3.texture.Image; import com.jme3.texture.Image;
import com.jme3.texture.Image.Format; 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 { class TextureUtil {
@ -104,7 +108,7 @@ class TextureUtil {
// Depth stencil formats // Depth stencil formats
setFormat(Format.Depth24Stencil8, GL30.GL_DEPTH24_STENCIL8, GL30.GL_DEPTH_STENCIL, GL30.GL_UNSIGNED_INT_24_8, false); setFormat(Format.Depth24Stencil8, GL30.GL_DEPTH24_STENCIL8, GL30.GL_DEPTH_STENCIL, GL30.GL_UNSIGNED_INT_24_8, false);
// RGB formats // RGB formats
setFormat(Format.BGR8, GL11.GL_RGB8, EXTBgra.GL_BGR_EXT, GL11.GL_UNSIGNED_BYTE, false); 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); 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 // LTC/LATC/3Dc formats
setFormat(Format.LTC, EXTTextureCompressionLATC.GL_COMPRESSED_LUMINANCE_LATC1_EXT, GL11.GL_LUMINANCE, GL11.GL_UNSIGNED_BYTE, true); 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){ switch (fmt){
case ABGR8: case ABGR8:
if (!caps.GL_EXT_abgr){ if (!caps.GL_EXT_abgr){
@ -213,24 +230,45 @@ class TextureUtil {
} }
break; break;
} }
if(isSrgb){
return getSrgbFormat(fmt);
}
return formatToGL[fmt.ordinal()]; return formatToGL[fmt.ordinal()];
} }
public static GLImageFormat getImageFormatWithError(Format fmt) { public static GLImageFormat getImageFormatWithError(Format fmt, boolean isSrgb) {
GLImageFormat glFmt = getImageFormat(GLContext.getCapabilities(), fmt); GLImageFormat glFmt = getImageFormat(GLContext.getCapabilities(), fmt, isSrgb);
if (glFmt == null) { if (glFmt == null) {
throw new RendererException("Image format '" + fmt + "' is unsupported by the video hardware."); throw new RendererException("Image format '" + fmt + "' is unsupported by the video hardware.");
} }
return glFmt; 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, public static void uploadTexture(Image image,
int target, int target,
int index, int index,
int border){ int border,
boolean linearizeSrgb){
Image.Format fmt = image.getFormat(); Image.Format fmt = image.getFormat();
GLImageFormat glFmt = getImageFormatWithError(fmt); GLImageFormat glFmt = getImageFormatWithError(fmt, image.isSrgb() && linearizeSrgb);
ByteBuffer data; ByteBuffer data;
if (index >= 0 && image.getData() != null && image.getData().size() > 0){ if (index >= 0 && image.getData() != null && image.getData().size() > 0){
@ -384,9 +422,10 @@ class TextureUtil {
int target, int target,
int index, int index,
int x, int x,
int y) { int y,
boolean linearizeSrgb) {
Image.Format fmt = image.getFormat(); Image.Format fmt = image.getFormat();
GLImageFormat glFmt = getImageFormatWithError(fmt); GLImageFormat glFmt = getImageFormatWithError(fmt, image.isSrgb() && linearizeSrgb);
ByteBuffer data = null; ByteBuffer data = null;
if (index >= 0 && image.getData() != null && image.getData().size() > 0) { if (index >= 0 && image.getData() != null && image.getData().size() > 0) {

@ -238,6 +238,7 @@ public abstract class LwjglContext implements JmeContext {
assert false; assert false;
} }
renderer.setMainFrameBufferSrgb(settings.getGammaCorrection()); renderer.setMainFrameBufferSrgb(settings.getGammaCorrection());
renderer.setLinearizeSrgbImages(settings.getGammaCorrection());
// Init input // Init input
if (keyInput != null) { if (keyInput != null) {

Loading…
Cancel
Save