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 */
32package com.jme3.renderer.lwjgl;
33
34import com.jme3.light.LightList;
35import com.jme3.material.RenderState;
36import com.jme3.material.RenderState.StencilOperation;
37import com.jme3.material.RenderState.TestFunction;
38import com.jme3.math.*;
39import com.jme3.renderer.*;
40import com.jme3.scene.Mesh;
41import com.jme3.scene.Mesh.Mode;
42import com.jme3.scene.VertexBuffer;
43import com.jme3.scene.VertexBuffer.Format;
44import com.jme3.scene.VertexBuffer.Type;
45import com.jme3.scene.VertexBuffer.Usage;
46import com.jme3.shader.Attribute;
47import com.jme3.shader.Shader;
48import com.jme3.shader.Shader.ShaderSource;
49import com.jme3.shader.Shader.ShaderType;
50import com.jme3.shader.Uniform;
51import com.jme3.texture.FrameBuffer;
52import com.jme3.texture.FrameBuffer.RenderBuffer;
53import com.jme3.texture.Image;
54import com.jme3.texture.Texture;
55import com.jme3.texture.Texture.WrapAxis;
56import com.jme3.util.BufferUtils;
57import com.jme3.util.IntMap;
58import com.jme3.util.IntMap.Entry;
59import com.jme3.util.ListMap;
60import com.jme3.util.NativeObjectManager;
61import com.jme3.util.SafeArrayList;
62import java.nio.*;
63import java.util.EnumSet;
64import java.util.List;
65import java.util.logging.Level;
66import java.util.logging.Logger;
67import jme3tools.converters.MipMapGenerator;
68import static org.lwjgl.opengl.ARBTextureMultisample.*;
69import static org.lwjgl.opengl.EXTFramebufferBlit.*;
70import static org.lwjgl.opengl.EXTFramebufferMultisample.*;
71import static org.lwjgl.opengl.EXTFramebufferObject.*;
72import static org.lwjgl.opengl.GL11.*;
73import static org.lwjgl.opengl.GL12.*;
74import static org.lwjgl.opengl.GL13.*;
75import static org.lwjgl.opengl.GL14.*;
76import static org.lwjgl.opengl.GL15.*;
77import static org.lwjgl.opengl.GL20.*;
78import org.lwjgl.opengl.*;
79//import static org.lwjgl.opengl.ARBDrawInstanced.*;
80
81public 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
118    public LwjglRenderer() {
119    }
120
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
133    public Statistics getStatistics() {
134        return statistics;
135    }
136
137    public EnumSet<Caps> getCaps() {
138        return caps;
139    }
140
141    @SuppressWarnings("fallthrough")
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
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
394    public void resetGLObjects() {
395        logger.log(Level.INFO, "Reseting objects and invalidating state");
396        objManager.resetObjects();
397        statistics.clearMemory();
398        invalidateState();
399    }
400
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
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    \*********************************************************************/
417    public void setDepthRange(float start, float end) {
418        glDepthRange(start, end);
419    }
420
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
451    public void setBackgroundColor(ColorRGBA color) {
452        glClearColor(color.r, color.g, color.b, color.a);
453    }
454
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
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
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
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    \*********************************************************************/
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
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
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
746    public void onFrame() {
747        objManager.deleteUnused(this);
748//        statistics.clearFrame();
749    }
750
751    public void setWorldMatrix(Matrix4f worldMatrix) {
752    }
753
754    public void setViewProjectionMatrices(Matrix4f viewMatrix, Matrix4f projMatrix) {
755    }
756
757    /*********************************************************************\
758    |* Shaders                                                           *|
759    \*********************************************************************/
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
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
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
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
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     */
910    public void setLighting(LightList list) {
911    }
912
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
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
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
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
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
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    \*********************************************************************/
1174    public void copyFrameBuffer(FrameBuffer src, FrameBuffer dst) {
1175        copyFrameBuffer(src, dst, true);
1176    }
1177
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
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
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
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
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
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
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
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
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
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
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
1481    public void setMainFrameBufferOverride(FrameBuffer fb){
1482        mainFbOverride = fb;
1483    }
1484
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
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
1627    private void deleteRenderBuffer(FrameBuffer fb, RenderBuffer rb) {
1628        intBuf1.put(0, rb.getId());
1629        glDeleteRenderbuffersEXT(intBuf1);
1630    }
1631
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    \*********************************************************************/
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
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
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
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")
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
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
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
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
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    \*********************************************************************/
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
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
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
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
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
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
2209    public void setVertexAttrib(VertexBuffer vb) {
2210        setVertexAttrib(vb, null);
2211    }
2212
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
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    \*********************************************************************/
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
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
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
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
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