Ports all changes done on the official renderer during several months to the JOGL renderer (I thank a lot the lazy developers)

experimental
Julien Gouesse 11 years ago
parent 44f7e8bb84
commit 2bca84a43d
  1. 396
      jme3-jogl/src/main/java/com/jme3/renderer/jogl/JoglRenderer.java

@ -33,27 +33,20 @@ package com.jme3.renderer.jogl;
import com.jme3.light.LightList; import com.jme3.light.LightList;
import com.jme3.material.RenderState; import com.jme3.material.RenderState;
import com.jme3.math.ColorRGBA; import com.jme3.material.RenderState.StencilOperation;
import com.jme3.math.FastMath; import com.jme3.material.RenderState.TestFunction;
import com.jme3.math.Matrix4f; import com.jme3.math.*;
import com.jme3.math.Quaternion; import com.jme3.renderer.*;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
import com.jme3.math.Vector4f;
import com.jme3.renderer.Caps;
import com.jme3.renderer.IDList;
import com.jme3.renderer.RenderContext;
import com.jme3.renderer.Renderer;
import com.jme3.renderer.RendererException;
import com.jme3.renderer.Statistics;
import com.jme3.scene.Mesh; import com.jme3.scene.Mesh;
import com.jme3.scene.Mesh.Mode; import com.jme3.scene.Mesh.Mode;
import com.jme3.scene.VertexBuffer; import com.jme3.scene.VertexBuffer;
import com.jme3.scene.VertexBuffer.Format;
import com.jme3.scene.VertexBuffer.Type; import com.jme3.scene.VertexBuffer.Type;
import com.jme3.scene.VertexBuffer.Usage; import com.jme3.scene.VertexBuffer.Usage;
import com.jme3.shader.Attribute; import com.jme3.shader.Attribute;
import com.jme3.shader.Shader; import com.jme3.shader.Shader;
import com.jme3.shader.Shader.ShaderSource; import com.jme3.shader.Shader.ShaderSource;
import com.jme3.shader.Shader.ShaderType;
import com.jme3.shader.Uniform; import com.jme3.shader.Uniform;
import com.jme3.texture.FrameBuffer; import com.jme3.texture.FrameBuffer;
import com.jme3.texture.FrameBuffer.RenderBuffer; import com.jme3.texture.FrameBuffer.RenderBuffer;
@ -63,7 +56,16 @@ import com.jme3.texture.Texture.WrapAxis;
import com.jme3.util.BufferUtils; import com.jme3.util.BufferUtils;
import com.jme3.util.ListMap; import com.jme3.util.ListMap;
import com.jme3.util.NativeObjectManager; import com.jme3.util.NativeObjectManager;
import com.jme3.util.SafeArrayList;
import java.nio.*;
import java.util.EnumSet;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import jme3tools.converters.MipMapGenerator;
import jme3tools.shader.ShaderDebug;
import java.nio.Buffer; import java.nio.Buffer;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.FloatBuffer; import java.nio.FloatBuffer;
@ -72,16 +74,16 @@ import java.util.EnumSet;
import java.util.List; import java.util.List;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.media.nativewindow.NativeWindowFactory; import javax.media.nativewindow.NativeWindowFactory;
import javax.media.opengl.GL; import javax.media.opengl.GL;
import javax.media.opengl.GL2; import javax.media.opengl.GL2;
import javax.media.opengl.GL2ES1; import javax.media.opengl.GL2ES1;
import javax.media.opengl.GL2ES2; import javax.media.opengl.GL2ES2;
import javax.media.opengl.GL2ES3;
import javax.media.opengl.GL2GL3; import javax.media.opengl.GL2GL3;
import javax.media.opengl.GL3; import javax.media.opengl.GL3;
import javax.media.opengl.GLContext; import javax.media.opengl.GLContext;
import jme3tools.converters.MipMapGenerator;
import jme3tools.shader.ShaderDebug;
public class JoglRenderer implements Renderer { public class JoglRenderer implements Renderer {
@ -136,11 +138,13 @@ public class JoglRenderer implements Renderer {
nameBuf.rewind(); nameBuf.rewind();
} }
public Statistics getStatistics() { @Override
public Statistics getStatistics() {
return statistics; return statistics;
} }
public EnumSet<Caps> getCaps() { @Override
public EnumSet<Caps> getCaps() {
return caps; return caps;
} }
@ -302,7 +306,7 @@ public class JoglRenderer implements Renderer {
caps.add(Caps.PackedDepthStencilBuffer); caps.add(Caps.PackedDepthStencilBuffer);
} }
if (gl.isExtensionAvailable("GL_ARB_draw_instanced")) { if (gl.isExtensionAvailable("GL_ARB_draw_instanced") || gl.isExtensionAvailable("GL_ARB_instanced_arrays")) {
caps.add(Caps.MeshInstancing); caps.add(Caps.MeshInstancing);
} }
@ -335,12 +339,13 @@ public class JoglRenderer implements Renderer {
boolean latc = gl.isExtensionAvailable("GL_EXT_texture_compression_latc"); boolean latc = gl.isExtensionAvailable("GL_EXT_texture_compression_latc");
//FIXME ignore atdc? //FIXME ignore atdc?
boolean atdc = gl.isExtensionAvailable("GL_ATI_texture_compression_3dc"); //boolean atdc = gl.isExtensionAvailable("GL_ATI_texture_compression_3dc");
if (latc || atdc) { if (latc /*|| atdc*/) {
caps.add(Caps.TextureCompressionLATC); caps.add(Caps.TextureCompressionLATC);
} }
if (gl.isExtensionAvailable("GL_EXT_packed_float")) { if (gl.isExtensionAvailable("GL_EXT_packed_float") || gl.isExtensionAvailable("GL_VERSION_3_0")) {
// This format is part of the OGL3 specification
caps.add(Caps.PackedFloatColorBuffer); caps.add(Caps.PackedFloatColorBuffer);
if (gl.isExtensionAvailable("GL_ARB_half_float_pixel")) { if (gl.isExtensionAvailable("GL_ARB_half_float_pixel")) {
// because textures are usually uploaded as RGB16F // because textures are usually uploaded as RGB16F
@ -349,11 +354,11 @@ public class JoglRenderer implements Renderer {
} }
} }
if (gl.isExtensionAvailable("GL_EXT_texture_array")) { if (gl.isExtensionAvailable("GL_EXT_texture_array") || gl.isExtensionAvailable("GL_VERSION_3_0")) {
caps.add(Caps.TextureArray); caps.add(Caps.TextureArray);
} }
if (gl.isExtensionAvailable("GL_EXT_texture_shared_exponent")) { if (gl.isExtensionAvailable("GL_EXT_texture_shared_exponent") || gl.isExtensionAvailable("GL_VERSION_3_0")) {
caps.add(Caps.SharedExponentTexture); caps.add(Caps.SharedExponentTexture);
} }
@ -417,14 +422,15 @@ public class JoglRenderer implements Renderer {
} }
//supports sRGB pipeline //supports sRGB pipeline
if (gl.isExtensionAvailable("GL_ARB_framebuffer_sRGB") && gl.isExtensionAvailable("GL_EXT_texture_sRGB")){ if ((gl.isExtensionAvailable("GL_ARB_framebuffer_sRGB") && gl.isExtensionAvailable("GL_EXT_texture_sRGB")) || gl.isExtensionAvailable("GL_VERSION_3_0")){
caps.add(Caps.Srgb); caps.add(Caps.Srgb);
} }
logger.log(Level.FINE, "Caps: {0}", caps); logger.log(Level.FINE, "Caps: {0}", caps);
} }
public void invalidateState() { @Override
public void invalidateState() {
context.reset(); context.reset();
boundShader = null; boundShader = null;
lastFb = null; lastFb = null;
@ -436,35 +442,39 @@ public class JoglRenderer implements Renderer {
initialReadBuf = intBuf1.get(0); initialReadBuf = intBuf1.get(0);
} }
public void resetGLObjects() { @Override
public void resetGLObjects() {
logger.log(Level.FINE, "Reseting objects and invalidating state"); logger.log(Level.FINE, "Reseting objects and invalidating state");
objManager.resetObjects(); objManager.resetObjects();
statistics.clearMemory(); statistics.clearMemory();
invalidateState(); invalidateState();
} }
public void cleanup() { @Override
public void cleanup() {
logger.log(Level.FINE, "Deleting objects and invalidating state"); logger.log(Level.FINE, "Deleting objects and invalidating state");
objManager.deleteAllObjects(this); objManager.deleteAllObjects(this);
statistics.clearMemory(); statistics.clearMemory();
invalidateState(); invalidateState();
} }
private void checkCap(Caps cap) { // private void checkCap(Caps cap) {
if (!caps.contains(cap)) { // if (!caps.contains(cap)) {
throw new UnsupportedOperationException("Required capability missing: " + cap.name()); // throw new UnsupportedOperationException("Required capability missing: " + cap.name());
} // }
} // }
/*********************************************************************\ /*********************************************************************\
|* Render State *| |* Render State *|
\*********************************************************************/ \*********************************************************************/
public void setDepthRange(float start, float end) { @Override
public void setDepthRange(float start, float end) {
GL gl = GLContext.getCurrentGL(); GL gl = GLContext.getCurrentGL();
gl.glDepthRange(start, end); gl.glDepthRange(start, end);
} }
public void clearBuffers(boolean color, boolean depth, boolean stencil) { @Override
public void clearBuffers(boolean color, boolean depth, boolean stencil) {
GL gl = GLContext.getCurrentGL(); GL gl = GLContext.getCurrentGL();
int bits = 0; int bits = 0;
if (color) { if (color) {
@ -495,12 +505,14 @@ public class JoglRenderer implements Renderer {
} }
} }
public void setBackgroundColor(ColorRGBA color) { @Override
public void setBackgroundColor(ColorRGBA color) {
GL gl = GLContext.getCurrentGL(); GL gl = GLContext.getCurrentGL();
gl.glClearColor(color.r, color.g, color.b, color.a); gl.glClearColor(color.r, color.g, color.b, color.a);
} }
public void setAlphaToCoverage(boolean value) { @Override
public void setAlphaToCoverage(boolean value) {
if (caps.contains(Caps.Multisample)) { if (caps.contains(Caps.Multisample)) {
GL gl = GLContext.getCurrentGL(); GL gl = GLContext.getCurrentGL();
if (value) { if (value) {
@ -511,7 +523,8 @@ public class JoglRenderer implements Renderer {
} }
} }
public void applyRenderState(RenderState state) { @Override
public void applyRenderState(RenderState state) {
GL gl = GLContext.getCurrentGL(); GL gl = GLContext.getCurrentGL();
if (state.isWireframe() && !context.wireframe) { if (state.isWireframe() && !context.wireframe) {
if (gl.isGL2GL3()) { if (gl.isGL2GL3()) {
@ -537,22 +550,23 @@ public class JoglRenderer implements Renderer {
gl.glDepthFunc(convertTestFunction(state.getDepthFunc())); gl.glDepthFunc(convertTestFunction(state.getDepthFunc()));
context.depthFunc = state.getDepthFunc(); context.depthFunc = state.getDepthFunc();
} }
if (gl.isGL2ES1()) {
if (state.isAlphaTest() && context.alphaTestFallOff == 0) { if (state.isAlphaTest() && !context.alphaTestEnabled) {
gl.glEnable(GL2ES1.GL_ALPHA_TEST); gl.glEnable(GL2ES1.GL_ALPHA_TEST);
if (gl.isGL2ES1()) { gl.getGL2ES1().glAlphaFunc(convertTestFunction(context.alphaFunc), context.alphaTestFallOff);
gl.getGL2ES1().glAlphaFunc(convertTestFunction(context.alphaFunc), state.getAlphaFallOff()); context.alphaTestEnabled = true;
} } else if (!state.isAlphaTest() && context.alphaTestEnabled) {
context.alphaTestFallOff = state.getAlphaFallOff();
} else if (!state.isAlphaTest() && context.alphaTestFallOff != 0) {
if (gl.isGL2ES1()) {
gl.glDisable(GL2ES1.GL_ALPHA_TEST); gl.glDisable(GL2ES1.GL_ALPHA_TEST);
context.alphaTestEnabled = false;
}
if (state.getAlphaFallOff() != context.alphaTestFallOff) {
gl.getGL2ES1().glAlphaFunc(convertTestFunction(context.alphaFunc), context.alphaTestFallOff);
context.alphaTestFallOff = state.getAlphaFallOff();
}
if (state.getAlphaFunc() != context.alphaFunc) {
gl.getGL2ES1().glAlphaFunc(convertTestFunction(state.getAlphaFunc()), context.alphaTestFallOff);
context.alphaFunc = state.getAlphaFunc();
} }
context.alphaTestFallOff = 0;
}
if (state.getAlphaFunc() != context.alphaFunc && gl.isGL2ES1()) {
gl.getGL2ES1().glAlphaFunc(convertTestFunction(context.alphaFunc), state.getAlphaFallOff());
context.alphaFunc = state.getAlphaFunc();
} }
if (state.isDepthWrite() && !context.depthWriteEnabled) { if (state.isDepthWrite() && !context.depthWriteEnabled) {
@ -783,7 +797,8 @@ public class JoglRenderer implements Renderer {
/*********************************************************************\ /*********************************************************************\
|* Camera and World transforms *| |* Camera and World transforms *|
\*********************************************************************/ \*********************************************************************/
public void setViewPort(int x, int y, int w, int h) { @Override
public void setViewPort(int x, int y, int w, int h) {
if (x != vpX || vpY != y || vpW != w || vpH != h) { if (x != vpX || vpY != y || vpW != w || vpH != h) {
GL gl = GLContext.getCurrentGL(); GL gl = GLContext.getCurrentGL();
gl.glViewport(x, y, w, h); gl.glViewport(x, y, w, h);
@ -794,7 +809,8 @@ public class JoglRenderer implements Renderer {
} }
} }
public void setClipRect(int x, int y, int width, int height) { @Override
public void setClipRect(int x, int y, int width, int height) {
GL gl = GLContext.getCurrentGL(); GL gl = GLContext.getCurrentGL();
if (!context.clipRectEnabled) { if (!context.clipRectEnabled) {
gl.glEnable(GL.GL_SCISSOR_TEST); gl.glEnable(GL.GL_SCISSOR_TEST);
@ -809,7 +825,8 @@ public class JoglRenderer implements Renderer {
} }
} }
public void clearClipRect() { @Override
public void clearClipRect() {
if (context.clipRectEnabled) { if (context.clipRectEnabled) {
GL gl = GLContext.getCurrentGL(); GL gl = GLContext.getCurrentGL();
gl.glDisable(GL.GL_SCISSOR_TEST); gl.glDisable(GL.GL_SCISSOR_TEST);
@ -822,14 +839,17 @@ public class JoglRenderer implements Renderer {
} }
} }
public void onFrame() { @Override
public void onFrame() {
objManager.deleteUnused(this); objManager.deleteUnused(this);
} }
public void setViewProjectionMatrices(Matrix4f viewMatrix, Matrix4f projMatrix) { @Override
public void setViewProjectionMatrices(Matrix4f viewMatrix, Matrix4f projMatrix) {
} }
public void setWorldMatrix(Matrix4f worldMatrix) { @Override
public void setWorldMatrix(Matrix4f worldMatrix) {
} }
/*********************************************************************\ /*********************************************************************\
@ -990,7 +1010,8 @@ public class JoglRenderer implements Renderer {
* (Non-javadoc) * (Non-javadoc)
* Only used for fixed-function. Ignored. * Only used for fixed-function. Ignored.
*/ */
public void setLighting(LightList list) { @Override
public void setLighting(LightList list) {
} }
public int convertShaderType(Shader.ShaderType type) { public int convertShaderType(Shader.ShaderType type) {
@ -1000,7 +1021,7 @@ public class JoglRenderer implements Renderer {
case Vertex: case Vertex:
return GL2ES2.GL_VERTEX_SHADER; return GL2ES2.GL_VERTEX_SHADER;
// case Geometry: // case Geometry:
// return ARBGeometryShader4.GL_GEOMETRY_SHADER_ARB; // return GL3.GL_GEOMETRY_SHADER_ARB;
default: default:
throw new UnsupportedOperationException("Unrecognized shader type."); throw new UnsupportedOperationException("Unrecognized shader type.");
} }
@ -1011,7 +1032,14 @@ public class JoglRenderer implements Renderer {
GL gl = GLContext.getCurrentGL(); GL gl = GLContext.getCurrentGL();
if (id == -1) { if (id == -1) {
// Create id // Create id
id = gl.getGL2ES2().glCreateShader(convertShaderType(source.getType())); // if (gl.isGL2ES2()) {
id = gl.getGL2ES2().glCreateShader(convertShaderType(source.getType()));
// }
// else {
// if (gl.isGL2()) {
// id = gl.getGL2().glCreateShaderObjectARB(convertShaderType(source.getType()));
// }
// }
if (id <= 0) { if (id <= 0) {
throw new RendererException("Invalid ID received when trying to create shader."); throw new RendererException("Invalid ID received when trying to create shader.");
} }
@ -1118,7 +1146,7 @@ public class JoglRenderer implements Renderer {
gl.getGL2ES2().glAttachShader(id, source.getId()); gl.getGL2ES2().glAttachShader(id, source.getId());
} }
if (caps.contains(Caps.OpenGL30) && gl.isGL2GL3()) { if (gl.isGL2GL3() && gl.isExtensionAvailable("GL_EXT_gpu_shader4")) {
// Check if GLSL version is 1.5 for shader // Check if GLSL version is 1.5 for shader
gl.getGL2GL3().glBindFragDataLocation(id, 0, "outFragColor"); gl.getGL2GL3().glBindFragDataLocation(id, 0, "outFragColor");
// For MRT // For MRT
@ -1167,14 +1195,15 @@ public class JoglRenderer implements Renderer {
} }
} else { } else {
if (infoLog != null) { if (infoLog != null) {
throw new RendererException("Shader link failure, shader:" + shader + "\n" + infoLog); throw new RendererException("Shader failed to link, shader:" + shader + "\n" + infoLog);
} else { } else {
throw new RendererException("Shader link failure, shader:" + shader + " info: <not provided>"); throw new RendererException("Shader failed to link, shader:" + shader + "\ninfo: <not provided>");
} }
} }
} }
public void setShader(Shader shader) { @Override
public void setShader(Shader shader) {
if (shader == null) { if (shader == null) {
throw new IllegalArgumentException("Shader cannot be null"); throw new IllegalArgumentException("Shader cannot be null");
} else { } else {
@ -1192,7 +1221,8 @@ public class JoglRenderer implements Renderer {
} }
} }
public void deleteShaderSource(ShaderSource source) { @Override
public void deleteShaderSource(ShaderSource source) {
if (source.getId() < 0) { if (source.getId() < 0) {
logger.warning("Shader source is not uploaded to GPU, cannot delete."); logger.warning("Shader source is not uploaded to GPU, cannot delete.");
return; return;
@ -1203,7 +1233,8 @@ public class JoglRenderer implements Renderer {
source.resetObject(); source.resetObject();
} }
public void deleteShader(Shader shader) { @Override
public void deleteShader(Shader shader) {
if (shader.getId() == -1) { if (shader.getId() == -1) {
logger.warning("Shader is not uploaded to GPU, cannot delete."); logger.warning("Shader is not uploaded to GPU, cannot delete.");
return; return;
@ -1225,22 +1256,24 @@ public class JoglRenderer implements Renderer {
/*********************************************************************\ /*********************************************************************\
|* Framebuffers *| |* Framebuffers *|
\*********************************************************************/ \*********************************************************************/
public void copyFrameBuffer(FrameBuffer src, FrameBuffer dst) { @Override
public void copyFrameBuffer(FrameBuffer src, FrameBuffer dst) {
copyFrameBuffer(src, dst, true); copyFrameBuffer(src, dst, true);
} }
public void copyFrameBuffer(FrameBuffer src, FrameBuffer dst, boolean copyDepth) { @Override
public void copyFrameBuffer(FrameBuffer src, FrameBuffer dst, boolean copyDepth) {
GL gl = GLContext.getCurrentGL(); GL gl = GLContext.getCurrentGL();
if (gl.isExtensionAvailable("GL_EXT_framebuffer_blit") && gl.isGL2GL3()) { if (gl.isExtensionAvailable("GL_EXT_framebuffer_blit") && gl.isGL2GL3()) {
int srcX0 = 0; int srcX0 = 0;
int srcY0 = 0; int srcY0 = 0;
int srcX1/* = 0*/; int srcX1;
int srcY1/* = 0*/; int srcY1;
int dstX0 = 0; int dstX0 = 0;
int dstY0 = 0; int dstY0 = 0;
int dstX1/* = 0*/; int dstX1;
int dstY1/* = 0*/; int dstY1;
int prevFBO = context.boundFBO; int prevFBO = context.boundFBO;
@ -1472,8 +1505,7 @@ public class JoglRenderer implements Renderer {
if (attachmentSlot == -100) { if (attachmentSlot == -100) {
return GL.GL_DEPTH_ATTACHMENT; return GL.GL_DEPTH_ATTACHMENT;
} else if (attachmentSlot < 0 || attachmentSlot >= 16) { } else if (attachmentSlot < 0 || attachmentSlot >= 16) {
throw new UnsupportedOperationException("Invalid FBO attachment slot: " throw new UnsupportedOperationException("Invalid FBO attachment slot: " + attachmentSlot);
+ attachmentSlot);
} }
return GL.GL_COLOR_ATTACHMENT0 + attachmentSlot; return GL.GL_COLOR_ATTACHMENT0 + attachmentSlot;
@ -1492,9 +1524,11 @@ public class JoglRenderer implements Renderer {
setupTextureParams(tex); setupTextureParams(tex);
} }
gl.glFramebufferTexture2D(GL.GL_FRAMEBUFFER, convertAttachmentSlot(rb.getSlot()), gl.glFramebufferTexture2D(GL.GL_FRAMEBUFFER,
convertAttachmentSlot(rb.getSlot()),
convertTextureType(tex.getType(), image.getMultiSamples(), rb.getFace()), convertTextureType(tex.getType(), image.getMultiSamples(), rb.getFace()),
image.getId(), 0); image.getId(),
0);
} }
public void updateFrameBufferAttachment(FrameBuffer fb, RenderBuffer rb) { public void updateFrameBufferAttachment(FrameBuffer fb, RenderBuffer rb) {
@ -1568,11 +1602,19 @@ public class JoglRenderer implements Renderer {
return samplePositions; return samplePositions;
} }
public void setMainFrameBufferOverride(FrameBuffer fb) { @Override
public void setMainFrameBufferOverride(FrameBuffer fb) {
mainFbOverride = fb; mainFbOverride = fb;
} }
public void setFrameBuffer(FrameBuffer fb) { @Override
public void setFrameBuffer(FrameBuffer fb) {
GL gl = GLContext.getCurrentGL();
if (!gl.isExtensionAvailable("GL_EXT_framebuffer_object")) {
throw new RendererException("Framebuffer objects are not supported" +
" by the video hardware");
}
if (fb == null && mainFbOverride != null) { if (fb == null && mainFbOverride != null) {
fb = mainFbOverride; fb = mainFbOverride;
} }
@ -1582,8 +1624,7 @@ public class JoglRenderer implements Renderer {
return; return;
} }
} }
GL gl = GLContext.getCurrentGL();
// generate mipmaps for last FB if needed // generate mipmaps for last FB if needed
if (lastFb != null) { if (lastFb != null) {
for (int i = 0; i < lastFb.getNumColorBuffers(); i++) { for (int i = 0; i < lastFb.getNumColorBuffers(); i++) {
@ -1711,7 +1752,8 @@ public class JoglRenderer implements Renderer {
} }
} }
public void readFrameBuffer(FrameBuffer fb, ByteBuffer byteBuf) { @Override
public void readFrameBuffer(FrameBuffer fb, ByteBuffer byteBuf) {
GL gl = GLContext.getCurrentGL(); GL gl = GLContext.getCurrentGL();
if (fb != null) { if (fb != null) {
RenderBuffer rb = fb.getColorBuffer(); RenderBuffer rb = fb.getColorBuffer();
@ -1740,7 +1782,8 @@ public class JoglRenderer implements Renderer {
gl.glDeleteRenderbuffers(1, intBuf1); gl.glDeleteRenderbuffers(1, intBuf1);
} }
public void deleteFrameBuffer(FrameBuffer fb) { @Override
public void deleteFrameBuffer(FrameBuffer fb) {
if (fb.getId() != -1) { if (fb.getId() != -1) {
GL gl = GLContext.getCurrentGL(); GL gl = GLContext.getCurrentGL();
if (context.boundFBO == fb.getId()) { if (context.boundFBO == fb.getId()) {
@ -1767,6 +1810,12 @@ public class JoglRenderer implements Renderer {
|* Textures *| |* Textures *|
\*********************************************************************/ \*********************************************************************/
private int convertTextureType(Texture.Type type, int samples, int face) { private int convertTextureType(Texture.Type type, int samples, int face) {
GL gl = GLContext.getCurrentGL();
if (samples > 1 && !gl.isExtensionAvailable("GL_ARB_texture_multisample")) {
throw new RendererException("Multisample textures are not supported" +
" by the video hardware.");
}
switch (type) { switch (type) {
case TwoDimensional: case TwoDimensional:
if (samples > 1) { if (samples > 1) {
@ -1830,7 +1879,7 @@ public class JoglRenderer implements Renderer {
case BorderClamp: case BorderClamp:
return GL2GL3.GL_CLAMP_TO_BORDER; return GL2GL3.GL_CLAMP_TO_BORDER;
case Clamp: case Clamp:
return GL2.GL_CLAMP; // Falldown intentional.
case EdgeClamp: case EdgeClamp:
return GL.GL_CLAMP_TO_EDGE; return GL.GL_CLAMP_TO_EDGE;
case Repeat: case Repeat:
@ -1904,8 +1953,7 @@ public class JoglRenderer implements Renderer {
* *
* @param img The image to upload * @param img The image to upload
* @param type How the data in the image argument should be interpreted. * @param type How the data in the image argument should be interpreted.
* @param unit The texture slot to be used to upload the image, not * @param unit The texture slot to be used to upload the image, not important
* important
*/ */
public void updateTexImageData(Image img, Texture.Type type, int unit) { public void updateTexImageData(Image img, Texture.Type type, int unit) {
int texId = img.getId(); int texId = img.getId();
@ -1934,22 +1982,28 @@ public class JoglRenderer implements Renderer {
} }
if (!img.hasMipmaps() && img.isGeneratedMipmapsRequired()) { if (!img.hasMipmaps() && img.isGeneratedMipmapsRequired()) {
// No pregenerated mips available, // Image does not have mipmaps, but they are required.
// generate from base level if required // Generate from base level.
if (!gl.isExtensionAvailable("GL_VERSION_3_0")) { if (!gl.isExtensionAvailable("GL_VERSION_3_0")) {
if (gl.isGL2ES1()) { if (gl.isGL2ES1()) {
gl.glTexParameteri(target, GL2ES1.GL_GENERATE_MIPMAP, GL.GL_TRUE); gl.glTexParameteri(target, GL2ES1.GL_GENERATE_MIPMAP, GL.GL_TRUE);
img.setMipmapsGenerated(true);
} }
img.setMipmapsGenerated(true); } else {
// For OpenGL3 and up.
// We'll generate mipmaps via glGenerateMipmapEXT (see below)
} }
} else { } else if (img.hasMipmaps()) {
// Image already has mipmaps or no mipmap generation desired. // Image already has mipmaps, set the max level based on the
// glTexParameteri(target, GL_TEXTURE_BASE_LEVEL, 0 ); // number of mipmaps we have.
if (img.getMipMapSizes() != null) { if (gl.isGL2GL3()) {
if (gl.isGL2GL3()) { gl.glTexParameteri(target, GL2ES3.GL_TEXTURE_MAX_LEVEL, img.getMipMapSizes().length - 1);
gl.glTexParameteri(target, GL2GL3.GL_TEXTURE_MAX_LEVEL, img.getMipMapSizes().length - 1);
}
} }
} else {
// Image does not have mipmaps and they are not required.
// Specify that that the texture has no mipmaps.
gl.glTexParameteri(target, GL2ES3.GL_TEXTURE_MAX_LEVEL, 0);
} }
int imageSamples = img.getMultiSamples(); int imageSamples = img.getMultiSamples();
@ -1977,17 +2031,6 @@ public class JoglRenderer implements Renderer {
} }
} }
if (target == GL.GL_TEXTURE_CUBE_MAP) {
// Check max texture size before upload
if (img.getWidth() > maxCubeTexSize || img.getHeight() > maxCubeTexSize) {
throw new RendererException("Cannot upload cubemap " + img + ". The maximum supported cubemap resolution is " + maxCubeTexSize);
}
} else {
if (img.getWidth() > maxTexSize || img.getHeight() > maxTexSize) {
throw new RendererException("Cannot upload texture " + img + ". The maximum supported texture resolution is " + maxTexSize);
}
}
if (target == GL.GL_TEXTURE_CUBE_MAP) { if (target == GL.GL_TEXTURE_CUBE_MAP) {
List<ByteBuffer> data = img.getData(); List<ByteBuffer> data = img.getData();
if (data.size() != 6) { if (data.size() != 6) {
@ -1999,9 +2042,15 @@ public class JoglRenderer implements Renderer {
TextureUtil.uploadTexture(img, GL.GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, i, 0, linearizeSrgbImages); 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) {
if (!caps.contains(Caps.TextureArray)) {
throw new RendererException("Texture arrays not supported by graphics hardware");
}
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, linearizeSrgbImages); 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
@ -2028,7 +2077,8 @@ public class JoglRenderer implements Renderer {
img.clearUpdateNeeded(); img.clearUpdateNeeded();
} }
public void setTexture(int unit, Texture tex) { @Override
public void setTexture(int unit, Texture tex) {
GL gl = GLContext.getCurrentGL(); GL gl = GLContext.getCurrentGL();
Image image = tex.getImage(); Image image = tex.getImage();
if (image.isUpdateNeeded() || (image.isGeneratedMipmapsRequired() && !image.isMipmapsGenerated())) { if (image.isUpdateNeeded() || (image.isGeneratedMipmapsRequired() && !image.isMipmapsGenerated())) {
@ -2065,7 +2115,8 @@ public class JoglRenderer implements Renderer {
setupTextureParams(tex); setupTextureParams(tex);
} }
public void modifyTexture(Texture tex, Image pixels, int x, int y) { @Override
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, linearizeSrgbImages); TextureUtil.uploadSubTexture(pixels, convertTextureType(tex.getType(), tex.getImage().getMultiSamples(), -1), 0, x, y, linearizeSrgbImages);
} }
@ -2087,7 +2138,8 @@ public class JoglRenderer implements Renderer {
context.textureIndexList.copyNewToOld();*/ context.textureIndexList.copyNewToOld();*/
} }
public void deleteImage(Image image) { @Override
public void deleteImage(Image image) {
int texId = image.getId(); int texId = image.getId();
if (texId != -1) { if (texId != -1) {
intBuf1.put(0, texId); intBuf1.put(0, texId);
@ -2130,9 +2182,6 @@ public class JoglRenderer implements Renderer {
return GL2ES2.GL_INT; return GL2ES2.GL_INT;
case UnsignedInt: case UnsignedInt:
return GL.GL_UNSIGNED_INT; return GL.GL_UNSIGNED_INT;
// case Half:
// return NVHalfFloat.GL_HALF_FLOAT_NV;
// return ARBHalfFloatVertex.GL_HALF_FLOAT;
case Float: case Float:
return GL.GL_FLOAT; return GL.GL_FLOAT;
case Double: case Double:
@ -2143,7 +2192,8 @@ public class JoglRenderer implements Renderer {
} }
} }
public void updateBufferData(VertexBuffer vb) { @Override
public void updateBufferData(VertexBuffer vb) {
GL gl = GLContext.getCurrentGL(); GL gl = GLContext.getCurrentGL();
int bufId = vb.getId(); int bufId = vb.getId();
boolean created = false; boolean created = false;
@ -2195,7 +2245,8 @@ public class JoglRenderer implements Renderer {
vb.clearUpdateNeeded(); vb.clearUpdateNeeded();
} }
public void deleteBuffer(VertexBuffer vb) { @Override
public void deleteBuffer(VertexBuffer vb) {
GL gl = GLContext.getCurrentGL(); GL gl = GLContext.getCurrentGL();
int bufId = vb.getId(); int bufId = vb.getId();
if (bufId != -1) { if (bufId != -1) {
@ -2210,11 +2261,14 @@ public class JoglRenderer implements Renderer {
} }
public void clearVertexAttribs() { public void clearVertexAttribs() {
GL gl = GLContext.getCurrentGL();
IDList attribList = context.attribIndexList; IDList attribList = context.attribIndexList;
for (int i = 0; i < attribList.oldLen; i++) { for (int i = 0; i < attribList.oldLen; i++) {
int idx = attribList.oldList[i]; int idx = attribList.oldList[i];
GL gl = GLContext.getCurrentGL();
gl.getGL2ES2().glDisableVertexAttribArray(idx); gl.getGL2ES2().glDisableVertexAttribArray(idx);
if (context.boundAttribs[idx].isInstanced() && gl.isGL3ES3()) {
gl.getGL3ES3().glVertexAttribDivisor(idx, 0);
}
context.boundAttribs[idx] = null; context.boundAttribs[idx] = null;
} }
context.attribIndexList.copyNewToOld(); context.attribIndexList.copyNewToOld();
@ -2248,15 +2302,34 @@ public class JoglRenderer implements Renderer {
attrib.setLocation(loc); attrib.setLocation(loc);
} }
} }
if (vb.isInstanced()) {
if (!gl.isExtensionAvailable("GL_ARB_instanced_arrays")
|| !gl.isExtensionAvailable("GL_ARB_draw_instanced")) {
throw new RendererException("Instancing is required, "
+ "but not supported by the "
+ "graphics hardware");
}
}
int slotsRequired = 1;
if (vb.getNumComponents() > 4) {
if (vb.getNumComponents() % 4 != 0) {
throw new RendererException("Number of components in multi-slot "
+ "buffers must be divisible by 4");
}
slotsRequired = vb.getNumComponents() / 4;
}
if (vb.isUpdateNeeded() && idb == null) { if (vb.isUpdateNeeded() && idb == null) {
updateBufferData(vb); updateBufferData(vb);
} }
VertexBuffer[] attribs = context.boundAttribs; VertexBuffer[] attribs = context.boundAttribs;
if (!context.attribIndexList.moveToNew(loc)) { for (int i = 0; i < slotsRequired; i++) {
gl.getGL2ES2().glEnableVertexAttribArray(loc); if (!context.attribIndexList.moveToNew(loc + i)) {
//System.out.println("Enabled ATTRIB IDX: "+loc); gl.getGL2ES2().glEnableVertexAttribArray(loc + i);
//System.out.println("Enabled ATTRIB IDX: "+loc + i);
}
} }
if (attribs[loc] != vb) { if (attribs[loc] != vb) {
// NOTE: Use id from interleaved buffer if specified // NOTE: Use id from interleaved buffer if specified
@ -2270,14 +2343,43 @@ public class JoglRenderer implements Renderer {
//statistics.onVertexBufferUse(vb, false); //statistics.onVertexBufferUse(vb, false);
} }
gl.getGL2ES2().glVertexAttribPointer(loc, if (slotsRequired == 1) {
gl.getGL2ES2().glVertexAttribPointer(loc,
vb.getNumComponents(), vb.getNumComponents(),
convertFormat(vb.getFormat()), convertFormat(vb.getFormat()),
vb.isNormalized(), vb.isNormalized(),
vb.getStride(), vb.getStride(),
vb.getOffset()); vb.getOffset());
} else {
attribs[loc] = vb; for (int i = 0; i < slotsRequired; i++) {
// The pointer maps the next 4 floats in the slot.
// E.g.
// P1: XXXX____________XXXX____________
// P2: ____XXXX____________XXXX________
// P3: ________XXXX____________XXXX____
// P4: ____________XXXX____________XXXX
// stride = 4 bytes in float * 4 floats in slot * num slots
// offset = 4 bytes in float * 4 floats in slot * slot index
gl.getGL2ES2().glVertexAttribPointer(loc + i,
4,
convertFormat(vb.getFormat()),
vb.isNormalized(),
4 * 4 * slotsRequired,
4 * 4 * i);
}
}
for (int i = 0; i < slotsRequired; i++) {
int slot = loc + i;
if (vb.isInstanced() && (attribs[slot] == null || !attribs[slot].isInstanced())) {
// non-instanced -> instanced
gl.getGL3ES3().glVertexAttribDivisor(slot, vb.getInstanceSpan());
} else if (!vb.isInstanced() && attribs[slot] != null && attribs[slot].isInstanced()) {
// instanced -> non-instanced
gl.getGL3ES3().glVertexAttribDivisor(slot, 0);
}
attribs[slot] = vb;
}
} }
} else { } else {
throw new IllegalStateException("Cannot render mesh without shader bound"); throw new IllegalStateException("Cannot render mesh without shader bound");
@ -2289,14 +2391,15 @@ public class JoglRenderer implements Renderer {
} }
public void drawTriangleArray(Mesh.Mode mode, int count, int vertCount) { public void drawTriangleArray(Mesh.Mode mode, int count, int vertCount) {
boolean useInstancing = count > 1 && caps.contains(Caps.MeshInstancing);
GL gl = GLContext.getCurrentGL(); GL gl = GLContext.getCurrentGL();
if (count > 1) { if (useInstancing) {
if (gl.isGL2GL3()) { if (gl.isGL2GL3()) {
gl.getGL2GL3().glDrawArraysInstanced(convertElementMode(mode), 0, gl.getGL2GL3().glDrawArraysInstanced(convertElementMode(mode), 0,
vertCount, count); vertCount, count);
} }
} else { } else {
gl.glDrawArrays(convertElementMode(mode), 0, vertCount); gl.glDrawArrays(convertElementMode(mode), 0, vertCount);
} }
} }
@ -2373,7 +2476,6 @@ public class JoglRenderer implements Renderer {
} }
} }
//FIXME check whether elSize is required
curOffset += elementLength * elSize; curOffset += elementLength * elSize;
} }
} else { } else {
@ -2432,7 +2534,7 @@ public class JoglRenderer implements Renderer {
} }
} }
public void updateVertexArray(Mesh mesh) { public void updateVertexArray(Mesh mesh, VertexBuffer instanceData) {
int id = mesh.getId(); int id = mesh.getId();
GL gl = GLContext.getCurrentGL(); GL gl = GLContext.getCurrentGL();
@ -2456,6 +2558,10 @@ public class JoglRenderer implements Renderer {
if (interleavedData != null && interleavedData.isUpdateNeeded()) { if (interleavedData != null && interleavedData.isUpdateNeeded()) {
updateBufferData(interleavedData); updateBufferData(interleavedData);
} }
if (instanceData != null) {
setVertexAttrib(instanceData, null);
}
for (VertexBuffer vb : mesh.getBufferList().getArray()) { for (VertexBuffer vb : mesh.getBufferList().getArray()) {
if (vb.getBufferType() == Type.InterleavedData if (vb.getBufferType() == Type.InterleavedData
@ -2474,9 +2580,9 @@ public class JoglRenderer implements Renderer {
} }
} }
private void renderMeshVertexArray(Mesh mesh, int lod, int count) { private void renderMeshVertexArray(Mesh mesh, int lod, int count, VertexBuffer instanceData) {
if (mesh.getId() == -1) { if (mesh.getId() == -1) {
updateVertexArray(mesh); updateVertexArray(mesh, instanceData);
} else { } else {
// TODO: Check if it was updated // TODO: Check if it was updated
} }
@ -2505,25 +2611,30 @@ public class JoglRenderer implements Renderer {
clearTextureUnits(); clearTextureUnits();
} }
private void renderMeshDefault(Mesh mesh, int lod, int count) { private void renderMeshDefault(Mesh mesh, int lod, int count, VertexBuffer[] instanceData) {
VertexBuffer indices;
// Here while count is still passed in. Can be removed when/if
// the method is collapsed again. -pspeed
count = Math.max(mesh.getInstanceCount(), count);
VertexBuffer interleavedData = mesh.getBuffer(Type.InterleavedData); VertexBuffer interleavedData = mesh.getBuffer(Type.InterleavedData);
if (interleavedData != null && interleavedData.isUpdateNeeded()) { if (interleavedData != null && interleavedData.isUpdateNeeded()) {
updateBufferData(interleavedData); updateBufferData(interleavedData);
} }
// IntMap<VertexBuffer> buffers = mesh.getBuffers(); VertexBuffer indices;
SafeArrayList<VertexBuffer> buffersList = mesh.getBufferList();
if (mesh.getNumLodLevels() > 0) { if (mesh.getNumLodLevels() > 0) {
indices = mesh.getLodLevel(lod); indices = mesh.getLodLevel(lod);
} else { } else {
indices = mesh.getBuffer(Type.Index); indices = mesh.getBuffer(Type.Index);
} }
// for (Entry<VertexBuffer> entry : buffers) { if (instanceData != null) {
// VertexBuffer vb = entry.getValue(); for (VertexBuffer vb : instanceData) {
setVertexAttrib(vb, null);
}
}
for (VertexBuffer vb : mesh.getBufferList().getArray()) { for (VertexBuffer vb : mesh.getBufferList().getArray()) {
if (vb.getBufferType() == Type.InterleavedData if (vb.getBufferType() == Type.InterleavedData
|| vb.getUsage() == Usage.CpuOnly // ignore cpu-only buffers || vb.getUsage() == Usage.CpuOnly // ignore cpu-only buffers
@ -2549,7 +2660,8 @@ public class JoglRenderer implements Renderer {
clearTextureUnits(); clearTextureUnits();
} }
public void renderMesh(Mesh mesh, int lod, int count, VertexBuffer[] instanceData) { @Override
public void renderMesh(Mesh mesh, int lod, int count, VertexBuffer[] instanceData) {
if (mesh.getVertexCount() == 0) { if (mesh.getVertexCount() == 0) {
return; return;
} }
@ -2587,11 +2699,12 @@ public class JoglRenderer implements Renderer {
// if (gl.isExtensionAvailable("GL_ARB_vertex_array_object")){ // if (gl.isExtensionAvailable("GL_ARB_vertex_array_object")){
// renderMeshVertexArray(mesh, lod, count); // renderMeshVertexArray(mesh, lod, count);
// }else{ // }else{
renderMeshDefault(mesh, lod, count); renderMeshDefault(mesh, lod, count, instanceData);
// } // }
} }
public void setMainFrameBufferSrgb(boolean srgb) { @Override
public void setMainFrameBufferSrgb(boolean srgb) {
//Gamma correction //Gamma correction
if(srgb && caps.contains(Caps.Srgb)){ if(srgb && caps.contains(Caps.Srgb)){
GLContext.getCurrentGL().glEnable(GL3.GL_FRAMEBUFFER_SRGB); GLContext.getCurrentGL().glEnable(GL3.GL_FRAMEBUFFER_SRGB);
@ -2602,7 +2715,8 @@ public class JoglRenderer implements Renderer {
} }
public void setLinearizeSrgbImages(boolean linearize) { @Override
public void setLinearizeSrgbImages(boolean linearize) {
linearizeSrgbImages = linearize; linearizeSrgbImages = linearize;
} }
} }

Loading…
Cancel
Save