• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2009-2010 jMonkeyEngine
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  * * Redistributions of source code must retain the above copyright
10  *   notice, this list of conditions and the following disclaimer.
11  *
12  * * Redistributions in binary form must reproduce the above copyright
13  *   notice, this list of conditions and the following disclaimer in the
14  *   documentation and/or other materials provided with the distribution.
15  *
16  * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
17  *   may be used to endorse or promote products derived from this software
18  *   without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32 package com.jme3.renderer.lwjgl;
33 
34 import com.jme3.light.LightList;
35 import com.jme3.material.RenderState;
36 import com.jme3.material.RenderState.StencilOperation;
37 import com.jme3.material.RenderState.TestFunction;
38 import com.jme3.math.*;
39 import com.jme3.renderer.*;
40 import com.jme3.scene.Mesh;
41 import com.jme3.scene.Mesh.Mode;
42 import com.jme3.scene.VertexBuffer;
43 import com.jme3.scene.VertexBuffer.Format;
44 import com.jme3.scene.VertexBuffer.Type;
45 import com.jme3.scene.VertexBuffer.Usage;
46 import com.jme3.shader.Attribute;
47 import com.jme3.shader.Shader;
48 import com.jme3.shader.Shader.ShaderSource;
49 import com.jme3.shader.Shader.ShaderType;
50 import com.jme3.shader.Uniform;
51 import com.jme3.texture.FrameBuffer;
52 import com.jme3.texture.FrameBuffer.RenderBuffer;
53 import com.jme3.texture.Image;
54 import com.jme3.texture.Texture;
55 import com.jme3.texture.Texture.WrapAxis;
56 import com.jme3.util.BufferUtils;
57 import com.jme3.util.IntMap;
58 import com.jme3.util.IntMap.Entry;
59 import com.jme3.util.ListMap;
60 import com.jme3.util.NativeObjectManager;
61 import com.jme3.util.SafeArrayList;
62 import java.nio.*;
63 import java.util.EnumSet;
64 import java.util.List;
65 import java.util.logging.Level;
66 import java.util.logging.Logger;
67 import jme3tools.converters.MipMapGenerator;
68 import static org.lwjgl.opengl.ARBTextureMultisample.*;
69 import static org.lwjgl.opengl.EXTFramebufferBlit.*;
70 import static org.lwjgl.opengl.EXTFramebufferMultisample.*;
71 import static org.lwjgl.opengl.EXTFramebufferObject.*;
72 import static org.lwjgl.opengl.GL11.*;
73 import static org.lwjgl.opengl.GL12.*;
74 import static org.lwjgl.opengl.GL13.*;
75 import static org.lwjgl.opengl.GL14.*;
76 import static org.lwjgl.opengl.GL15.*;
77 import static org.lwjgl.opengl.GL20.*;
78 import org.lwjgl.opengl.*;
79 //import static org.lwjgl.opengl.ARBDrawInstanced.*;
80 
81 public class LwjglRenderer implements Renderer {
82 
83     private static final Logger logger = Logger.getLogger(LwjglRenderer.class.getName());
84     private static final boolean VALIDATE_SHADER = false;
85     private final ByteBuffer nameBuf = BufferUtils.createByteBuffer(250);
86     private final StringBuilder stringBuf = new StringBuilder(250);
87     private final IntBuffer intBuf1 = BufferUtils.createIntBuffer(1);
88     private final IntBuffer intBuf16 = BufferUtils.createIntBuffer(16);
89     private final RenderContext context = new RenderContext();
90     private final NativeObjectManager objManager = new NativeObjectManager();
91     private final EnumSet<Caps> caps = EnumSet.noneOf(Caps.class);
92     // current state
93     private Shader boundShader;
94     private int initialDrawBuf, initialReadBuf;
95     private int glslVer;
96     private int vertexTextureUnits;
97     private int fragTextureUnits;
98     private int vertexUniforms;
99     private int fragUniforms;
100     private int vertexAttribs;
101     private int maxFBOSamples;
102     private int maxFBOAttachs;
103     private int maxMRTFBOAttachs;
104     private int maxRBSize;
105     private int maxTexSize;
106     private int maxCubeTexSize;
107     private int maxVertCount;
108     private int maxTriCount;
109     private int maxColorTexSamples;
110     private int maxDepthTexSamples;
111     private boolean tdc;
112     private FrameBuffer lastFb = null;
113     private FrameBuffer mainFbOverride = null;
114     private final Statistics statistics = new Statistics();
115     private int vpX, vpY, vpW, vpH;
116     private int clipX, clipY, clipW, clipH;
117 
LwjglRenderer()118     public LwjglRenderer() {
119     }
120 
updateNameBuffer()121     protected void updateNameBuffer() {
122         int len = stringBuf.length();
123 
124         nameBuf.position(0);
125         nameBuf.limit(len);
126         for (int i = 0; i < len; i++) {
127             nameBuf.put((byte) stringBuf.charAt(i));
128         }
129 
130         nameBuf.rewind();
131     }
132 
getStatistics()133     public Statistics getStatistics() {
134         return statistics;
135     }
136 
getCaps()137     public EnumSet<Caps> getCaps() {
138         return caps;
139     }
140 
141     @SuppressWarnings("fallthrough")
initialize()142     public void initialize() {
143         ContextCapabilities ctxCaps = GLContext.getCapabilities();
144         if (ctxCaps.OpenGL20) {
145             caps.add(Caps.OpenGL20);
146             if (ctxCaps.OpenGL21) {
147                 caps.add(Caps.OpenGL21);
148                 if (ctxCaps.OpenGL30) {
149                     caps.add(Caps.OpenGL30);
150                     if (ctxCaps.OpenGL31) {
151                         caps.add(Caps.OpenGL31);
152                         if (ctxCaps.OpenGL32) {
153                             caps.add(Caps.OpenGL32);
154                         }
155                     }
156                 }
157             }
158         }
159 
160         String versionStr = null;
161         if (ctxCaps.OpenGL20) {
162             versionStr = glGetString(GL_SHADING_LANGUAGE_VERSION);
163         }
164         if (versionStr == null || versionStr.equals("")) {
165             glslVer = -1;
166             throw new UnsupportedOperationException("GLSL and OpenGL2 is "
167                     + "required for the LWJGL "
168                     + "renderer!");
169         }
170 
171         // Fix issue in TestRenderToMemory when GL_FRONT is the main
172         // buffer being used.
173         initialDrawBuf = glGetInteger(GL_DRAW_BUFFER);
174         initialReadBuf = glGetInteger(GL_READ_BUFFER);
175 
176         // XXX: This has to be GL_BACK for canvas on Mac
177         // Since initialDrawBuf is GL_FRONT for pbuffer, gotta
178         // change this value later on ...
179 //        initialDrawBuf = GL_BACK;
180 //        initialReadBuf = GL_BACK;
181 
182         int spaceIdx = versionStr.indexOf(" ");
183         if (spaceIdx >= 1) {
184             versionStr = versionStr.substring(0, spaceIdx);
185         }
186 
187         float version = Float.parseFloat(versionStr);
188         glslVer = (int) (version * 100);
189 
190         switch (glslVer) {
191             default:
192                 if (glslVer < 400) {
193                     break;
194                 }
195 
196             // so that future OpenGL revisions wont break jme3
197 
198             // fall through intentional
199             case 400:
200             case 330:
201             case 150:
202                 caps.add(Caps.GLSL150);
203             case 140:
204                 caps.add(Caps.GLSL140);
205             case 130:
206                 caps.add(Caps.GLSL130);
207             case 120:
208                 caps.add(Caps.GLSL120);
209             case 110:
210                 caps.add(Caps.GLSL110);
211             case 100:
212                 caps.add(Caps.GLSL100);
213                 break;
214         }
215 
216         if (!caps.contains(Caps.GLSL100)) {
217             logger.log(Level.WARNING, "Force-adding GLSL100 support, since OpenGL2 is supported.");
218             caps.add(Caps.GLSL100);
219         }
220 
221         glGetInteger(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, intBuf16);
222         vertexTextureUnits = intBuf16.get(0);
223         logger.log(Level.FINER, "VTF Units: {0}", vertexTextureUnits);
224         if (vertexTextureUnits > 0) {
225             caps.add(Caps.VertexTextureFetch);
226         }
227 
228         glGetInteger(GL_MAX_TEXTURE_IMAGE_UNITS, intBuf16);
229         fragTextureUnits = intBuf16.get(0);
230         logger.log(Level.FINER, "Texture Units: {0}", fragTextureUnits);
231 
232         glGetInteger(GL_MAX_VERTEX_UNIFORM_COMPONENTS, intBuf16);
233         vertexUniforms = intBuf16.get(0);
234         logger.log(Level.FINER, "Vertex Uniforms: {0}", vertexUniforms);
235 
236         glGetInteger(GL_MAX_FRAGMENT_UNIFORM_COMPONENTS, intBuf16);
237         fragUniforms = intBuf16.get(0);
238         logger.log(Level.FINER, "Fragment Uniforms: {0}", fragUniforms);
239 
240         glGetInteger(GL_MAX_VERTEX_ATTRIBS, intBuf16);
241         vertexAttribs = intBuf16.get(0);
242         logger.log(Level.FINER, "Vertex Attributes: {0}", vertexAttribs);
243 
244         glGetInteger(GL_SUBPIXEL_BITS, intBuf16);
245         int subpixelBits = intBuf16.get(0);
246         logger.log(Level.FINER, "Subpixel Bits: {0}", subpixelBits);
247 
248         glGetInteger(GL_MAX_ELEMENTS_VERTICES, intBuf16);
249         maxVertCount = intBuf16.get(0);
250         logger.log(Level.FINER, "Preferred Batch Vertex Count: {0}", maxVertCount);
251 
252         glGetInteger(GL_MAX_ELEMENTS_INDICES, intBuf16);
253         maxTriCount = intBuf16.get(0);
254         logger.log(Level.FINER, "Preferred Batch Index Count: {0}", maxTriCount);
255 
256         glGetInteger(GL_MAX_TEXTURE_SIZE, intBuf16);
257         maxTexSize = intBuf16.get(0);
258         logger.log(Level.FINER, "Maximum Texture Resolution: {0}", maxTexSize);
259 
260         glGetInteger(GL_MAX_CUBE_MAP_TEXTURE_SIZE, intBuf16);
261         maxCubeTexSize = intBuf16.get(0);
262         logger.log(Level.FINER, "Maximum CubeMap Resolution: {0}", maxCubeTexSize);
263 
264         if (ctxCaps.GL_ARB_color_buffer_float) {
265             // XXX: Require both 16 and 32 bit float support for FloatColorBuffer.
266             if (ctxCaps.GL_ARB_half_float_pixel) {
267                 caps.add(Caps.FloatColorBuffer);
268             }
269         }
270 
271         if (ctxCaps.GL_ARB_depth_buffer_float) {
272             caps.add(Caps.FloatDepthBuffer);
273         }
274 
275         if (ctxCaps.GL_ARB_draw_instanced) {
276             caps.add(Caps.MeshInstancing);
277         }
278 
279         if (ctxCaps.GL_ARB_fragment_program) {
280             caps.add(Caps.ARBprogram);
281         }
282 
283         if (ctxCaps.GL_ARB_texture_buffer_object) {
284             caps.add(Caps.TextureBuffer);
285         }
286 
287         if (ctxCaps.GL_ARB_texture_float) {
288             if (ctxCaps.GL_ARB_half_float_pixel) {
289                 caps.add(Caps.FloatTexture);
290             }
291         }
292 
293         if (ctxCaps.GL_ARB_vertex_array_object) {
294             caps.add(Caps.VertexBufferArray);
295         }
296 
297         if (ctxCaps.GL_ARB_texture_non_power_of_two) {
298             caps.add(Caps.NonPowerOfTwoTextures);
299         } else {
300             logger.log(Level.WARNING, "Your graphics card does not "
301                     + "support non-power-of-2 textures. "
302                     + "Some features might not work.");
303         }
304 
305         boolean latc = ctxCaps.GL_EXT_texture_compression_latc;
306         boolean atdc = ctxCaps.GL_ATI_texture_compression_3dc;
307         if (latc || atdc) {
308             caps.add(Caps.TextureCompressionLATC);
309             if (atdc && !latc) {
310                 tdc = true;
311             }
312         }
313 
314         if (ctxCaps.GL_EXT_packed_float) {
315             caps.add(Caps.PackedFloatColorBuffer);
316             if (ctxCaps.GL_ARB_half_float_pixel) {
317                 // because textures are usually uploaded as RGB16F
318                 // need half-float pixel
319                 caps.add(Caps.PackedFloatTexture);
320             }
321         }
322 
323         if (ctxCaps.GL_EXT_texture_array) {
324             caps.add(Caps.TextureArray);
325         }
326 
327         if (ctxCaps.GL_EXT_texture_shared_exponent) {
328             caps.add(Caps.SharedExponentTexture);
329         }
330 
331         if (ctxCaps.GL_EXT_framebuffer_object) {
332             caps.add(Caps.FrameBuffer);
333 
334             glGetInteger(GL_MAX_RENDERBUFFER_SIZE_EXT, intBuf16);
335             maxRBSize = intBuf16.get(0);
336             logger.log(Level.FINER, "FBO RB Max Size: {0}", maxRBSize);
337 
338             glGetInteger(GL_MAX_COLOR_ATTACHMENTS_EXT, intBuf16);
339             maxFBOAttachs = intBuf16.get(0);
340             logger.log(Level.FINER, "FBO Max renderbuffers: {0}", maxFBOAttachs);
341 
342             if (ctxCaps.GL_EXT_framebuffer_multisample) {
343                 caps.add(Caps.FrameBufferMultisample);
344 
345                 glGetInteger(GL_MAX_SAMPLES_EXT, intBuf16);
346                 maxFBOSamples = intBuf16.get(0);
347                 logger.log(Level.FINER, "FBO Max Samples: {0}", maxFBOSamples);
348             }
349 
350             if (ctxCaps.GL_ARB_texture_multisample) {
351                 caps.add(Caps.TextureMultisample);
352 
353                 glGetInteger(GL_MAX_COLOR_TEXTURE_SAMPLES, intBuf16);
354                 maxColorTexSamples = intBuf16.get(0);
355                 logger.log(Level.FINER, "Texture Multisample Color Samples: {0}", maxColorTexSamples);
356 
357                 glGetInteger(GL_MAX_DEPTH_TEXTURE_SAMPLES, intBuf16);
358                 maxDepthTexSamples = intBuf16.get(0);
359                 logger.log(Level.FINER, "Texture Multisample Depth Samples: {0}", maxDepthTexSamples);
360             }
361 
362             if (ctxCaps.GL_ARB_draw_buffers) {
363                 caps.add(Caps.FrameBufferMRT);
364                 glGetInteger(ARBDrawBuffers.GL_MAX_DRAW_BUFFERS_ARB, intBuf16);
365                 maxMRTFBOAttachs = intBuf16.get(0);
366                 logger.log(Level.FINER, "FBO Max MRT renderbuffers: {0}", maxMRTFBOAttachs);
367             }
368         }
369 
370         if (ctxCaps.GL_ARB_multisample) {
371             glGetInteger(ARBMultisample.GL_SAMPLE_BUFFERS_ARB, intBuf16);
372             boolean available = intBuf16.get(0) != 0;
373             glGetInteger(ARBMultisample.GL_SAMPLES_ARB, intBuf16);
374             int samples = intBuf16.get(0);
375             logger.log(Level.FINER, "Samples: {0}", samples);
376             boolean enabled = glIsEnabled(ARBMultisample.GL_MULTISAMPLE_ARB);
377             if (samples > 0 && available && !enabled) {
378                 glEnable(ARBMultisample.GL_MULTISAMPLE_ARB);
379             }
380         }
381 
382         logger.log(Level.INFO, "Caps: {0}", caps);
383     }
384 
invalidateState()385     public void invalidateState() {
386         context.reset();
387         boundShader = null;
388         lastFb = null;
389 
390         initialDrawBuf = glGetInteger(GL_DRAW_BUFFER);
391         initialReadBuf = glGetInteger(GL_READ_BUFFER);
392     }
393 
resetGLObjects()394     public void resetGLObjects() {
395         logger.log(Level.INFO, "Reseting objects and invalidating state");
396         objManager.resetObjects();
397         statistics.clearMemory();
398         invalidateState();
399     }
400 
cleanup()401     public void cleanup() {
402         logger.log(Level.INFO, "Deleting objects and invalidating state");
403         objManager.deleteAllObjects(this);
404         statistics.clearMemory();
405         invalidateState();
406     }
407 
checkCap(Caps cap)408     private void checkCap(Caps cap) {
409         if (!caps.contains(cap)) {
410             throw new UnsupportedOperationException("Required capability missing: " + cap.name());
411         }
412     }
413 
414     /*********************************************************************\
415     |* Render State                                                      *|
416     \*********************************************************************/
setDepthRange(float start, float end)417     public void setDepthRange(float start, float end) {
418         glDepthRange(start, end);
419     }
420 
clearBuffers(boolean color, boolean depth, boolean stencil)421     public void clearBuffers(boolean color, boolean depth, boolean stencil) {
422         int bits = 0;
423         if (color) {
424             //See explanations of the depth below, we must enable color write to be able to clear the color buffer
425             if (context.colorWriteEnabled == false) {
426                 glColorMask(true, true, true, true);
427                 context.colorWriteEnabled = true;
428             }
429             bits = GL_COLOR_BUFFER_BIT;
430         }
431         if (depth) {
432 
433             //glClear(GL_DEPTH_BUFFER_BIT) seems to not work when glDepthMask is false
434             //here s some link on openl board
435             //http://www.opengl.org/discussion_boards/ubbthreads.php?ubb=showflat&Number=257223
436             //if depth clear is requested, we enable the depthMask
437             if (context.depthWriteEnabled == false) {
438                 glDepthMask(true);
439                 context.depthWriteEnabled = true;
440             }
441             bits |= GL_DEPTH_BUFFER_BIT;
442         }
443         if (stencil) {
444             bits |= GL_STENCIL_BUFFER_BIT;
445         }
446         if (bits != 0) {
447             glClear(bits);
448         }
449     }
450 
setBackgroundColor(ColorRGBA color)451     public void setBackgroundColor(ColorRGBA color) {
452         glClearColor(color.r, color.g, color.b, color.a);
453     }
454 
setAlphaToCoverage(boolean value)455     public void setAlphaToCoverage(boolean value) {
456         if (value) {
457             glEnable(ARBMultisample.GL_SAMPLE_ALPHA_TO_COVERAGE_ARB);
458         } else {
459             glDisable(ARBMultisample.GL_SAMPLE_ALPHA_TO_COVERAGE_ARB);
460         }
461     }
462 
applyRenderState(RenderState state)463     public void applyRenderState(RenderState state) {
464         if (state.isWireframe() && !context.wireframe) {
465             glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
466             context.wireframe = true;
467         } else if (!state.isWireframe() && context.wireframe) {
468             glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
469             context.wireframe = false;
470         }
471 
472         if (state.isDepthTest() && !context.depthTestEnabled) {
473             glEnable(GL_DEPTH_TEST);
474             glDepthFunc(GL_LEQUAL);
475             context.depthTestEnabled = true;
476         } else if (!state.isDepthTest() && context.depthTestEnabled) {
477             glDisable(GL_DEPTH_TEST);
478             context.depthTestEnabled = false;
479         }
480 
481         if (state.isAlphaTest() && !context.alphaTestEnabled) {
482             glEnable(GL_ALPHA_TEST);
483             glAlphaFunc(GL_GREATER, state.getAlphaFallOff());
484             context.alphaTestEnabled = true;
485         } else if (!state.isAlphaTest() && context.alphaTestEnabled) {
486             glDisable(GL_ALPHA_TEST);
487             context.alphaTestEnabled = false;
488         }
489 
490         if (state.isDepthWrite() && !context.depthWriteEnabled) {
491             glDepthMask(true);
492             context.depthWriteEnabled = true;
493         } else if (!state.isDepthWrite() && context.depthWriteEnabled) {
494             glDepthMask(false);
495             context.depthWriteEnabled = false;
496         }
497 
498         if (state.isColorWrite() && !context.colorWriteEnabled) {
499             glColorMask(true, true, true, true);
500             context.colorWriteEnabled = true;
501         } else if (!state.isColorWrite() && context.colorWriteEnabled) {
502             glColorMask(false, false, false, false);
503             context.colorWriteEnabled = false;
504         }
505 
506         if (state.isPointSprite() && !context.pointSprite) {
507             // Only enable/disable sprite
508             if (context.boundTextures[0] != null){
509                 if (context.boundTextureUnit != 0){
510                     glActiveTexture(GL_TEXTURE0);
511                     context.boundTextureUnit = 0;
512                 }
513                 glEnable(GL_POINT_SPRITE);
514                 glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);
515             }
516             context.pointSprite = true;
517         } else if (!state.isPointSprite() && context.pointSprite) {
518             if (context.boundTextures[0] != null){
519                 if (context.boundTextureUnit != 0){
520                     glActiveTexture(GL_TEXTURE0);
521                     context.boundTextureUnit = 0;
522                 }
523                 glDisable(GL_POINT_SPRITE);
524                 glDisable(GL_VERTEX_PROGRAM_POINT_SIZE);
525                 context.pointSprite = false;
526             }
527         }
528 
529         if (state.isPolyOffset()) {
530             if (!context.polyOffsetEnabled) {
531                 glEnable(GL_POLYGON_OFFSET_FILL);
532                 glPolygonOffset(state.getPolyOffsetFactor(),
533                         state.getPolyOffsetUnits());
534                 context.polyOffsetEnabled = true;
535                 context.polyOffsetFactor = state.getPolyOffsetFactor();
536                 context.polyOffsetUnits = state.getPolyOffsetUnits();
537             } else {
538                 if (state.getPolyOffsetFactor() != context.polyOffsetFactor
539                         || state.getPolyOffsetUnits() != context.polyOffsetUnits) {
540                     glPolygonOffset(state.getPolyOffsetFactor(),
541                             state.getPolyOffsetUnits());
542                     context.polyOffsetFactor = state.getPolyOffsetFactor();
543                     context.polyOffsetUnits = state.getPolyOffsetUnits();
544                 }
545             }
546         } else {
547             if (context.polyOffsetEnabled) {
548                 glDisable(GL_POLYGON_OFFSET_FILL);
549                 context.polyOffsetEnabled = false;
550                 context.polyOffsetFactor = 0;
551                 context.polyOffsetUnits = 0;
552             }
553         }
554 
555         if (state.getFaceCullMode() != context.cullMode) {
556             if (state.getFaceCullMode() == RenderState.FaceCullMode.Off) {
557                 glDisable(GL_CULL_FACE);
558             } else {
559                 glEnable(GL_CULL_FACE);
560             }
561 
562             switch (state.getFaceCullMode()) {
563                 case Off:
564                     break;
565                 case Back:
566                     glCullFace(GL_BACK);
567                     break;
568                 case Front:
569                     glCullFace(GL_FRONT);
570                     break;
571                 case FrontAndBack:
572                     glCullFace(GL_FRONT_AND_BACK);
573                     break;
574                 default:
575                     throw new UnsupportedOperationException("Unrecognized face cull mode: "
576                             + state.getFaceCullMode());
577             }
578 
579             context.cullMode = state.getFaceCullMode();
580         }
581 
582         if (state.getBlendMode() != context.blendMode) {
583             if (state.getBlendMode() == RenderState.BlendMode.Off) {
584                 glDisable(GL_BLEND);
585             } else {
586                 glEnable(GL_BLEND);
587                 switch (state.getBlendMode()) {
588                     case Off:
589                         break;
590                     case Additive:
591                         glBlendFunc(GL_ONE, GL_ONE);
592                         break;
593                     case AlphaAdditive:
594                         glBlendFunc(GL_SRC_ALPHA, GL_ONE);
595                         break;
596                     case Color:
597                         glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_COLOR);
598                         break;
599                     case Alpha:
600                         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
601                         break;
602                     case PremultAlpha:
603                         glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
604                         break;
605                     case Modulate:
606                         glBlendFunc(GL_DST_COLOR, GL_ZERO);
607                         break;
608                     case ModulateX2:
609                         glBlendFunc(GL_DST_COLOR, GL_SRC_COLOR);
610                         break;
611                     default:
612                         throw new UnsupportedOperationException("Unrecognized blend mode: "
613                                 + state.getBlendMode());
614                 }
615             }
616 
617             context.blendMode = state.getBlendMode();
618         }
619 
620         if (context.stencilTest != state.isStencilTest()
621                 || context.frontStencilStencilFailOperation != state.getFrontStencilStencilFailOperation()
622                 || context.frontStencilDepthFailOperation != state.getFrontStencilDepthFailOperation()
623                 || context.frontStencilDepthPassOperation != state.getFrontStencilDepthPassOperation()
624                 || context.backStencilStencilFailOperation != state.getBackStencilStencilFailOperation()
625                 || context.backStencilDepthFailOperation != state.getBackStencilDepthFailOperation()
626                 || context.backStencilDepthPassOperation != state.getBackStencilDepthPassOperation()
627                 || context.frontStencilFunction != state.getFrontStencilFunction()
628                 || context.backStencilFunction != state.getBackStencilFunction()) {
629 
630             context.frontStencilStencilFailOperation = state.getFrontStencilStencilFailOperation();   //terrible looking, I know
631             context.frontStencilDepthFailOperation = state.getFrontStencilDepthFailOperation();
632             context.frontStencilDepthPassOperation = state.getFrontStencilDepthPassOperation();
633             context.backStencilStencilFailOperation = state.getBackStencilStencilFailOperation();
634             context.backStencilDepthFailOperation = state.getBackStencilDepthFailOperation();
635             context.backStencilDepthPassOperation = state.getBackStencilDepthPassOperation();
636             context.frontStencilFunction = state.getFrontStencilFunction();
637             context.backStencilFunction = state.getBackStencilFunction();
638 
639             if (state.isStencilTest()) {
640                 glEnable(GL_STENCIL_TEST);
641                 glStencilOpSeparate(GL_FRONT,
642                         convertStencilOperation(state.getFrontStencilStencilFailOperation()),
643                         convertStencilOperation(state.getFrontStencilDepthFailOperation()),
644                         convertStencilOperation(state.getFrontStencilDepthPassOperation()));
645                 glStencilOpSeparate(GL_BACK,
646                         convertStencilOperation(state.getBackStencilStencilFailOperation()),
647                         convertStencilOperation(state.getBackStencilDepthFailOperation()),
648                         convertStencilOperation(state.getBackStencilDepthPassOperation()));
649                 glStencilFuncSeparate(GL_FRONT,
650                         convertTestFunction(state.getFrontStencilFunction()),
651                         0, Integer.MAX_VALUE);
652                 glStencilFuncSeparate(GL_BACK,
653                         convertTestFunction(state.getBackStencilFunction()),
654                         0, Integer.MAX_VALUE);
655             } else {
656                 glDisable(GL_STENCIL_TEST);
657             }
658         }
659     }
660 
convertStencilOperation(StencilOperation stencilOp)661     private int convertStencilOperation(StencilOperation stencilOp) {
662         switch (stencilOp) {
663             case Keep:
664                 return GL_KEEP;
665             case Zero:
666                 return GL_ZERO;
667             case Replace:
668                 return GL_REPLACE;
669             case Increment:
670                 return GL_INCR;
671             case IncrementWrap:
672                 return GL_INCR_WRAP;
673             case Decrement:
674                 return GL_DECR;
675             case DecrementWrap:
676                 return GL_DECR_WRAP;
677             case Invert:
678                 return GL_INVERT;
679             default:
680                 throw new UnsupportedOperationException("Unrecognized stencil operation: " + stencilOp);
681         }
682     }
683 
convertTestFunction(TestFunction testFunc)684     private int convertTestFunction(TestFunction testFunc) {
685         switch (testFunc) {
686             case Never:
687                 return GL_NEVER;
688             case Less:
689                 return GL_LESS;
690             case LessOrEqual:
691                 return GL_LEQUAL;
692             case Greater:
693                 return GL_GREATER;
694             case GreaterOrEqual:
695                 return GL_GEQUAL;
696             case Equal:
697                 return GL_EQUAL;
698             case NotEqual:
699                 return GL_NOTEQUAL;
700             case Always:
701                 return GL_ALWAYS;
702             default:
703                 throw new UnsupportedOperationException("Unrecognized test function: " + testFunc);
704         }
705     }
706 
707     /*********************************************************************\
708     |* Camera and World transforms                                       *|
709     \*********************************************************************/
setViewPort(int x, int y, int w, int h)710     public void setViewPort(int x, int y, int w, int h) {
711         if (x != vpX || vpY != y || vpW != w || vpH != h) {
712             glViewport(x, y, w, h);
713             vpX = x;
714             vpY = y;
715             vpW = w;
716             vpH = h;
717         }
718     }
719 
setClipRect(int x, int y, int width, int height)720     public void setClipRect(int x, int y, int width, int height) {
721         if (!context.clipRectEnabled) {
722             glEnable(GL_SCISSOR_TEST);
723             context.clipRectEnabled = true;
724         }
725         if (clipX != x || clipY != y || clipW != width || clipH != height) {
726             glScissor(x, y, width, height);
727             clipX = x;
728             clipY = y;
729             clipW = width;
730             clipH = height;
731         }
732     }
733 
clearClipRect()734     public void clearClipRect() {
735         if (context.clipRectEnabled) {
736             glDisable(GL_SCISSOR_TEST);
737             context.clipRectEnabled = false;
738 
739             clipX = 0;
740             clipY = 0;
741             clipW = 0;
742             clipH = 0;
743         }
744     }
745 
onFrame()746     public void onFrame() {
747         objManager.deleteUnused(this);
748 //        statistics.clearFrame();
749     }
750 
setWorldMatrix(Matrix4f worldMatrix)751     public void setWorldMatrix(Matrix4f worldMatrix) {
752     }
753 
setViewProjectionMatrices(Matrix4f viewMatrix, Matrix4f projMatrix)754     public void setViewProjectionMatrices(Matrix4f viewMatrix, Matrix4f projMatrix) {
755     }
756 
757     /*********************************************************************\
758     |* Shaders                                                           *|
759     \*********************************************************************/
updateUniformLocation(Shader shader, Uniform uniform)760     protected void updateUniformLocation(Shader shader, Uniform uniform) {
761         stringBuf.setLength(0);
762         stringBuf.append(uniform.getName()).append('\0');
763         updateNameBuffer();
764         int loc = glGetUniformLocation(shader.getId(), nameBuf);
765         if (loc < 0) {
766             uniform.setLocation(-1);
767             // uniform is not declared in shader
768             logger.log(Level.INFO, "Uniform {0} is not declared in shader {1}.", new Object[]{uniform.getName(), shader.getSources()});
769         } else {
770             uniform.setLocation(loc);
771         }
772     }
773 
bindProgram(Shader shader)774     protected void bindProgram(Shader shader){
775         int shaderId = shader.getId();
776         if (context.boundShaderProgram != shaderId) {
777             glUseProgram(shaderId);
778             statistics.onShaderUse(shader, true);
779             boundShader = shader;
780             context.boundShaderProgram = shaderId;
781         } else {
782             statistics.onShaderUse(shader, false);
783         }
784     }
785 
updateUniform(Shader shader, Uniform uniform)786     protected void updateUniform(Shader shader, Uniform uniform) {
787         int shaderId = shader.getId();
788 
789         assert uniform.getName() != null;
790         assert shader.getId() > 0;
791 
792         bindProgram(shader);
793 
794         int loc = uniform.getLocation();
795         if (loc == -1) {
796             return;
797         }
798 
799         if (loc == -2) {
800             // get uniform location
801             updateUniformLocation(shader, uniform);
802             if (uniform.getLocation() == -1) {
803                 // not declared, ignore
804                 uniform.clearUpdateNeeded();
805                 return;
806             }
807             loc = uniform.getLocation();
808         }
809 
810         if (uniform.getVarType() == null) {
811             return; // value not set yet..
812         }
813         statistics.onUniformSet();
814 
815         uniform.clearUpdateNeeded();
816         FloatBuffer fb;
817         switch (uniform.getVarType()) {
818             case Float:
819                 Float f = (Float) uniform.getValue();
820                 glUniform1f(loc, f.floatValue());
821                 break;
822             case Vector2:
823                 Vector2f v2 = (Vector2f) uniform.getValue();
824                 glUniform2f(loc, v2.getX(), v2.getY());
825                 break;
826             case Vector3:
827                 Vector3f v3 = (Vector3f) uniform.getValue();
828                 glUniform3f(loc, v3.getX(), v3.getY(), v3.getZ());
829                 break;
830             case Vector4:
831                 Object val = uniform.getValue();
832                 if (val instanceof ColorRGBA) {
833                     ColorRGBA c = (ColorRGBA) val;
834                     glUniform4f(loc, c.r, c.g, c.b, c.a);
835                 } else if (val instanceof Vector4f) {
836                     Vector4f c = (Vector4f) val;
837                     glUniform4f(loc, c.x, c.y, c.z, c.w);
838                 } else {
839                     Quaternion c = (Quaternion) uniform.getValue();
840                     glUniform4f(loc, c.getX(), c.getY(), c.getZ(), c.getW());
841                 }
842                 break;
843             case Boolean:
844                 Boolean b = (Boolean) uniform.getValue();
845                 glUniform1i(loc, b.booleanValue() ? GL_TRUE : GL_FALSE);
846                 break;
847             case Matrix3:
848                 fb = (FloatBuffer) uniform.getValue();
849                 assert fb.remaining() == 9;
850                 glUniformMatrix3(loc, false, fb);
851                 break;
852             case Matrix4:
853                 fb = (FloatBuffer) uniform.getValue();
854                 assert fb.remaining() == 16;
855                 glUniformMatrix4(loc, false, fb);
856                 break;
857             case FloatArray:
858                 fb = (FloatBuffer) uniform.getValue();
859                 glUniform1(loc, fb);
860                 break;
861             case Vector2Array:
862                 fb = (FloatBuffer) uniform.getValue();
863                 glUniform2(loc, fb);
864                 break;
865             case Vector3Array:
866                 fb = (FloatBuffer) uniform.getValue();
867                 glUniform3(loc, fb);
868                 break;
869             case Vector4Array:
870                 fb = (FloatBuffer) uniform.getValue();
871                 glUniform4(loc, fb);
872                 break;
873             case Matrix4Array:
874                 fb = (FloatBuffer) uniform.getValue();
875                 glUniformMatrix4(loc, false, fb);
876                 break;
877             case Int:
878                 Integer i = (Integer) uniform.getValue();
879                 glUniform1i(loc, i.intValue());
880                 break;
881             default:
882                 throw new UnsupportedOperationException("Unsupported uniform type: " + uniform.getVarType());
883         }
884     }
885 
updateShaderUniforms(Shader shader)886     protected void updateShaderUniforms(Shader shader) {
887         ListMap<String, Uniform> uniforms = shader.getUniformMap();
888 //        for (Uniform uniform : shader.getUniforms()){
889         for (int i = 0; i < uniforms.size(); i++) {
890             Uniform uniform = uniforms.getValue(i);
891             if (uniform.isUpdateNeeded()) {
892                 updateUniform(shader, uniform);
893             }
894         }
895     }
896 
resetUniformLocations(Shader shader)897     protected void resetUniformLocations(Shader shader) {
898         ListMap<String, Uniform> uniforms = shader.getUniformMap();
899 //        for (Uniform uniform : shader.getUniforms()){
900         for (int i = 0; i < uniforms.size(); i++) {
901             Uniform uniform = uniforms.getValue(i);
902             uniform.reset(); // e.g check location again
903         }
904     }
905 
906     /*
907      * (Non-javadoc)
908      * Only used for fixed-function. Ignored.
909      */
setLighting(LightList list)910     public void setLighting(LightList list) {
911     }
912 
convertShaderType(ShaderType type)913     public int convertShaderType(ShaderType type) {
914         switch (type) {
915             case Fragment:
916                 return GL_FRAGMENT_SHADER;
917             case Vertex:
918                 return GL_VERTEX_SHADER;
919 //            case Geometry:
920 //                return ARBGeometryShader4.GL_GEOMETRY_SHADER_ARB;
921             default:
922                 throw new UnsupportedOperationException("Unrecognized shader type.");
923         }
924     }
925 
updateShaderSourceData(ShaderSource source, String language)926     public void updateShaderSourceData(ShaderSource source, String language) {
927         int id = source.getId();
928         if (id == -1) {
929             // create id
930             id = glCreateShader(convertShaderType(source.getType()));
931             if (id <= 0) {
932                 throw new RendererException("Invalid ID received when trying to create shader.");
933             }
934 
935             source.setId(id);
936         }else{
937             throw new RendererException("Cannot recompile shader source");
938         }
939 
940         // upload shader source
941         // merge the defines and source code
942 
943         stringBuf.setLength(0);
944         if (language.startsWith("GLSL")) {
945             int version = Integer.parseInt(language.substring(4));
946             if (version > 100) {
947                 stringBuf.append("#version ");
948                 stringBuf.append(language.substring(4));
949                 if (version >= 150) {
950                     stringBuf.append(" core");
951                 }
952                 stringBuf.append("\n");
953             }
954         }
955         updateNameBuffer();
956 
957         byte[] definesCodeData = source.getDefines().getBytes();
958         byte[] sourceCodeData = source.getSource().getBytes();
959         ByteBuffer codeBuf = BufferUtils.createByteBuffer(nameBuf.limit()
960                 + definesCodeData.length
961                 + sourceCodeData.length);
962         codeBuf.put(nameBuf);
963         codeBuf.put(definesCodeData);
964         codeBuf.put(sourceCodeData);
965         codeBuf.flip();
966 
967         glShaderSource(id, codeBuf);
968         glCompileShader(id);
969 
970         glGetShader(id, GL_COMPILE_STATUS, intBuf1);
971 
972         boolean compiledOK = intBuf1.get(0) == GL_TRUE;
973         String infoLog = null;
974 
975         if (VALIDATE_SHADER || !compiledOK) {
976             // even if compile succeeded, check
977             // log for warnings
978             glGetShader(id, GL_INFO_LOG_LENGTH, intBuf1);
979             int length = intBuf1.get(0);
980             if (length > 3) {
981                 // get infos
982                 ByteBuffer logBuf = BufferUtils.createByteBuffer(length);
983                 glGetShaderInfoLog(id, null, logBuf);
984                 byte[] logBytes = new byte[length];
985                 logBuf.get(logBytes, 0, length);
986                 // convert to string, etc
987                 infoLog = new String(logBytes);
988             }
989         }
990 
991         if (compiledOK) {
992             if (infoLog != null) {
993                 logger.log(Level.INFO, "{0} compile success\n{1}",
994                         new Object[]{source.getName(), infoLog});
995             } else {
996                 logger.log(Level.FINE, "{0} compile success", source.getName());
997             }
998         } else {
999             logger.log(Level.WARNING, "Bad compile of:\n{0}{1}",
1000                     new Object[]{source.getDefines(), source.getSource()});
1001             if (infoLog != null) {
1002                 throw new RendererException("compile error in:" + source + " error:" + infoLog);
1003             } else {
1004                 throw new RendererException("compile error in:" + source + " error: <not provided>");
1005             }
1006         }
1007 
1008         source.clearUpdateNeeded();
1009         // only usable if compiled
1010         source.setUsable(compiledOK);
1011         if (!compiledOK) {
1012             // make sure to dispose id cause all program's
1013             // shaders will be cleared later.
1014             glDeleteShader(id);
1015         } else {
1016             // register for cleanup since the ID is usable
1017             // NOTE: From now on cleanup is handled
1018             // by the parent shader object so no need
1019             // to register.
1020             //objManager.registerForCleanup(source);
1021         }
1022     }
1023 
updateShaderData(Shader shader)1024     public void updateShaderData(Shader shader) {
1025         int id = shader.getId();
1026         boolean needRegister = false;
1027         if (id == -1) {
1028             // create program
1029             id = glCreateProgram();
1030             if (id == 0) {
1031                 throw new RendererException("Invalid ID (" + id + ") received when trying to create shader program.");
1032             }
1033 
1034             shader.setId(id);
1035             needRegister = true;
1036         }
1037 
1038         for (ShaderSource source : shader.getSources()) {
1039             if (source.isUpdateNeeded()) {
1040                 updateShaderSourceData(source, shader.getLanguage());
1041                 // shader has been compiled here
1042             }
1043 
1044             if (!source.isUsable()) {
1045                 // it's useless.. just forget about everything..
1046                 shader.setUsable(false);
1047                 shader.clearUpdateNeeded();
1048                 return;
1049             }
1050             glAttachShader(id, source.getId());
1051         }
1052 
1053         if (caps.contains(Caps.OpenGL30)) {
1054             // Check if GLSL version is 1.5 for shader
1055             GL30.glBindFragDataLocation(id, 0, "outFragColor");
1056         }
1057 
1058         // link shaders to program
1059         glLinkProgram(id);
1060         glGetProgram(id, GL_LINK_STATUS, intBuf1);
1061         boolean linkOK = intBuf1.get(0) == GL_TRUE;
1062         String infoLog = null;
1063 
1064         if (VALIDATE_SHADER || !linkOK) {
1065             glGetProgram(id, GL_INFO_LOG_LENGTH, intBuf1);
1066             int length = intBuf1.get(0);
1067             if (length > 3) {
1068                 // get infos
1069                 ByteBuffer logBuf = BufferUtils.createByteBuffer(length);
1070                 glGetProgramInfoLog(id, null, logBuf);
1071 
1072                 // convert to string, etc
1073                 byte[] logBytes = new byte[length];
1074                 logBuf.get(logBytes, 0, length);
1075                 infoLog = new String(logBytes);
1076             }
1077         }
1078 
1079         if (linkOK) {
1080             if (infoLog != null) {
1081                 logger.log(Level.INFO, "shader link success. \n{0}", infoLog);
1082             } else {
1083                 logger.fine("shader link success");
1084             }
1085         } else {
1086             if (infoLog != null) {
1087                 throw new RendererException("Shader link failure, shader:" + shader + " info:" + infoLog);
1088             } else {
1089                 throw new RendererException("Shader link failure, shader:" + shader + " info: <not provided>");
1090             }
1091         }
1092 
1093         shader.clearUpdateNeeded();
1094         if (!linkOK) {
1095             // failure.. forget about everything
1096             shader.resetSources();
1097             shader.setUsable(false);
1098             deleteShader(shader);
1099         } else {
1100             shader.setUsable(true);
1101             if (needRegister) {
1102                 objManager.registerForCleanup(shader);
1103                 statistics.onNewShader();
1104             } else {
1105                 // OpenGL spec: uniform locations may change after re-link
1106                 resetUniformLocations(shader);
1107             }
1108         }
1109     }
1110 
setShader(Shader shader)1111     public void setShader(Shader shader) {
1112         if (shader == null) {
1113             throw new IllegalArgumentException("shader cannot be null");
1114 //            if (context.boundShaderProgram > 0) {
1115 //                glUseProgram(0);
1116 //                statistics.onShaderUse(null, true);
1117 //                context.boundShaderProgram = 0;
1118 //                boundShader = null;
1119 //            }
1120         } else {
1121             if (shader.isUpdateNeeded()) {
1122                 updateShaderData(shader);
1123             }
1124 
1125             // NOTE: might want to check if any of the
1126             // sources need an update?
1127 
1128             if (!shader.isUsable()) {
1129                 return;
1130             }
1131 
1132             assert shader.getId() > 0;
1133 
1134             updateShaderUniforms(shader);
1135             bindProgram(shader);
1136         }
1137     }
1138 
deleteShaderSource(ShaderSource source)1139     public void deleteShaderSource(ShaderSource source) {
1140         if (source.getId() < 0) {
1141             logger.warning("Shader source is not uploaded to GPU, cannot delete.");
1142             return;
1143         }
1144         source.setUsable(false);
1145         source.clearUpdateNeeded();
1146         glDeleteShader(source.getId());
1147         source.resetObject();
1148     }
1149 
deleteShader(Shader shader)1150     public void deleteShader(Shader shader) {
1151         if (shader.getId() == -1) {
1152             logger.warning("Shader is not uploaded to GPU, cannot delete.");
1153             return;
1154         }
1155 
1156         for (ShaderSource source : shader.getSources()) {
1157             if (source.getId() != -1) {
1158                 glDetachShader(shader.getId(), source.getId());
1159                 deleteShaderSource(source);
1160             }
1161         }
1162 
1163         // kill all references so sources can be collected
1164         // if needed.
1165         shader.resetSources();
1166         glDeleteProgram(shader.getId());
1167 
1168         statistics.onDeleteShader();
1169     }
1170 
1171     /*********************************************************************\
1172     |* Framebuffers                                                      *|
1173     \*********************************************************************/
copyFrameBuffer(FrameBuffer src, FrameBuffer dst)1174     public void copyFrameBuffer(FrameBuffer src, FrameBuffer dst) {
1175         copyFrameBuffer(src, dst, true);
1176     }
1177 
copyFrameBuffer(FrameBuffer src, FrameBuffer dst, boolean copyDepth)1178     public void copyFrameBuffer(FrameBuffer src, FrameBuffer dst, boolean copyDepth) {
1179         if (GLContext.getCapabilities().GL_EXT_framebuffer_blit) {
1180             int srcW = 0;
1181             int srcH = 0;
1182             int dstW = 0;
1183             int dstH = 0;
1184             int prevFBO = context.boundFBO;
1185 
1186             if (src != null && src.isUpdateNeeded()) {
1187                 updateFrameBuffer(src);
1188             }
1189 
1190             if (dst != null && dst.isUpdateNeeded()) {
1191                 updateFrameBuffer(dst);
1192             }
1193 
1194             if (src == null) {
1195                 glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, 0);
1196 //                srcW = viewWidth;
1197 //                srcH = viewHeight;
1198             } else {
1199                 glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, src.getId());
1200                 srcW = src.getWidth();
1201                 srcH = src.getHeight();
1202             }
1203             if (dst == null) {
1204                 glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, 0);
1205 //                dstW = viewWidth;
1206 //                dstH = viewHeight;
1207             } else {
1208                 glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, dst.getId());
1209                 dstW = dst.getWidth();
1210                 dstH = dst.getHeight();
1211             }
1212             int mask = GL_COLOR_BUFFER_BIT;
1213             if (copyDepth) {
1214                 mask |= GL_DEPTH_BUFFER_BIT;
1215             }
1216             glBlitFramebufferEXT(0, 0, srcW, srcH,
1217                     0, 0, dstW, dstH, mask,
1218                     GL_NEAREST);
1219 
1220 
1221             glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, prevFBO);
1222             try {
1223                 checkFrameBufferError();
1224             } catch (IllegalStateException ex) {
1225                 logger.log(Level.SEVERE, "Source FBO:\n{0}", src);
1226                 logger.log(Level.SEVERE, "Dest FBO:\n{0}", dst);
1227                 throw ex;
1228             }
1229         } else {
1230             throw new RendererException("EXT_framebuffer_blit required.");
1231             // TODO: support non-blit copies?
1232         }
1233     }
1234 
getTargetBufferName(int buffer)1235     private String getTargetBufferName(int buffer){
1236         switch (buffer){
1237             case GL_NONE: return "NONE";
1238             case GL_FRONT: return "GL_FRONT";
1239             case GL_BACK: return "GL_BACK";
1240             default:
1241                 if ( buffer >= GL_COLOR_ATTACHMENT0_EXT
1242                   && buffer <= GL_COLOR_ATTACHMENT15_EXT){
1243                     return "GL_COLOR_ATTACHMENT" +
1244                                 (buffer - GL_COLOR_ATTACHMENT0_EXT);
1245                 }else{
1246                     return "UNKNOWN? " + buffer;
1247                 }
1248         }
1249     }
1250 
printRealRenderBufferInfo(FrameBuffer fb, RenderBuffer rb, String name)1251     private void printRealRenderBufferInfo(FrameBuffer fb, RenderBuffer rb, String name){
1252         System.out.println("== Renderbuffer " + name + " ==");
1253         System.out.println("RB ID: " + rb.getId());
1254         System.out.println("Is proper? " + glIsRenderbufferEXT(rb.getId()));
1255 
1256         int attachment = convertAttachmentSlot(rb.getSlot());
1257 
1258         int type = glGetFramebufferAttachmentParameterEXT(GL_DRAW_FRAMEBUFFER_EXT,
1259                                                           attachment,
1260                                                           GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_EXT);
1261 
1262         int rbName = glGetFramebufferAttachmentParameterEXT(GL_DRAW_FRAMEBUFFER_EXT,
1263                                                             attachment,
1264                                                             GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_EXT);
1265 
1266         switch (type){
1267             case GL_NONE:
1268                 System.out.println("Type: None");
1269                 return; // note: return from method as other queries will be invalid
1270             case GL_TEXTURE:
1271                 System.out.println("Type: Texture");
1272                 break;
1273             case GL_RENDERBUFFER_EXT:
1274                 System.out.println("Type: Buffer");
1275                 System.out.println("RB ID: " + rbName);
1276                 break;
1277         }
1278 
1279 
1280 
1281     }
1282 
printRealFrameBufferInfo(FrameBuffer fb)1283     private void printRealFrameBufferInfo(FrameBuffer fb) {
1284         boolean doubleBuffer = glGetBoolean(GL_DOUBLEBUFFER);
1285         String drawBuf = getTargetBufferName(glGetInteger(GL_DRAW_BUFFER));
1286         String readBuf = getTargetBufferName(glGetInteger(GL_READ_BUFFER));
1287 
1288         int fbId = fb.getId();
1289         int curDrawBinding = glGetInteger(ARBFramebufferObject.GL_DRAW_FRAMEBUFFER_BINDING);
1290         int curReadBinding = glGetInteger(ARBFramebufferObject.GL_READ_FRAMEBUFFER_BINDING);
1291 
1292         System.out.println("=== OpenGL FBO State ===");
1293         System.out.println("Context doublebuffered? " + doubleBuffer);
1294         System.out.println("FBO ID: " + fbId);
1295         System.out.println("Is proper? " + glIsFramebufferEXT(fbId));
1296         System.out.println("Is bound to draw? " + (fbId == curDrawBinding));
1297         System.out.println("Is bound to read? " + (fbId == curReadBinding));
1298         System.out.println("Draw buffer: " + drawBuf);
1299         System.out.println("Read buffer: " + readBuf);
1300 
1301         if (context.boundFBO != fbId){
1302             glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, fbId);
1303             context.boundFBO = fbId;
1304         }
1305 
1306         if (fb.getDepthBuffer() != null){
1307             printRealRenderBufferInfo(fb, fb.getDepthBuffer(), "Depth");
1308         }
1309         for (int i = 0; i < fb.getNumColorBuffers(); i++){
1310             printRealRenderBufferInfo(fb, fb.getColorBuffer(i), "Color" + i);
1311         }
1312     }
1313 
checkFrameBufferError()1314     private void checkFrameBufferError() {
1315         int status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
1316         switch (status) {
1317             case GL_FRAMEBUFFER_COMPLETE_EXT:
1318                 break;
1319             case GL_FRAMEBUFFER_UNSUPPORTED_EXT:
1320                 //Choose different formats
1321                 throw new IllegalStateException("Framebuffer object format is "
1322                         + "unsupported by the video hardware.");
1323             case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT:
1324                 throw new IllegalStateException("Framebuffer has erronous attachment.");
1325             case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT:
1326                 throw new IllegalStateException("Framebuffer doesn't have any renderbuffers attached.");
1327             case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT:
1328                 throw new IllegalStateException("Framebuffer attachments must have same dimensions.");
1329             case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT:
1330                 throw new IllegalStateException("Framebuffer attachments must have same formats.");
1331             case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT:
1332                 throw new IllegalStateException("Incomplete draw buffer.");
1333             case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT:
1334                 throw new IllegalStateException("Incomplete read buffer.");
1335             case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_EXT:
1336                 throw new IllegalStateException("Incomplete multisample buffer.");
1337             default:
1338                 //Programming error; will fail on all hardware
1339                 throw new IllegalStateException("Some video driver error "
1340                         + "or programming error occured. "
1341                         + "Framebuffer object status is invalid. ");
1342         }
1343     }
1344 
updateRenderBuffer(FrameBuffer fb, RenderBuffer rb)1345     private void updateRenderBuffer(FrameBuffer fb, RenderBuffer rb) {
1346         int id = rb.getId();
1347         if (id == -1) {
1348             glGenRenderbuffersEXT(intBuf1);
1349             id = intBuf1.get(0);
1350             rb.setId(id);
1351         }
1352 
1353         if (context.boundRB != id) {
1354             glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, id);
1355             context.boundRB = id;
1356         }
1357 
1358         if (fb.getWidth() > maxRBSize || fb.getHeight() > maxRBSize) {
1359             throw new RendererException("Resolution " + fb.getWidth()
1360                     + ":" + fb.getHeight() + " is not supported.");
1361         }
1362 
1363         TextureUtil.checkFormatSupported(rb.getFormat());
1364 
1365         if (fb.getSamples() > 1 && GLContext.getCapabilities().GL_EXT_framebuffer_multisample) {
1366             int samples = fb.getSamples();
1367             if (maxFBOSamples < samples) {
1368                 samples = maxFBOSamples;
1369             }
1370             glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT,
1371                     samples,
1372                     TextureUtil.convertTextureFormat(rb.getFormat()),
1373                     fb.getWidth(),
1374                     fb.getHeight());
1375         } else {
1376             glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT,
1377                     TextureUtil.convertTextureFormat(rb.getFormat()),
1378                     fb.getWidth(),
1379                     fb.getHeight());
1380         }
1381     }
1382 
convertAttachmentSlot(int attachmentSlot)1383     private int convertAttachmentSlot(int attachmentSlot) {
1384         // can also add support for stencil here
1385         if (attachmentSlot == -100) {
1386             return GL_DEPTH_ATTACHMENT_EXT;
1387         } else if (attachmentSlot < 0 || attachmentSlot >= 16) {
1388             throw new UnsupportedOperationException("Invalid FBO attachment slot: " + attachmentSlot);
1389         }
1390 
1391         return GL_COLOR_ATTACHMENT0_EXT + attachmentSlot;
1392     }
1393 
updateRenderTexture(FrameBuffer fb, RenderBuffer rb)1394     public void updateRenderTexture(FrameBuffer fb, RenderBuffer rb) {
1395         Texture tex = rb.getTexture();
1396         Image image = tex.getImage();
1397         if (image.isUpdateNeeded()) {
1398             updateTexImageData(image, tex.getType(), tex.getMinFilter().usesMipMapLevels(), 0);
1399 
1400             // NOTE: For depth textures, sets nearest/no-mips mode
1401             // Required to fix "framebuffer unsupported"
1402             // for old NVIDIA drivers!
1403             setupTextureParams(tex);
1404         }
1405 
1406         glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT,
1407                 convertAttachmentSlot(rb.getSlot()),
1408                 convertTextureType(tex.getType(), image.getMultiSamples()),
1409                 image.getId(),
1410                 0);
1411     }
1412 
updateFrameBufferAttachment(FrameBuffer fb, RenderBuffer rb)1413     public void updateFrameBufferAttachment(FrameBuffer fb, RenderBuffer rb) {
1414         boolean needAttach;
1415         if (rb.getTexture() == null) {
1416             // if it hasn't been created yet, then attach is required.
1417             needAttach = rb.getId() == -1;
1418             updateRenderBuffer(fb, rb);
1419         } else {
1420             needAttach = false;
1421             updateRenderTexture(fb, rb);
1422         }
1423         if (needAttach) {
1424             glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT,
1425                     convertAttachmentSlot(rb.getSlot()),
1426                     GL_RENDERBUFFER_EXT,
1427                     rb.getId());
1428         }
1429     }
1430 
updateFrameBuffer(FrameBuffer fb)1431     public void updateFrameBuffer(FrameBuffer fb) {
1432         int id = fb.getId();
1433         if (id == -1) {
1434             // create FBO
1435             glGenFramebuffersEXT(intBuf1);
1436             id = intBuf1.get(0);
1437             fb.setId(id);
1438             objManager.registerForCleanup(fb);
1439 
1440             statistics.onNewFrameBuffer();
1441         }
1442 
1443         if (context.boundFBO != id) {
1444             glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, id);
1445             // binding an FBO automatically sets draw buf to GL_COLOR_ATTACHMENT0
1446             context.boundDrawBuf = 0;
1447             context.boundFBO = id;
1448         }
1449 
1450         FrameBuffer.RenderBuffer depthBuf = fb.getDepthBuffer();
1451         if (depthBuf != null) {
1452             updateFrameBufferAttachment(fb, depthBuf);
1453         }
1454 
1455         for (int i = 0; i < fb.getNumColorBuffers(); i++) {
1456             FrameBuffer.RenderBuffer colorBuf = fb.getColorBuffer(i);
1457             updateFrameBufferAttachment(fb, colorBuf);
1458         }
1459 
1460         fb.clearUpdateNeeded();
1461     }
1462 
getFrameBufferSamplePositions(FrameBuffer fb)1463     public Vector2f[] getFrameBufferSamplePositions(FrameBuffer fb) {
1464         if (fb.getSamples() <= 1) {
1465             throw new IllegalArgumentException("Framebuffer must be multisampled");
1466         }
1467 
1468         setFrameBuffer(fb);
1469 
1470         Vector2f[] samplePositions = new Vector2f[fb.getSamples()];
1471         FloatBuffer samplePos = BufferUtils.createFloatBuffer(2);
1472         for (int i = 0; i < samplePositions.length; i++) {
1473             glGetMultisample(GL_SAMPLE_POSITION, i, samplePos);
1474             samplePos.clear();
1475             samplePositions[i] = new Vector2f(samplePos.get(0) - 0.5f,
1476                     samplePos.get(1) - 0.5f);
1477         }
1478         return samplePositions;
1479     }
1480 
setMainFrameBufferOverride(FrameBuffer fb)1481     public void setMainFrameBufferOverride(FrameBuffer fb){
1482         mainFbOverride = fb;
1483     }
1484 
setFrameBuffer(FrameBuffer fb)1485     public void setFrameBuffer(FrameBuffer fb) {
1486         if (fb == null && mainFbOverride != null){
1487             fb = mainFbOverride;
1488         }
1489 
1490         if (lastFb == fb) {
1491             if (fb == null || !fb.isUpdateNeeded()){
1492                 return;
1493             }
1494         }
1495 
1496         // generate mipmaps for last FB if needed
1497         if (lastFb != null) {
1498             for (int i = 0; i < lastFb.getNumColorBuffers(); i++) {
1499                 RenderBuffer rb = lastFb.getColorBuffer(i);
1500                 Texture tex = rb.getTexture();
1501                 if (tex != null
1502                         && tex.getMinFilter().usesMipMapLevels()) {
1503                     setTexture(0, rb.getTexture());
1504 
1505                     int textureType = convertTextureType(tex.getType(), tex.getImage().getMultiSamples());
1506                     glEnable(textureType);
1507                     glGenerateMipmapEXT(textureType);
1508                     glDisable(textureType);
1509                 }
1510             }
1511         }
1512 
1513         if (fb == null) {
1514             // unbind any fbos
1515             if (context.boundFBO != 0) {
1516                 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
1517                 statistics.onFrameBufferUse(null, true);
1518 
1519                 context.boundFBO = 0;
1520             }
1521             // select back buffer
1522             if (context.boundDrawBuf != -1) {
1523                 glDrawBuffer(initialDrawBuf);
1524                 context.boundDrawBuf = -1;
1525             }
1526             if (context.boundReadBuf != -1) {
1527                 glReadBuffer(initialReadBuf);
1528                 context.boundReadBuf = -1;
1529             }
1530 
1531             lastFb = null;
1532         } else {
1533             if (fb.getNumColorBuffers() == 0 && fb.getDepthBuffer() == null){
1534                 throw new IllegalArgumentException("The framebuffer: " + fb +
1535                                                    "\nDoesn't have any color/depth buffers");
1536             }
1537 
1538             if (fb.isUpdateNeeded()) {
1539                 updateFrameBuffer(fb);
1540             }
1541 
1542             if (context.boundFBO != fb.getId()) {
1543                 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb.getId());
1544                 statistics.onFrameBufferUse(fb, true);
1545 
1546                 // update viewport to reflect framebuffer's resolution
1547                 setViewPort(0, 0, fb.getWidth(), fb.getHeight());
1548 
1549                 context.boundFBO = fb.getId();
1550             } else {
1551                 statistics.onFrameBufferUse(fb, false);
1552             }
1553             if (fb.getNumColorBuffers() == 0) {
1554                 // make sure to select NONE as draw buf
1555                 // no color buffer attached. select NONE
1556                 if (context.boundDrawBuf != -2) {
1557                     glDrawBuffer(GL_NONE);
1558                     context.boundDrawBuf = -2;
1559                 }
1560                 if (context.boundReadBuf != -2) {
1561                     glReadBuffer(GL_NONE);
1562                     context.boundReadBuf = -2;
1563                 }
1564             } else {
1565                 if (fb.isMultiTarget()) {
1566                     if (fb.getNumColorBuffers() > maxMRTFBOAttachs) {
1567                         throw new RendererException("Framebuffer has more"
1568                                 + " targets than are supported"
1569                                 + " on the system!");
1570                     }
1571 
1572                     if (context.boundDrawBuf != 100 + fb.getNumColorBuffers()) {
1573                         intBuf16.clear();
1574                         for (int i = 0; i < fb.getNumColorBuffers(); i++) {
1575                             intBuf16.put(GL_COLOR_ATTACHMENT0_EXT + i);
1576                         }
1577 
1578                         intBuf16.flip();
1579                         glDrawBuffers(intBuf16);
1580                         context.boundDrawBuf = 100 + fb.getNumColorBuffers();
1581                     }
1582                 } else {
1583                     RenderBuffer rb = fb.getColorBuffer(fb.getTargetIndex());
1584                     // select this draw buffer
1585                     if (context.boundDrawBuf != rb.getSlot()) {
1586                         glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT + rb.getSlot());
1587                         context.boundDrawBuf = rb.getSlot();
1588                     }
1589                 }
1590             }
1591 
1592             assert fb.getId() >= 0;
1593             assert context.boundFBO == fb.getId();
1594 
1595             lastFb = fb;
1596 
1597             try {
1598                 checkFrameBufferError();
1599             } catch (IllegalStateException ex) {
1600                 logger.log(Level.SEVERE, "=== jMonkeyEngine FBO State ===\n{0}", fb);
1601                 printRealFrameBufferInfo(fb);
1602                 throw ex;
1603             }
1604         }
1605     }
1606 
readFrameBuffer(FrameBuffer fb, ByteBuffer byteBuf)1607     public void readFrameBuffer(FrameBuffer fb, ByteBuffer byteBuf) {
1608         if (fb != null) {
1609             RenderBuffer rb = fb.getColorBuffer();
1610             if (rb == null) {
1611                 throw new IllegalArgumentException("Specified framebuffer"
1612                         + " does not have a colorbuffer");
1613             }
1614 
1615             setFrameBuffer(fb);
1616             if (context.boundReadBuf != rb.getSlot()) {
1617                 glReadBuffer(GL_COLOR_ATTACHMENT0_EXT + rb.getSlot());
1618                 context.boundReadBuf = rb.getSlot();
1619             }
1620         } else {
1621             setFrameBuffer(null);
1622         }
1623 
1624         glReadPixels(vpX, vpY, vpW, vpH, /*GL_RGBA*/ GL_BGRA, GL_UNSIGNED_BYTE, byteBuf);
1625     }
1626 
deleteRenderBuffer(FrameBuffer fb, RenderBuffer rb)1627     private void deleteRenderBuffer(FrameBuffer fb, RenderBuffer rb) {
1628         intBuf1.put(0, rb.getId());
1629         glDeleteRenderbuffersEXT(intBuf1);
1630     }
1631 
deleteFrameBuffer(FrameBuffer fb)1632     public void deleteFrameBuffer(FrameBuffer fb) {
1633         if (fb.getId() != -1) {
1634             if (context.boundFBO == fb.getId()) {
1635                 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
1636                 context.boundFBO = 0;
1637             }
1638 
1639             if (fb.getDepthBuffer() != null) {
1640                 deleteRenderBuffer(fb, fb.getDepthBuffer());
1641             }
1642             if (fb.getColorBuffer() != null) {
1643                 deleteRenderBuffer(fb, fb.getColorBuffer());
1644             }
1645 
1646             intBuf1.put(0, fb.getId());
1647             glDeleteFramebuffersEXT(intBuf1);
1648             fb.resetObject();
1649 
1650             statistics.onDeleteFrameBuffer();
1651         }
1652     }
1653 
1654     /*********************************************************************\
1655     |* Textures                                                          *|
1656     \*********************************************************************/
convertTextureType(Texture.Type type, int samples)1657     private int convertTextureType(Texture.Type type, int samples) {
1658         switch (type) {
1659             case TwoDimensional:
1660                 if (samples > 1) {
1661                     return ARBTextureMultisample.GL_TEXTURE_2D_MULTISAMPLE;
1662                 } else {
1663                     return GL_TEXTURE_2D;
1664                 }
1665             case TwoDimensionalArray:
1666                 if (samples > 1) {
1667                     return ARBTextureMultisample.GL_TEXTURE_2D_MULTISAMPLE_ARRAY;
1668                 } else {
1669                     return EXTTextureArray.GL_TEXTURE_2D_ARRAY_EXT;
1670                 }
1671             case ThreeDimensional:
1672                 return GL_TEXTURE_3D;
1673             case CubeMap:
1674                 return GL_TEXTURE_CUBE_MAP;
1675             default:
1676                 throw new UnsupportedOperationException("Unknown texture type: " + type);
1677         }
1678     }
1679 
convertMagFilter(Texture.MagFilter filter)1680     private int convertMagFilter(Texture.MagFilter filter) {
1681         switch (filter) {
1682             case Bilinear:
1683                 return GL_LINEAR;
1684             case Nearest:
1685                 return GL_NEAREST;
1686             default:
1687                 throw new UnsupportedOperationException("Unknown mag filter: " + filter);
1688         }
1689     }
1690 
convertMinFilter(Texture.MinFilter filter)1691     private int convertMinFilter(Texture.MinFilter filter) {
1692         switch (filter) {
1693             case Trilinear:
1694                 return GL_LINEAR_MIPMAP_LINEAR;
1695             case BilinearNearestMipMap:
1696                 return GL_LINEAR_MIPMAP_NEAREST;
1697             case NearestLinearMipMap:
1698                 return GL_NEAREST_MIPMAP_LINEAR;
1699             case NearestNearestMipMap:
1700                 return GL_NEAREST_MIPMAP_NEAREST;
1701             case BilinearNoMipMaps:
1702                 return GL_LINEAR;
1703             case NearestNoMipMaps:
1704                 return GL_NEAREST;
1705             default:
1706                 throw new UnsupportedOperationException("Unknown min filter: " + filter);
1707         }
1708     }
1709 
convertWrapMode(Texture.WrapMode mode)1710     private int convertWrapMode(Texture.WrapMode mode) {
1711         switch (mode) {
1712             case BorderClamp:
1713                 return GL_CLAMP_TO_BORDER;
1714             case Clamp:
1715                 return GL_CLAMP;
1716             case EdgeClamp:
1717                 return GL_CLAMP_TO_EDGE;
1718             case Repeat:
1719                 return GL_REPEAT;
1720             case MirroredRepeat:
1721                 return GL_MIRRORED_REPEAT;
1722             default:
1723                 throw new UnsupportedOperationException("Unknown wrap mode: " + mode);
1724         }
1725     }
1726 
1727     @SuppressWarnings("fallthrough")
setupTextureParams(Texture tex)1728     private void setupTextureParams(Texture tex) {
1729         Image image = tex.getImage();
1730         int target = convertTextureType(tex.getType(), image != null ? image.getMultiSamples() : 1);
1731 
1732         // filter things
1733         int minFilter = convertMinFilter(tex.getMinFilter());
1734         int magFilter = convertMagFilter(tex.getMagFilter());
1735         glTexParameteri(target, GL_TEXTURE_MIN_FILTER, minFilter);
1736         glTexParameteri(target, GL_TEXTURE_MAG_FILTER, magFilter);
1737 
1738         if (tex.getAnisotropicFilter() > 1) {
1739             if (GLContext.getCapabilities().GL_EXT_texture_filter_anisotropic) {
1740                 glTexParameterf(target,
1741                         EXTTextureFilterAnisotropic.GL_TEXTURE_MAX_ANISOTROPY_EXT,
1742                         tex.getAnisotropicFilter());
1743             }
1744         }
1745 
1746         if (context.pointSprite) {
1747             return; // Attempt to fix glTexParameter crash for some ATI GPUs
1748         }
1749         // repeat modes
1750         switch (tex.getType()) {
1751             case ThreeDimensional:
1752             case CubeMap: // cubemaps use 3D coords
1753                 glTexParameteri(target, GL_TEXTURE_WRAP_R, convertWrapMode(tex.getWrap(WrapAxis.R)));
1754             case TwoDimensional:
1755             case TwoDimensionalArray:
1756                 glTexParameteri(target, GL_TEXTURE_WRAP_T, convertWrapMode(tex.getWrap(WrapAxis.T)));
1757                 // fall down here is intentional..
1758 //            case OneDimensional:
1759                 glTexParameteri(target, GL_TEXTURE_WRAP_S, convertWrapMode(tex.getWrap(WrapAxis.S)));
1760                 break;
1761             default:
1762                 throw new UnsupportedOperationException("Unknown texture type: " + tex.getType());
1763         }
1764 
1765         // R to Texture compare mode
1766         if (tex.getShadowCompareMode() != Texture.ShadowCompareMode.Off) {
1767             glTexParameteri(target, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE);
1768             glTexParameteri(target, GL_DEPTH_TEXTURE_MODE, GL_INTENSITY);
1769             if (tex.getShadowCompareMode() == Texture.ShadowCompareMode.GreaterOrEqual) {
1770                 glTexParameteri(target, GL_TEXTURE_COMPARE_FUNC, GL_GEQUAL);
1771             } else {
1772                 glTexParameteri(target, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);
1773             }
1774         }
1775     }
1776 
updateTexImageData(Image img, Texture.Type type, boolean mips, int unit)1777     public void updateTexImageData(Image img, Texture.Type type, boolean mips, int unit) {
1778         int texId = img.getId();
1779         if (texId == -1) {
1780             // create texture
1781             glGenTextures(intBuf1);
1782             texId = intBuf1.get(0);
1783             img.setId(texId);
1784             objManager.registerForCleanup(img);
1785 
1786             statistics.onNewTexture();
1787         }
1788 
1789         // bind texture
1790         int target = convertTextureType(type, img.getMultiSamples());
1791         if (context.boundTextureUnit != unit) {
1792             glActiveTexture(GL_TEXTURE0 + unit);
1793             context.boundTextureUnit = unit;
1794         }
1795         if (context.boundTextures[unit] != img) {
1796             glBindTexture(target, texId);
1797             context.boundTextures[unit] = img;
1798 
1799             statistics.onTextureUse(img, true);
1800         }
1801 
1802         if (!img.hasMipmaps() && mips) {
1803             // No pregenerated mips available,
1804             // generate from base level if required
1805             if (!GLContext.getCapabilities().OpenGL30) {
1806                 glTexParameteri(target, GL_GENERATE_MIPMAP, GL_TRUE);
1807             }
1808         } else {
1809 //          glTexParameteri(target, GL_TEXTURE_BASE_LEVEL, 0 );
1810             if (img.getMipMapSizes() != null) {
1811                 glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, img.getMipMapSizes().length);
1812             }
1813         }
1814 
1815         int imageSamples = img.getMultiSamples();
1816         if (imageSamples > 1) {
1817             if (img.getFormat().isDepthFormat()) {
1818                 img.setMultiSamples(Math.min(maxDepthTexSamples, imageSamples));
1819             } else {
1820                 img.setMultiSamples(Math.min(maxColorTexSamples, imageSamples));
1821             }
1822         }
1823 
1824         // Yes, some OpenGL2 cards (GeForce 5) still dont support NPOT.
1825         if (!GLContext.getCapabilities().GL_ARB_texture_non_power_of_two) {
1826             if (img.getWidth() != 0 && img.getHeight() != 0) {
1827                 if (!FastMath.isPowerOfTwo(img.getWidth())
1828                         || !FastMath.isPowerOfTwo(img.getHeight())) {
1829                     if (img.getData(0) == null) {
1830                         throw new RendererException("non-power-of-2 framebuffer textures are not supported by the video hardware");
1831                     } else {
1832                         MipMapGenerator.resizeToPowerOf2(img);
1833                     }
1834                 }
1835             }
1836         }
1837 
1838         // Check if graphics card doesn't support multisample textures
1839         if (!GLContext.getCapabilities().GL_ARB_texture_multisample) {
1840             if (img.getMultiSamples() > 1) {
1841                 throw new RendererException("Multisample textures not supported by graphics hardware");
1842             }
1843         }
1844 
1845         if (target == GL_TEXTURE_CUBE_MAP) {
1846             List<ByteBuffer> data = img.getData();
1847             if (data.size() != 6) {
1848                 logger.log(Level.WARNING, "Invalid texture: {0}\n"
1849                         + "Cubemap textures must contain 6 data units.", img);
1850                 return;
1851             }
1852             for (int i = 0; i < 6; i++) {
1853                 TextureUtil.uploadTexture(img, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, i, 0, tdc);
1854             }
1855         } else if (target == EXTTextureArray.GL_TEXTURE_2D_ARRAY_EXT) {
1856             List<ByteBuffer> data = img.getData();
1857             // -1 index specifies prepare data for 2D Array
1858             TextureUtil.uploadTexture(img, target, -1, 0, tdc);
1859             for (int i = 0; i < data.size(); i++) {
1860                 // upload each slice of 2D array in turn
1861                 // this time with the appropriate index
1862                 TextureUtil.uploadTexture(img, target, i, 0, tdc);
1863             }
1864         } else {
1865             TextureUtil.uploadTexture(img, target, 0, 0, tdc);
1866         }
1867 
1868         if (img.getMultiSamples() != imageSamples) {
1869             img.setMultiSamples(imageSamples);
1870         }
1871 
1872         if (GLContext.getCapabilities().OpenGL30) {
1873             if (!img.hasMipmaps() && mips && img.getData() != null) {
1874                 // XXX: Required for ATI
1875                 glEnable(target);
1876                 glGenerateMipmapEXT(target);
1877                 glDisable(target);
1878             }
1879         }
1880 
1881         img.clearUpdateNeeded();
1882     }
1883 
setTexture(int unit, Texture tex)1884     public void setTexture(int unit, Texture tex) {
1885         Image image = tex.getImage();
1886         if (image.isUpdateNeeded()) {
1887             updateTexImageData(image, tex.getType(), tex.getMinFilter().usesMipMapLevels(), unit);
1888         }
1889 
1890         int texId = image.getId();
1891         assert texId != -1;
1892 
1893         Image[] textures = context.boundTextures;
1894 
1895         int type = convertTextureType(tex.getType(), image.getMultiSamples());
1896 //        if (!context.textureIndexList.moveToNew(unit)) {
1897 //             if (context.boundTextureUnit != unit){
1898 //                glActiveTexture(GL_TEXTURE0 + unit);
1899 //                context.boundTextureUnit = unit;
1900 //             }
1901 //             glEnable(type);
1902 //        }
1903 
1904         if (context.boundTextureUnit != unit) {
1905             glActiveTexture(GL_TEXTURE0 + unit);
1906             context.boundTextureUnit = unit;
1907         }
1908         if (textures[unit] != image) {
1909             glBindTexture(type, texId);
1910             textures[unit] = image;
1911 
1912             statistics.onTextureUse(image, true);
1913         } else {
1914             statistics.onTextureUse(image, false);
1915         }
1916 
1917         setupTextureParams(tex);
1918     }
1919 
clearTextureUnits()1920     public void clearTextureUnits() {
1921 //        IDList textureList = context.textureIndexList;
1922 //        Image[] textures = context.boundTextures;
1923 //        for (int i = 0; i < textureList.oldLen; i++) {
1924 //            int idx = textureList.oldList[i];
1925 //            if (context.boundTextureUnit != idx){
1926 //                glActiveTexture(GL_TEXTURE0 + idx);
1927 //                context.boundTextureUnit = idx;
1928 //            }
1929 //            glDisable(convertTextureType(textures[idx].getType()));
1930 //            textures[idx] = null;
1931 //        }
1932 //        context.textureIndexList.copyNewToOld();
1933     }
1934 
deleteImage(Image image)1935     public void deleteImage(Image image) {
1936         int texId = image.getId();
1937         if (texId != -1) {
1938             intBuf1.put(0, texId);
1939             intBuf1.position(0).limit(1);
1940             glDeleteTextures(intBuf1);
1941             image.resetObject();
1942 
1943             statistics.onDeleteTexture();
1944         }
1945     }
1946 
1947     /*********************************************************************\
1948     |* Vertex Buffers and Attributes                                     *|
1949     \*********************************************************************/
convertUsage(Usage usage)1950     private int convertUsage(Usage usage) {
1951         switch (usage) {
1952             case Static:
1953                 return GL_STATIC_DRAW;
1954             case Dynamic:
1955                 return GL_DYNAMIC_DRAW;
1956             case Stream:
1957                 return GL_STREAM_DRAW;
1958             default:
1959                 throw new UnsupportedOperationException("Unknown usage type.");
1960         }
1961     }
1962 
convertFormat(Format format)1963     private int convertFormat(Format format) {
1964         switch (format) {
1965             case Byte:
1966                 return GL_BYTE;
1967             case UnsignedByte:
1968                 return GL_UNSIGNED_BYTE;
1969             case Short:
1970                 return GL_SHORT;
1971             case UnsignedShort:
1972                 return GL_UNSIGNED_SHORT;
1973             case Int:
1974                 return GL_INT;
1975             case UnsignedInt:
1976                 return GL_UNSIGNED_INT;
1977             case Half:
1978                 return NVHalfFloat.GL_HALF_FLOAT_NV;
1979 //                return ARBHalfFloatVertex.GL_HALF_FLOAT;
1980             case Float:
1981                 return GL_FLOAT;
1982             case Double:
1983                 return GL_DOUBLE;
1984             default:
1985                 throw new UnsupportedOperationException("Unknown buffer format.");
1986 
1987         }
1988     }
1989 
updateBufferData(VertexBuffer vb)1990     public void updateBufferData(VertexBuffer vb) {
1991         int bufId = vb.getId();
1992         boolean created = false;
1993         if (bufId == -1) {
1994             // create buffer
1995             glGenBuffers(intBuf1);
1996             bufId = intBuf1.get(0);
1997             vb.setId(bufId);
1998             objManager.registerForCleanup(vb);
1999 
2000             //statistics.onNewVertexBuffer();
2001 
2002             created = true;
2003         }
2004 
2005         // bind buffer
2006         int target;
2007         if (vb.getBufferType() == VertexBuffer.Type.Index) {
2008             target = GL_ELEMENT_ARRAY_BUFFER;
2009             if (context.boundElementArrayVBO != bufId) {
2010                 glBindBuffer(target, bufId);
2011                 context.boundElementArrayVBO = bufId;
2012                 //statistics.onVertexBufferUse(vb, true);
2013             }else{
2014                 //statistics.onVertexBufferUse(vb, false);
2015             }
2016         } else {
2017             target = GL_ARRAY_BUFFER;
2018             if (context.boundArrayVBO != bufId) {
2019                 glBindBuffer(target, bufId);
2020                 context.boundArrayVBO = bufId;
2021                 //statistics.onVertexBufferUse(vb, true);
2022             }else{
2023                 //statistics.onVertexBufferUse(vb, false);
2024             }
2025         }
2026 
2027         int usage = convertUsage(vb.getUsage());
2028         vb.getData().rewind();
2029 
2030         if (created || vb.hasDataSizeChanged()) {
2031             // upload data based on format
2032             switch (vb.getFormat()) {
2033                 case Byte:
2034                 case UnsignedByte:
2035                     glBufferData(target, (ByteBuffer) vb.getData(), usage);
2036                     break;
2037                 //            case Half:
2038                 case Short:
2039                 case UnsignedShort:
2040                     glBufferData(target, (ShortBuffer) vb.getData(), usage);
2041                     break;
2042                 case Int:
2043                 case UnsignedInt:
2044                     glBufferData(target, (IntBuffer) vb.getData(), usage);
2045                     break;
2046                 case Float:
2047                     glBufferData(target, (FloatBuffer) vb.getData(), usage);
2048                     break;
2049                 case Double:
2050                     glBufferData(target, (DoubleBuffer) vb.getData(), usage);
2051                     break;
2052                 default:
2053                     throw new UnsupportedOperationException("Unknown buffer format.");
2054             }
2055         } else {
2056             switch (vb.getFormat()) {
2057                 case Byte:
2058                 case UnsignedByte:
2059                     glBufferSubData(target, 0, (ByteBuffer) vb.getData());
2060                     break;
2061                 case Short:
2062                 case UnsignedShort:
2063                     glBufferSubData(target, 0, (ShortBuffer) vb.getData());
2064                     break;
2065                 case Int:
2066                 case UnsignedInt:
2067                     glBufferSubData(target, 0, (IntBuffer) vb.getData());
2068                     break;
2069                 case Float:
2070                     glBufferSubData(target, 0, (FloatBuffer) vb.getData());
2071                     break;
2072                 case Double:
2073                     glBufferSubData(target, 0, (DoubleBuffer) vb.getData());
2074                     break;
2075                 default:
2076                     throw new UnsupportedOperationException("Unknown buffer format.");
2077             }
2078         }
2079 //        }else{
2080 //            if (created || vb.hasDataSizeChanged()){
2081 //                glBufferData(target, vb.getData().capacity() * vb.getFormat().getComponentSize(), usage);
2082 //            }
2083 //
2084 //            ByteBuffer buf = glMapBuffer(target,
2085 //                                         GL_WRITE_ONLY,
2086 //                                         vb.getMappedData());
2087 //
2088 //            if (buf != vb.getMappedData()){
2089 //                buf = buf.order(ByteOrder.nativeOrder());
2090 //                vb.setMappedData(buf);
2091 //            }
2092 //
2093 //            buf.clear();
2094 //
2095 //            switch (vb.getFormat()){
2096 //                case Byte:
2097 //                case UnsignedByte:
2098 //                    buf.put( (ByteBuffer) vb.getData() );
2099 //                    break;
2100 //                case Short:
2101 //                case UnsignedShort:
2102 //                    buf.asShortBuffer().put( (ShortBuffer) vb.getData() );
2103 //                    break;
2104 //                case Int:
2105 //                case UnsignedInt:
2106 //                    buf.asIntBuffer().put( (IntBuffer) vb.getData() );
2107 //                    break;
2108 //                case Float:
2109 //                    buf.asFloatBuffer().put( (FloatBuffer) vb.getData() );
2110 //                    break;
2111 //                case Double:
2112 //                    break;
2113 //                default:
2114 //                    throw new RuntimeException("Unknown buffer format.");
2115 //            }
2116 //
2117 //            glUnmapBuffer(target);
2118 //        }
2119 
2120         vb.clearUpdateNeeded();
2121     }
2122 
deleteBuffer(VertexBuffer vb)2123     public void deleteBuffer(VertexBuffer vb) {
2124         int bufId = vb.getId();
2125         if (bufId != -1) {
2126             // delete buffer
2127             intBuf1.put(0, bufId);
2128             intBuf1.position(0).limit(1);
2129             glDeleteBuffers(intBuf1);
2130             vb.resetObject();
2131 
2132             //statistics.onDeleteVertexBuffer();
2133         }
2134     }
2135 
clearVertexAttribs()2136     public void clearVertexAttribs() {
2137         IDList attribList = context.attribIndexList;
2138         for (int i = 0; i < attribList.oldLen; i++) {
2139             int idx = attribList.oldList[i];
2140             glDisableVertexAttribArray(idx);
2141             context.boundAttribs[idx] = null;
2142         }
2143         context.attribIndexList.copyNewToOld();
2144     }
2145 
setVertexAttrib(VertexBuffer vb, VertexBuffer idb)2146     public void setVertexAttrib(VertexBuffer vb, VertexBuffer idb) {
2147         if (vb.getBufferType() == VertexBuffer.Type.Index) {
2148             throw new IllegalArgumentException("Index buffers not allowed to be set to vertex attrib");
2149         }
2150 
2151         int programId = context.boundShaderProgram;
2152         if (programId > 0) {
2153             Attribute attrib = boundShader.getAttribute(vb.getBufferType());
2154             int loc = attrib.getLocation();
2155             if (loc == -1) {
2156                 return; // not defined
2157             }
2158             if (loc == -2) {
2159                 stringBuf.setLength(0);
2160                 stringBuf.append("in").append(vb.getBufferType().name()).append('\0');
2161                 updateNameBuffer();
2162                 loc = glGetAttribLocation(programId, nameBuf);
2163 
2164                 // not really the name of it in the shader (inPosition\0) but
2165                 // the internal name of the enum (Position).
2166                 if (loc < 0) {
2167                     attrib.setLocation(-1);
2168                     return; // not available in shader.
2169                 } else {
2170                     attrib.setLocation(loc);
2171                 }
2172             }
2173 
2174             if (vb.isUpdateNeeded() && idb == null) {
2175                 updateBufferData(vb);
2176             }
2177 
2178             VertexBuffer[] attribs = context.boundAttribs;
2179             if (!context.attribIndexList.moveToNew(loc)) {
2180                 glEnableVertexAttribArray(loc);
2181                 //System.out.println("Enabled ATTRIB IDX: "+loc);
2182             }
2183             if (attribs[loc] != vb) {
2184                 // NOTE: Use id from interleaved buffer if specified
2185                 int bufId = idb != null ? idb.getId() : vb.getId();
2186                 assert bufId != -1;
2187                 if (context.boundArrayVBO != bufId) {
2188                     glBindBuffer(GL_ARRAY_BUFFER, bufId);
2189                     context.boundArrayVBO = bufId;
2190                     //statistics.onVertexBufferUse(vb, true);
2191                 }else{
2192                     //statistics.onVertexBufferUse(vb, false);
2193                 }
2194 
2195                 glVertexAttribPointer(loc,
2196                         vb.getNumComponents(),
2197                         convertFormat(vb.getFormat()),
2198                         vb.isNormalized(),
2199                         vb.getStride(),
2200                         vb.getOffset());
2201 
2202                 attribs[loc] = vb;
2203             }
2204         } else {
2205             throw new IllegalStateException("Cannot render mesh without shader bound");
2206         }
2207     }
2208 
setVertexAttrib(VertexBuffer vb)2209     public void setVertexAttrib(VertexBuffer vb) {
2210         setVertexAttrib(vb, null);
2211     }
2212 
drawTriangleArray(Mesh.Mode mode, int count, int vertCount)2213     public void drawTriangleArray(Mesh.Mode mode, int count, int vertCount) {
2214         if (count > 1) {
2215             ARBDrawInstanced.glDrawArraysInstancedARB(convertElementMode(mode), 0,
2216                     vertCount, count);
2217         } else {
2218             glDrawArrays(convertElementMode(mode), 0, vertCount);
2219         }
2220     }
2221 
drawTriangleList(VertexBuffer indexBuf, Mesh mesh, int count)2222     public void drawTriangleList(VertexBuffer indexBuf, Mesh mesh, int count) {
2223         if (indexBuf.getBufferType() != VertexBuffer.Type.Index) {
2224             throw new IllegalArgumentException("Only index buffers are allowed as triangle lists.");
2225         }
2226 
2227         if (indexBuf.isUpdateNeeded()) {
2228             updateBufferData(indexBuf);
2229         }
2230 
2231         int bufId = indexBuf.getId();
2232         assert bufId != -1;
2233 
2234         if (context.boundElementArrayVBO != bufId) {
2235             glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bufId);
2236             context.boundElementArrayVBO = bufId;
2237             //statistics.onVertexBufferUse(indexBuf, true);
2238         }else{
2239             //statistics.onVertexBufferUse(indexBuf, true);
2240         }
2241 
2242         int vertCount = mesh.getVertexCount();
2243         boolean useInstancing = count > 1 && caps.contains(Caps.MeshInstancing);
2244 
2245         if (mesh.getMode() == Mode.Hybrid) {
2246             int[] modeStart = mesh.getModeStart();
2247             int[] elementLengths = mesh.getElementLengths();
2248 
2249             int elMode = convertElementMode(Mode.Triangles);
2250             int fmt = convertFormat(indexBuf.getFormat());
2251             int elSize = indexBuf.getFormat().getComponentSize();
2252             int listStart = modeStart[0];
2253             int stripStart = modeStart[1];
2254             int fanStart = modeStart[2];
2255             int curOffset = 0;
2256             for (int i = 0; i < elementLengths.length; i++) {
2257                 if (i == stripStart) {
2258                     elMode = convertElementMode(Mode.TriangleStrip);
2259                 } else if (i == fanStart) {
2260                     elMode = convertElementMode(Mode.TriangleStrip);
2261                 }
2262                 int elementLength = elementLengths[i];
2263 
2264                 if (useInstancing) {
2265                     ARBDrawInstanced.glDrawElementsInstancedARB(elMode,
2266                             elementLength,
2267                             fmt,
2268                             curOffset,
2269                             count);
2270                 } else {
2271                     glDrawRangeElements(elMode,
2272                             0,
2273                             vertCount,
2274                             elementLength,
2275                             fmt,
2276                             curOffset);
2277                 }
2278 
2279                 curOffset += elementLength * elSize;
2280             }
2281         } else {
2282             if (useInstancing) {
2283                 ARBDrawInstanced.glDrawElementsInstancedARB(convertElementMode(mesh.getMode()),
2284                         indexBuf.getData().limit(),
2285                         convertFormat(indexBuf.getFormat()),
2286                         0,
2287                         count);
2288             } else {
2289                 glDrawRangeElements(convertElementMode(mesh.getMode()),
2290                         0,
2291                         vertCount,
2292                         indexBuf.getData().limit(),
2293                         convertFormat(indexBuf.getFormat()),
2294                         0);
2295             }
2296         }
2297     }
2298 
2299     /*********************************************************************\
2300     |* Render Calls                                                      *|
2301     \*********************************************************************/
convertElementMode(Mesh.Mode mode)2302     public int convertElementMode(Mesh.Mode mode) {
2303         switch (mode) {
2304             case Points:
2305                 return GL_POINTS;
2306             case Lines:
2307                 return GL_LINES;
2308             case LineLoop:
2309                 return GL_LINE_LOOP;
2310             case LineStrip:
2311                 return GL_LINE_STRIP;
2312             case Triangles:
2313                 return GL_TRIANGLES;
2314             case TriangleFan:
2315                 return GL_TRIANGLE_FAN;
2316             case TriangleStrip:
2317                 return GL_TRIANGLE_STRIP;
2318             default:
2319                 throw new UnsupportedOperationException("Unrecognized mesh mode: " + mode);
2320         }
2321     }
2322 
updateVertexArray(Mesh mesh)2323     public void updateVertexArray(Mesh mesh) {
2324         int id = mesh.getId();
2325         if (id == -1) {
2326             IntBuffer temp = intBuf1;
2327             ARBVertexArrayObject.glGenVertexArrays(temp);
2328             id = temp.get(0);
2329             mesh.setId(id);
2330         }
2331 
2332         if (context.boundVertexArray != id) {
2333             ARBVertexArrayObject.glBindVertexArray(id);
2334             context.boundVertexArray = id;
2335         }
2336 
2337         VertexBuffer interleavedData = mesh.getBuffer(Type.InterleavedData);
2338         if (interleavedData != null && interleavedData.isUpdateNeeded()) {
2339             updateBufferData(interleavedData);
2340         }
2341 
2342         IntMap<VertexBuffer> buffers = mesh.getBuffers();
2343         for (Entry<VertexBuffer> entry : buffers) {
2344             VertexBuffer vb = entry.getValue();
2345 
2346             if (vb.getBufferType() == Type.InterleavedData
2347                     || vb.getUsage() == Usage.CpuOnly // ignore cpu-only buffers
2348                     || vb.getBufferType() == Type.Index) {
2349                 continue;
2350             }
2351 
2352             if (vb.getStride() == 0) {
2353                 // not interleaved
2354                 setVertexAttrib(vb);
2355             } else {
2356                 // interleaved
2357                 setVertexAttrib(vb, interleavedData);
2358             }
2359         }
2360     }
2361 
renderMeshVertexArray(Mesh mesh, int lod, int count)2362     private void renderMeshVertexArray(Mesh mesh, int lod, int count) {
2363         if (mesh.getId() == -1){
2364             updateVertexArray(mesh);
2365         }else{
2366             // TODO: Check if it was updated
2367         }
2368 
2369         if (context.boundVertexArray != mesh.getId()) {
2370             ARBVertexArrayObject.glBindVertexArray(mesh.getId());
2371             context.boundVertexArray = mesh.getId();
2372         }
2373 
2374 //        IntMap<VertexBuffer> buffers = mesh.getBuffers();
2375         VertexBuffer indices = null;
2376         if (mesh.getNumLodLevels() > 0) {
2377             indices = mesh.getLodLevel(lod);
2378         } else {
2379             indices = mesh.getBuffer(Type.Index);
2380         }
2381         if (indices != null) {
2382             drawTriangleList(indices, mesh, count);
2383         } else {
2384             drawTriangleArray(mesh.getMode(), count, mesh.getVertexCount());
2385         }
2386         clearVertexAttribs();
2387         clearTextureUnits();
2388     }
2389 
renderMeshDefault(Mesh mesh, int lod, int count)2390     private void renderMeshDefault(Mesh mesh, int lod, int count) {
2391         VertexBuffer indices = null;
2392 
2393         VertexBuffer interleavedData = mesh.getBuffer(Type.InterleavedData);
2394         if (interleavedData != null && interleavedData.isUpdateNeeded()) {
2395             updateBufferData(interleavedData);
2396         }
2397 
2398 //        IntMap<VertexBuffer> buffers = mesh.getBuffers();
2399         SafeArrayList<VertexBuffer> buffersList = mesh.getBufferList();
2400 
2401         if (mesh.getNumLodLevels() > 0) {
2402             indices = mesh.getLodLevel(lod);
2403         } else {
2404             indices = mesh.getBuffer(Type.Index);
2405         }
2406 
2407 //        for (Entry<VertexBuffer> entry : buffers) {
2408 //             VertexBuffer vb = entry.getValue();
2409         for (VertexBuffer vb : mesh.getBufferList().getArray()){
2410             if (vb.getBufferType() == Type.InterleavedData
2411                     || vb.getUsage() == Usage.CpuOnly // ignore cpu-only buffers
2412                     || vb.getBufferType() == Type.Index) {
2413                 continue;
2414             }
2415 
2416             if (vb.getStride() == 0) {
2417                 // not interleaved
2418                 setVertexAttrib(vb);
2419             } else {
2420                 // interleaved
2421                 setVertexAttrib(vb, interleavedData);
2422             }
2423         }
2424 
2425         if (indices != null) {
2426             drawTriangleList(indices, mesh, count);
2427         } else {
2428             drawTriangleArray(mesh.getMode(), count, mesh.getVertexCount());
2429         }
2430         clearVertexAttribs();
2431         clearTextureUnits();
2432     }
2433 
renderMesh(Mesh mesh, int lod, int count)2434     public void renderMesh(Mesh mesh, int lod, int count) {
2435         if (mesh.getVertexCount() == 0) {
2436             return;
2437         }
2438 
2439         if (context.pointSprite && mesh.getMode() != Mode.Points){
2440             // XXX: Hack, disable point sprite mode if mesh not in point mode
2441             if (context.boundTextures[0] != null){
2442                 if (context.boundTextureUnit != 0){
2443                     glActiveTexture(GL_TEXTURE0);
2444                     context.boundTextureUnit = 0;
2445                 }
2446                 glDisable(GL_POINT_SPRITE);
2447                 glDisable(GL_VERTEX_PROGRAM_POINT_SIZE);
2448                 context.pointSprite = false;
2449             }
2450         }
2451 
2452         if (context.pointSize != mesh.getPointSize()) {
2453             glPointSize(mesh.getPointSize());
2454             context.pointSize = mesh.getPointSize();
2455         }
2456         if (context.lineWidth != mesh.getLineWidth()) {
2457             glLineWidth(mesh.getLineWidth());
2458             context.lineWidth = mesh.getLineWidth();
2459         }
2460 
2461         statistics.onMeshDrawn(mesh, lod);
2462 //        if (GLContext.getCapabilities().GL_ARB_vertex_array_object){
2463 //            renderMeshVertexArray(mesh, lod, count);
2464 //        }else{
2465             renderMeshDefault(mesh, lod, count);
2466 //        }
2467     }
2468 }
2469