1package com.jme3.renderer.lwjgl;
2
3import com.jme3.light.*;
4import com.jme3.material.FixedFuncBinding;
5import com.jme3.material.RenderState;
6import com.jme3.math.ColorRGBA;
7import com.jme3.math.FastMath;
8import com.jme3.math.Matrix4f;
9import com.jme3.math.Vector3f;
10import com.jme3.renderer.Caps;
11import com.jme3.renderer.GL1Renderer;
12import com.jme3.renderer.RenderContext;
13import com.jme3.renderer.Statistics;
14import com.jme3.scene.Mesh;
15import com.jme3.scene.Mesh.Mode;
16import com.jme3.scene.VertexBuffer;
17import com.jme3.scene.VertexBuffer.Type;
18import com.jme3.scene.VertexBuffer.Usage;
19import com.jme3.shader.Shader;
20import com.jme3.shader.Shader.ShaderSource;
21import com.jme3.texture.FrameBuffer;
22import com.jme3.texture.Image;
23import com.jme3.texture.Texture;
24import com.jme3.texture.Texture.WrapAxis;
25import com.jme3.util.BufferUtils;
26import com.jme3.util.IntMap;
27import com.jme3.util.IntMap.Entry;
28import com.jme3.util.NativeObjectManager;
29import java.nio.*;
30import java.util.ArrayList;
31import java.util.EnumSet;
32import java.util.logging.Level;
33import java.util.logging.Logger;
34import jme3tools.converters.MipMapGenerator;
35import static org.lwjgl.opengl.GL11.*;
36import org.lwjgl.opengl.GL12;
37import org.lwjgl.opengl.GL14;
38import org.lwjgl.opengl.GLContext;
39
40public class LwjglGL1Renderer implements GL1Renderer {
41
42    private static final Logger logger = Logger.getLogger(LwjglRenderer.class.getName());
43    private final ByteBuffer nameBuf = BufferUtils.createByteBuffer(250);
44    private final StringBuilder stringBuf = new StringBuilder(250);
45    private final IntBuffer ib1 = BufferUtils.createIntBuffer(1);
46    private final IntBuffer intBuf16 = BufferUtils.createIntBuffer(16);
47    private final FloatBuffer fb16 = BufferUtils.createFloatBuffer(16);
48    private final FloatBuffer fb4Null = BufferUtils.createFloatBuffer(4);
49    private final RenderContext context = new RenderContext();
50    private final NativeObjectManager objManager = new NativeObjectManager();
51    private final EnumSet<Caps> caps = EnumSet.noneOf(Caps.class);
52    private int maxTexSize;
53    private int maxCubeTexSize;
54    private int maxVertCount;
55    private int maxTriCount;
56    private int maxLights;
57    private boolean gl12 = false;
58    private final Statistics statistics = new Statistics();
59    private int vpX, vpY, vpW, vpH;
60    private int clipX, clipY, clipW, clipH;
61
62    private Matrix4f worldMatrix = new Matrix4f();
63    private Matrix4f viewMatrix = new Matrix4f();
64
65    private ArrayList<Light> lightList = new ArrayList<Light>(8);
66    private ColorRGBA materialAmbientColor = new ColorRGBA();
67    private Vector3f tempVec = new Vector3f();
68
69    protected void updateNameBuffer() {
70        int len = stringBuf.length();
71
72        nameBuf.position(0);
73        nameBuf.limit(len);
74        for (int i = 0; i < len; i++) {
75            nameBuf.put((byte) stringBuf.charAt(i));
76        }
77
78        nameBuf.rewind();
79    }
80
81    public Statistics getStatistics() {
82        return statistics;
83    }
84
85    public EnumSet<Caps> getCaps() {
86        return caps;
87    }
88
89    public void initialize() {
90        if (GLContext.getCapabilities().OpenGL12){
91            gl12 = true;
92        }
93
94        // Default values for certain GL state.
95        glShadeModel(GL_SMOOTH);
96        glColorMaterial(GL_FRONT_AND_BACK, GL_DIFFUSE);
97        glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
98
99		// Enable rescaling/normaling of normal vectors.
100		// Fixes lighting issues with scaled models.
101        if (gl12){
102            glEnable(GL12.GL_RESCALE_NORMAL);
103        }else{
104            glEnable(GL_NORMALIZE);
105        }
106
107        if (GLContext.getCapabilities().GL_ARB_texture_non_power_of_two) {
108            caps.add(Caps.NonPowerOfTwoTextures);
109        } else {
110            logger.log(Level.WARNING, "Your graphics card does not "
111                    + "support non-power-of-2 textures. "
112                    + "Some features might not work.");
113        }
114
115        maxLights = glGetInteger(GL_MAX_LIGHTS);
116
117    }
118
119    public void invalidateState() {
120        context.reset();
121    }
122
123    public void resetGLObjects() {
124        logger.log(Level.INFO, "Reseting objects and invalidating state");
125        objManager.resetObjects();
126        statistics.clearMemory();
127        invalidateState();
128    }
129
130    public void cleanup() {
131        logger.log(Level.INFO, "Deleting objects and invalidating state");
132        objManager.deleteAllObjects(this);
133        statistics.clearMemory();
134        invalidateState();
135    }
136
137    public void setDepthRange(float start, float end) {
138        glDepthRange(start, end);
139    }
140
141    public void clearBuffers(boolean color, boolean depth, boolean stencil) {
142        int bits = 0;
143        if (color) {
144            //See explanations of the depth below, we must enable color write to be able to clear the color buffer
145            if (context.colorWriteEnabled == false) {
146                glColorMask(true, true, true, true);
147                context.colorWriteEnabled = true;
148            }
149            bits = GL_COLOR_BUFFER_BIT;
150        }
151        if (depth) {
152
153            //glClear(GL_DEPTH_BUFFER_BIT) seems to not work when glDepthMask is false
154            //here s some link on openl board
155            //http://www.opengl.org/discussion_boards/ubbthreads.php?ubb=showflat&Number=257223
156            //if depth clear is requested, we enable the depthMask
157            if (context.depthWriteEnabled == false) {
158                glDepthMask(true);
159                context.depthWriteEnabled = true;
160            }
161            bits |= GL_DEPTH_BUFFER_BIT;
162        }
163        if (stencil) {
164            bits |= GL_STENCIL_BUFFER_BIT;
165        }
166        if (bits != 0) {
167            glClear(bits);
168        }
169    }
170
171    public void setBackgroundColor(ColorRGBA color) {
172        glClearColor(color.r, color.g, color.b, color.a);
173    }
174
175    private void setMaterialColor(int type, ColorRGBA color, ColorRGBA defaultColor) {
176        if (color != null){
177            fb16.put(color.r).put(color.g).put(color.b).put(color.a).flip();
178        }else{
179            fb16.put(defaultColor.r).put(defaultColor.g).put(defaultColor.b).put(defaultColor.a).flip();
180        }
181        glMaterial(GL_FRONT_AND_BACK, type, fb16);
182    }
183
184    /**
185     * Applies fixed function bindings from the context to OpenGL
186     */
187    private void applyFixedFuncBindings(boolean forLighting){
188        if (forLighting){
189            glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, context.shininess);
190            setMaterialColor(GL_AMBIENT,  context.ambient,  ColorRGBA.DarkGray);
191            setMaterialColor(GL_DIFFUSE,  context.diffuse,  ColorRGBA.White);
192            setMaterialColor(GL_SPECULAR, context.specular, ColorRGBA.Black);
193
194            if (context.useVertexColor){
195                glEnable(GL_COLOR_MATERIAL);
196            }else{
197                glDisable(GL_COLOR_MATERIAL);
198            }
199        }else{
200            // Ignore other values as they have no effect when
201            // GL_LIGHTING is disabled.
202            ColorRGBA color = context.color;
203            if (color != null){
204                glColor4f(color.r, color.g, color.b, color.a);
205            }else{
206                glColor4f(1,1,1,1);
207            }
208        }
209    }
210
211    /**
212     * Reset fixed function bindings to default values.
213     */
214    private void resetFixedFuncBindings(){
215        context.color = null;
216        context.ambient = null;
217        context.diffuse = null;
218        context.specular = null;
219        context.shininess = 0;
220        context.useVertexColor = false;
221    }
222
223    public void setFixedFuncBinding(FixedFuncBinding ffBinding, Object val) {
224        switch (ffBinding) {
225            case Color:
226                context.color = (ColorRGBA) val;
227                break;
228            case MaterialAmbient:
229                context.ambient = (ColorRGBA) val;
230                break;
231            case MaterialDiffuse:
232                context.diffuse = (ColorRGBA) val;
233                break;
234            case MaterialSpecular:
235                context.specular = (ColorRGBA) val;
236                break;
237            case MaterialShininess:
238                context.shininess = (Float) val;
239                break;
240            case UseVertexColor:
241                context.useVertexColor = (Boolean) val;
242                break;
243        }
244    }
245
246    public void applyRenderState(RenderState state) {
247        if (state.isWireframe() && !context.wireframe) {
248            glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
249            context.wireframe = true;
250        } else if (!state.isWireframe() && context.wireframe) {
251            glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
252            context.wireframe = false;
253        }
254
255        if (state.isDepthTest() && !context.depthTestEnabled) {
256            glEnable(GL_DEPTH_TEST);
257            glDepthFunc(GL_LEQUAL);
258            context.depthTestEnabled = true;
259        } else if (!state.isDepthTest() && context.depthTestEnabled) {
260            glDisable(GL_DEPTH_TEST);
261            context.depthTestEnabled = false;
262        }
263
264        if (state.isAlphaTest() && !context.alphaTestEnabled) {
265            glEnable(GL_ALPHA_TEST);
266            glAlphaFunc(GL_GREATER, state.getAlphaFallOff());
267            context.alphaTestEnabled = true;
268        } else if (!state.isAlphaTest() && context.alphaTestEnabled) {
269            glDisable(GL_ALPHA_TEST);
270            context.alphaTestEnabled = false;
271        }
272
273        if (state.isDepthWrite() && !context.depthWriteEnabled) {
274            glDepthMask(true);
275            context.depthWriteEnabled = true;
276        } else if (!state.isDepthWrite() && context.depthWriteEnabled) {
277            glDepthMask(false);
278            context.depthWriteEnabled = false;
279        }
280
281        if (state.isColorWrite() && !context.colorWriteEnabled) {
282            glColorMask(true, true, true, true);
283            context.colorWriteEnabled = true;
284        } else if (!state.isColorWrite() && context.colorWriteEnabled) {
285            glColorMask(false, false, false, false);
286            context.colorWriteEnabled = false;
287        }
288
289        if (state.isPointSprite()) {
290            logger.log(Level.WARNING, "Point Sprite unsupported!");
291        }
292
293        if (state.isPolyOffset()) {
294            if (!context.polyOffsetEnabled) {
295                glEnable(GL_POLYGON_OFFSET_FILL);
296                glPolygonOffset(state.getPolyOffsetFactor(),
297                        state.getPolyOffsetUnits());
298                context.polyOffsetEnabled = true;
299                context.polyOffsetFactor = state.getPolyOffsetFactor();
300                context.polyOffsetUnits = state.getPolyOffsetUnits();
301            } else {
302                if (state.getPolyOffsetFactor() != context.polyOffsetFactor
303                        || state.getPolyOffsetUnits() != context.polyOffsetUnits) {
304                    glPolygonOffset(state.getPolyOffsetFactor(),
305                            state.getPolyOffsetUnits());
306                    context.polyOffsetFactor = state.getPolyOffsetFactor();
307                    context.polyOffsetUnits = state.getPolyOffsetUnits();
308                }
309            }
310        } else {
311            if (context.polyOffsetEnabled) {
312                glDisable(GL_POLYGON_OFFSET_FILL);
313                context.polyOffsetEnabled = false;
314                context.polyOffsetFactor = 0;
315                context.polyOffsetUnits = 0;
316            }
317        }
318        if (state.getFaceCullMode() != context.cullMode) {
319            if (state.getFaceCullMode() == RenderState.FaceCullMode.Off) {
320                glDisable(GL_CULL_FACE);
321            } else {
322                glEnable(GL_CULL_FACE);
323            }
324
325            switch (state.getFaceCullMode()) {
326                case Off:
327                    break;
328                case Back:
329                    glCullFace(GL_BACK);
330                    break;
331                case Front:
332                    glCullFace(GL_FRONT);
333                    break;
334                case FrontAndBack:
335                    glCullFace(GL_FRONT_AND_BACK);
336                    break;
337                default:
338                    throw new UnsupportedOperationException("Unrecognized face cull mode: "
339                            + state.getFaceCullMode());
340            }
341
342            context.cullMode = state.getFaceCullMode();
343        }
344
345        if (state.getBlendMode() != context.blendMode) {
346            if (state.getBlendMode() == RenderState.BlendMode.Off) {
347                glDisable(GL_BLEND);
348            } else {
349                glEnable(GL_BLEND);
350                switch (state.getBlendMode()) {
351                    case Off:
352                        break;
353                    case Additive:
354                        glBlendFunc(GL_ONE, GL_ONE);
355                        break;
356                    case AlphaAdditive:
357                        glBlendFunc(GL_SRC_ALPHA, GL_ONE);
358                        break;
359                    case Color:
360                        glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_COLOR);
361                        break;
362                    case Alpha:
363                        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
364                        break;
365                    case PremultAlpha:
366                        glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
367                        break;
368                    case Modulate:
369                        glBlendFunc(GL_DST_COLOR, GL_ZERO);
370                        break;
371                    case ModulateX2:
372                        glBlendFunc(GL_DST_COLOR, GL_SRC_COLOR);
373                        break;
374                    default:
375                        throw new UnsupportedOperationException("Unrecognized blend mode: "
376                                + state.getBlendMode());
377                }
378            }
379
380            context.blendMode = state.getBlendMode();
381        }
382
383        if (state.isStencilTest()) {
384            throw new UnsupportedOperationException("OpenGL 1.1 doesn't support two sided stencil operations.");
385        }
386
387    }
388
389    public void setViewPort(int x, int y, int w, int h) {
390        if (x != vpX || vpY != y || vpW != w || vpH != h) {
391            glViewport(x, y, w, h);
392            vpX = x;
393            vpY = y;
394            vpW = w;
395            vpH = h;
396        }
397    }
398
399    public void setClipRect(int x, int y, int width, int height) {
400        if (!context.clipRectEnabled) {
401            glEnable(GL_SCISSOR_TEST);
402            context.clipRectEnabled = true;
403        }
404        if (clipX != x || clipY != y || clipW != width || clipH != height) {
405            glScissor(x, y, width, height);
406            clipX = x;
407            clipY = y;
408            clipW = width;
409            clipH = height;
410        }
411    }
412
413    public void clearClipRect() {
414        if (context.clipRectEnabled) {
415            glDisable(GL_SCISSOR_TEST);
416            context.clipRectEnabled = false;
417
418            clipX = 0;
419            clipY = 0;
420            clipW = 0;
421            clipH = 0;
422        }
423    }
424
425    public void onFrame() {
426        objManager.deleteUnused(this);
427//        statistics.clearFrame();
428    }
429
430    private FloatBuffer storeMatrix(Matrix4f matrix, FloatBuffer store) {
431        store.clear();
432        matrix.fillFloatBuffer(store, true);
433        store.clear();
434        return store;
435    }
436
437    private void setModelView(Matrix4f modelMatrix, Matrix4f viewMatrix){
438        if (context.matrixMode != GL_MODELVIEW) {
439            glMatrixMode(GL_MODELVIEW);
440            context.matrixMode = GL_MODELVIEW;
441        }
442
443        glLoadMatrix(storeMatrix(viewMatrix, fb16));
444        glMultMatrix(storeMatrix(modelMatrix, fb16));
445    }
446
447    private void setProjection(Matrix4f projMatrix){
448        if (context.matrixMode != GL_PROJECTION) {
449            glMatrixMode(GL_PROJECTION);
450            context.matrixMode = GL_PROJECTION;
451        }
452
453        glLoadMatrix(storeMatrix(projMatrix, fb16));
454    }
455
456    public void setWorldMatrix(Matrix4f worldMatrix) {
457        this.worldMatrix.set(worldMatrix);
458    }
459
460    public void setViewProjectionMatrices(Matrix4f viewMatrix, Matrix4f projMatrix) {
461        this.viewMatrix.set(viewMatrix);
462        setProjection(projMatrix);
463    }
464
465    public void setLighting(LightList list) {
466        // XXX: This is abuse of setLighting() to
467		// apply fixed function bindings
468        // and do other book keeping.
469        if (list == null || list.size() == 0){
470            glDisable(GL_LIGHTING);
471            applyFixedFuncBindings(false);
472            setModelView(worldMatrix, viewMatrix);
473            return;
474        }
475
476        // Number of lights set previously
477        int numLightsSetPrev = lightList.size();
478
479        // If more than maxLights are defined, they will be ignored.
480        // The GL1 renderer is not permitted to crash due to a
481        // GL1 limitation. It must render anything that the GL2 renderer
482        // can render (even incorrectly).
483        lightList.clear();
484        materialAmbientColor.set(0, 0, 0, 0);
485
486        for (int i = 0; i < list.size(); i++){
487            Light l = list.get(i);
488            if (l.getType() == Light.Type.Ambient){
489                // Gather
490                materialAmbientColor.addLocal(l.getColor());
491            }else{
492                // Add to list
493                lightList.add(l);
494
495                // Once maximum lights reached, exit loop.
496                if (lightList.size() >= maxLights){
497                    break;
498                }
499            }
500        }
501
502        applyFixedFuncBindings(true);
503
504        glEnable(GL_LIGHTING);
505
506        fb16.clear();
507        fb16.put(materialAmbientColor.r)
508            .put(materialAmbientColor.g)
509            .put(materialAmbientColor.b)
510            .put(1).flip();
511
512        glLightModel(GL_LIGHT_MODEL_AMBIENT, fb16);
513
514        if (context.matrixMode != GL_MODELVIEW) {
515            glMatrixMode(GL_MODELVIEW);
516            context.matrixMode = GL_MODELVIEW;
517        }
518        // Lights are already in world space, so just convert
519        // them to view space.
520        glLoadMatrix(storeMatrix(viewMatrix, fb16));
521
522        for (int i = 0; i < lightList.size(); i++){
523            int glLightIndex = GL_LIGHT0 + i;
524            Light light = lightList.get(i);
525            Light.Type lightType = light.getType();
526            ColorRGBA col = light.getColor();
527            Vector3f pos;
528
529            // Enable the light
530            glEnable(glLightIndex);
531
532            // OGL spec states default value for light ambient is black
533            switch (lightType){
534                case Directional:
535                    DirectionalLight dLight = (DirectionalLight) light;
536
537                    fb16.clear();
538                    fb16.put(col.r).put(col.g).put(col.b).put(col.a).flip();
539                    glLight(glLightIndex, GL_DIFFUSE, fb16);
540                    glLight(glLightIndex, GL_SPECULAR, fb16);
541
542                    pos = tempVec.set(dLight.getDirection()).negateLocal().normalizeLocal();
543                    fb16.clear();
544                    fb16.put(pos.x).put(pos.y).put(pos.z).put(0.0f).flip();
545                    glLight(glLightIndex, GL_POSITION, fb16);
546                    glLightf(glLightIndex, GL_SPOT_CUTOFF, 180);
547                    break;
548                case Point:
549                    PointLight pLight = (PointLight) light;
550
551                    fb16.clear();
552                    fb16.put(col.r).put(col.g).put(col.b).put(col.a).flip();
553                    glLight(glLightIndex, GL_DIFFUSE, fb16);
554                    glLight(glLightIndex, GL_SPECULAR, fb16);
555
556                    pos = pLight.getPosition();
557                    fb16.clear();
558                    fb16.put(pos.x).put(pos.y).put(pos.z).put(1.0f).flip();
559                    glLight(glLightIndex, GL_POSITION, fb16);
560                    glLightf(glLightIndex, GL_SPOT_CUTOFF, 180);
561
562                    if (pLight.getRadius() > 0) {
563                        // Note: this doesn't follow the same attenuation model
564                        // as the one used in the lighting shader.
565                        glLightf(glLightIndex, GL_CONSTANT_ATTENUATION,  1);
566                        glLightf(glLightIndex, GL_LINEAR_ATTENUATION,    pLight.getInvRadius() * 2);
567                        glLightf(glLightIndex, GL_QUADRATIC_ATTENUATION, pLight.getInvRadius() * pLight.getInvRadius());
568                    }else{
569                        glLightf(glLightIndex, GL_CONSTANT_ATTENUATION,  1);
570                        glLightf(glLightIndex, GL_LINEAR_ATTENUATION,    0);
571                        glLightf(glLightIndex, GL_QUADRATIC_ATTENUATION, 0);
572                    }
573
574                    break;
575                case Spot:
576                    SpotLight sLight = (SpotLight) light;
577
578                    fb16.clear();
579                    fb16.put(col.r).put(col.g).put(col.b).put(col.a).flip();
580                    glLight(glLightIndex, GL_DIFFUSE, fb16);
581                    glLight(glLightIndex, GL_SPECULAR, fb16);
582
583                    pos = sLight.getPosition();
584                    fb16.clear();
585                    fb16.put(pos.x).put(pos.y).put(pos.z).put(1.0f).flip();
586                    glLight(glLightIndex, GL_POSITION, fb16);
587
588                    Vector3f dir = sLight.getDirection();
589                    fb16.clear();
590                    fb16.put(dir.x).put(dir.y).put(dir.z).put(1.0f).flip();
591                    glLight(glLightIndex, GL_SPOT_DIRECTION, fb16);
592
593                    float outerAngleRad = sLight.getSpotOuterAngle();
594                    float innerAngleRad = sLight.getSpotInnerAngle();
595                    float spotCut = outerAngleRad * FastMath.RAD_TO_DEG;
596                    float spotExpo = 0.0f;
597                    if (outerAngleRad > 0) {
598                        spotExpo = (1.0f - (innerAngleRad / outerAngleRad)) * 128.0f;
599                    }
600
601                    glLightf(glLightIndex, GL_SPOT_CUTOFF, spotCut);
602                    glLightf(glLightIndex, GL_SPOT_EXPONENT, spotExpo);
603
604                    if (sLight.getSpotRange() > 0) {
605                        glLightf(glLightIndex, GL_LINEAR_ATTENUATION, sLight.getInvSpotRange());
606                    }else{
607                        glLightf(glLightIndex, GL_LINEAR_ATTENUATION, 0);
608                    }
609
610                    break;
611                default:
612                    throw new UnsupportedOperationException(
613                            "Unrecognized light type: " + lightType);
614            }
615        }
616
617        // Disable lights after the index
618        for (int i = lightList.size(); i < numLightsSetPrev; i++){
619            glDisable(GL_LIGHT0 + i);
620        }
621
622        // This will set view matrix as well.
623        setModelView(worldMatrix, viewMatrix);
624    }
625
626    private int convertTextureType(Texture.Type type) {
627        switch (type) {
628            case TwoDimensional:
629                return GL_TEXTURE_2D;
630//            case ThreeDimensional:
631//                return GL_TEXTURE_3D;
632//            case CubeMap:
633//                return GL_TEXTURE_CUBE_MAP;
634            default:
635                throw new UnsupportedOperationException("Unknown texture type: " + type);
636        }
637    }
638
639    private int convertMagFilter(Texture.MagFilter filter) {
640        switch (filter) {
641            case Bilinear:
642                return GL_LINEAR;
643            case Nearest:
644                return GL_NEAREST;
645            default:
646                throw new UnsupportedOperationException("Unknown mag filter: " + filter);
647        }
648    }
649
650    private int convertMinFilter(Texture.MinFilter filter) {
651        switch (filter) {
652            case Trilinear:
653                return GL_LINEAR_MIPMAP_LINEAR;
654            case BilinearNearestMipMap:
655                return GL_LINEAR_MIPMAP_NEAREST;
656            case NearestLinearMipMap:
657                return GL_NEAREST_MIPMAP_LINEAR;
658            case NearestNearestMipMap:
659                return GL_NEAREST_MIPMAP_NEAREST;
660            case BilinearNoMipMaps:
661                return GL_LINEAR;
662            case NearestNoMipMaps:
663                return GL_NEAREST;
664            default:
665                throw new UnsupportedOperationException("Unknown min filter: " + filter);
666        }
667    }
668
669    private int convertWrapMode(Texture.WrapMode mode) {
670        switch (mode) {
671            case EdgeClamp:
672            case Clamp:
673            case BorderClamp:
674                return GL_CLAMP;
675            case Repeat:
676                return GL_REPEAT;
677            default:
678                throw new UnsupportedOperationException("Unknown wrap mode: " + mode);
679        }
680    }
681
682    private void setupTextureParams(Texture tex) {
683        int target = convertTextureType(tex.getType());
684
685        // filter things
686        int minFilter = convertMinFilter(tex.getMinFilter());
687        int magFilter = convertMagFilter(tex.getMagFilter());
688        glTexParameteri(target, GL_TEXTURE_MIN_FILTER, minFilter);
689        glTexParameteri(target, GL_TEXTURE_MAG_FILTER, magFilter);
690
691        // repeat modes
692        switch (tex.getType()) {
693//            case ThreeDimensional:
694//            case CubeMap:
695//                glTexParameteri(target, GL_TEXTURE_WRAP_R, convertWrapMode(tex.getWrap(WrapAxis.R)));
696            case TwoDimensional:
697                glTexParameteri(target, GL_TEXTURE_WRAP_T, convertWrapMode(tex.getWrap(WrapAxis.T)));
698                // fall down here is intentional..
699//            case OneDimensional:
700                glTexParameteri(target, GL_TEXTURE_WRAP_S, convertWrapMode(tex.getWrap(WrapAxis.S)));
701                break;
702            default:
703                throw new UnsupportedOperationException("Unknown texture type: " + tex.getType());
704        }
705    }
706
707    public void updateTexImageData(Image img, Texture.Type type, boolean mips, int unit) {
708        int texId = img.getId();
709        if (texId == -1) {
710            // create texture
711            glGenTextures(ib1);
712            texId = ib1.get(0);
713            img.setId(texId);
714            objManager.registerForCleanup(img);
715
716            statistics.onNewTexture();
717        }
718
719        // bind texture
720        int target = convertTextureType(type);
721//        if (context.boundTextureUnit != unit) {
722//            glActiveTexture(GL_TEXTURE0 + unit);
723//            context.boundTextureUnit = unit;
724//        }
725        if (context.boundTextures[unit] != img) {
726            glEnable(target);
727            glBindTexture(target, texId);
728            context.boundTextures[unit] = img;
729
730            statistics.onTextureUse(img, true);
731        }
732
733        // Check sizes if graphics card doesn't support NPOT
734        if (!GLContext.getCapabilities().GL_ARB_texture_non_power_of_two) {
735            if (img.getWidth() != 0 && img.getHeight() != 0) {
736                if (!FastMath.isPowerOfTwo(img.getWidth())
737                        || !FastMath.isPowerOfTwo(img.getHeight())) {
738
739                    // Resize texture to Power-of-2 size
740                    MipMapGenerator.resizeToPowerOf2(img);
741
742                }
743            }
744        }
745
746        if (!img.hasMipmaps() && mips) {
747            // No pregenerated mips available,
748            // generate from base level if required
749
750            // Check if hardware mips are supported
751            if (GLContext.getCapabilities().OpenGL14) {
752                glTexParameteri(target, GL14.GL_GENERATE_MIPMAP, GL_TRUE);
753            } else {
754                MipMapGenerator.generateMipMaps(img);
755            }
756        } else {
757        }
758
759        /*
760        if (target == GL_TEXTURE_CUBE_MAP) {
761        List<ByteBuffer> data = img.getData();
762        if (data.size() != 6) {
763        logger.log(Level.WARNING, "Invalid texture: {0}\n"
764        + "Cubemap textures must contain 6 data units.", img);
765        return;
766        }
767        for (int i = 0; i < 6; i++) {
768        TextureUtil.uploadTexture(img, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, i, 0, tdc);
769        }
770        } else if (target == EXTTextureArray.GL_TEXTURE_2D_ARRAY_EXT) {
771        List<ByteBuffer> data = img.getData();
772        // -1 index specifies prepare data for 2D Array
773        TextureUtil.uploadTexture(img, target, -1, 0, tdc);
774        for (int i = 0; i < data.size(); i++) {
775        // upload each slice of 2D array in turn
776        // this time with the appropriate index
777        TextureUtil.uploadTexture(img, target, i, 0, tdc);
778        }
779        } else {*/
780        TextureUtil.uploadTexture(img, target, 0, 0, false);
781        //}
782
783        img.clearUpdateNeeded();
784    }
785
786    public void setTexture(int unit, Texture tex) {
787        if (unit != 0 || tex.getType() != Texture.Type.TwoDimensional) {
788            //throw new UnsupportedOperationException();
789            return;
790        }
791
792        Image image = tex.getImage();
793        if (image.isUpdateNeeded()) {
794            updateTexImageData(image, tex.getType(), tex.getMinFilter().usesMipMapLevels(), unit);
795        }
796
797        int texId = image.getId();
798        assert texId != -1;
799
800        Image[] textures = context.boundTextures;
801
802        int type = convertTextureType(tex.getType());
803//        if (!context.textureIndexList.moveToNew(unit)) {
804//             if (context.boundTextureUnit != unit){
805//                glActiveTexture(GL_TEXTURE0 + unit);
806//                context.boundTextureUnit = unit;
807//             }
808//             glEnable(type);
809//        }
810
811//        if (context.boundTextureUnit != unit) {
812//            glActiveTexture(GL_TEXTURE0 + unit);
813//            context.boundTextureUnit = unit;
814//        }
815
816        if (textures[unit] != image) {
817            glEnable(type);
818            glBindTexture(type, texId);
819            textures[unit] = image;
820
821            statistics.onTextureUse(image, true);
822        } else {
823            statistics.onTextureUse(image, false);
824        }
825
826        setupTextureParams(tex);
827    }
828
829    private void clearTextureUnits() {
830        Image[] textures = context.boundTextures;
831        if (textures[0] != null) {
832            glDisable(GL_TEXTURE_2D);
833            textures[0] = null;
834        }
835    }
836
837    public void deleteImage(Image image) {
838        int texId = image.getId();
839        if (texId != -1) {
840            ib1.put(0, texId);
841            ib1.position(0).limit(1);
842            glDeleteTextures(ib1);
843            image.resetObject();
844        }
845    }
846
847    private int convertArrayType(VertexBuffer.Type type) {
848        switch (type) {
849            case Position:
850                return GL_VERTEX_ARRAY;
851            case Normal:
852                return GL_NORMAL_ARRAY;
853            case TexCoord:
854                return GL_TEXTURE_COORD_ARRAY;
855            case Color:
856                return GL_COLOR_ARRAY;
857            default:
858                return -1; // unsupported
859        }
860    }
861
862    private int convertVertexFormat(VertexBuffer.Format fmt) {
863        switch (fmt) {
864            case Byte:
865                return GL_BYTE;
866            case Float:
867                return GL_FLOAT;
868            case Int:
869                return GL_INT;
870            case Short:
871                return GL_SHORT;
872            case UnsignedByte:
873                return GL_UNSIGNED_BYTE;
874            case UnsignedInt:
875                return GL_UNSIGNED_INT;
876            case UnsignedShort:
877                return GL_UNSIGNED_SHORT;
878            default:
879                throw new UnsupportedOperationException("Unrecognized vertex format: " + fmt);
880        }
881    }
882
883    private int convertElementMode(Mesh.Mode mode) {
884        switch (mode) {
885            case Points:
886                return GL_POINTS;
887            case Lines:
888                return GL_LINES;
889            case LineLoop:
890                return GL_LINE_LOOP;
891            case LineStrip:
892                return GL_LINE_STRIP;
893            case Triangles:
894                return GL_TRIANGLES;
895            case TriangleFan:
896                return GL_TRIANGLE_FAN;
897            case TriangleStrip:
898                return GL_TRIANGLE_STRIP;
899            default:
900                throw new UnsupportedOperationException("Unrecognized mesh mode: " + mode);
901        }
902    }
903
904    public void drawTriangleArray(Mesh.Mode mode, int count, int vertCount) {
905        if (count > 1) {
906            throw new UnsupportedOperationException();
907        }
908
909        glDrawArrays(convertElementMode(mode), 0, vertCount);
910    }
911
912    public void setVertexAttrib(VertexBuffer vb, VertexBuffer idb) {
913        int arrayType = convertArrayType(vb.getBufferType());
914        if (arrayType == -1) {
915            return; // unsupported
916        }
917        glEnableClientState(arrayType);
918        context.boundAttribs[vb.getBufferType().ordinal()] = vb;
919
920        if (vb.getBufferType() == Type.Normal) {
921            // normalize if requested
922            if (vb.isNormalized() && !context.normalizeEnabled) {
923                glEnable(GL_NORMALIZE);
924                context.normalizeEnabled = true;
925            } else if (!vb.isNormalized() && context.normalizeEnabled) {
926                glDisable(GL_NORMALIZE);
927                context.normalizeEnabled = false;
928            }
929        }
930
931        // NOTE: Use data from interleaved buffer if specified
932        Buffer data = idb != null ? idb.getData() : vb.getData();
933        int comps = vb.getNumComponents();
934        int type = convertVertexFormat(vb.getFormat());
935
936        data.rewind();
937
938        switch (vb.getBufferType()) {
939            case Position:
940                if (!(data instanceof FloatBuffer)) {
941                    throw new UnsupportedOperationException();
942                }
943
944                glVertexPointer(comps, vb.getStride(), (FloatBuffer) data);
945                break;
946            case Normal:
947                if (!(data instanceof FloatBuffer)) {
948                    throw new UnsupportedOperationException();
949                }
950
951                glNormalPointer(vb.getStride(), (FloatBuffer) data);
952                break;
953            case Color:
954                if (data instanceof FloatBuffer) {
955                    glColorPointer(comps, vb.getStride(), (FloatBuffer) data);
956                } else if (data instanceof ByteBuffer) {
957                    glColorPointer(comps, true, vb.getStride(), (ByteBuffer) data);
958                } else {
959                    throw new UnsupportedOperationException();
960                }
961                break;
962            case TexCoord:
963                if (!(data instanceof FloatBuffer)) {
964                    throw new UnsupportedOperationException();
965                }
966
967                glTexCoordPointer(comps, vb.getStride(), (FloatBuffer) data);
968                break;
969            default:
970                // Ignore, this is an unsupported attribute for OpenGL1.
971                break;
972        }
973    }
974
975    public void setVertexAttrib(VertexBuffer vb) {
976        setVertexAttrib(vb, null);
977    }
978
979    private void drawElements(int mode, int format, Buffer data) {
980        switch (format) {
981            case GL_UNSIGNED_BYTE:
982                glDrawElements(mode, (ByteBuffer) data);
983                break;
984            case GL_UNSIGNED_SHORT:
985                glDrawElements(mode, (ShortBuffer) data);
986                break;
987            case GL_UNSIGNED_INT:
988                glDrawElements(mode, (IntBuffer) data);
989                break;
990            default:
991                throw new UnsupportedOperationException();
992        }
993    }
994
995    public void drawTriangleList(VertexBuffer indexBuf, Mesh mesh, int count) {
996        Mesh.Mode mode = mesh.getMode();
997
998        Buffer indexData = indexBuf.getData();
999        indexData.rewind();
1000
1001        if (mesh.getMode() == Mode.Hybrid) {
1002            throw new UnsupportedOperationException();
1003            /*
1004            int[] modeStart = mesh.getModeStart();
1005            int[] elementLengths = mesh.getElementLengths();
1006
1007            int elMode = convertElementMode(Mode.Triangles);
1008            int fmt = convertVertexFormat(indexBuf.getFormat());
1009            //            int elSize = indexBuf.getFormat().getComponentSize();
1010            //            int listStart = modeStart[0];
1011            int stripStart = modeStart[1];
1012            int fanStart = modeStart[2];
1013            int curOffset = 0;
1014            for (int i = 0; i < elementLengths.length; i++) {
1015            if (i == stripStart) {
1016            elMode = convertElementMode(Mode.TriangleStrip);
1017            } else if (i == fanStart) {
1018            elMode = convertElementMode(Mode.TriangleStrip);
1019            }
1020            int elementLength = elementLengths[i];
1021            indexData.position(curOffset);
1022
1023            drawElements(elMode,
1024            fmt,
1025            indexData);
1026
1027            curOffset += elementLength;
1028            }*/
1029        } else {
1030            drawElements(convertElementMode(mode),
1031                    convertVertexFormat(indexBuf.getFormat()),
1032                    indexData);
1033        }
1034    }
1035
1036    public void clearVertexAttribs() {
1037        for (int i = 0; i < 16; i++) {
1038            VertexBuffer vb = context.boundAttribs[i];
1039            if (vb != null) {
1040                int arrayType = convertArrayType(vb.getBufferType());
1041                glDisableClientState(arrayType);
1042                context.boundAttribs[vb.getBufferType().ordinal()] = null;
1043            }
1044        }
1045    }
1046
1047    private void renderMeshDefault(Mesh mesh, int lod, int count) {
1048        VertexBuffer indices = null;
1049
1050        VertexBuffer interleavedData = mesh.getBuffer(Type.InterleavedData);
1051        if (interleavedData != null && interleavedData.isUpdateNeeded()) {
1052            updateBufferData(interleavedData);
1053        }
1054
1055        IntMap<VertexBuffer> buffers = mesh.getBuffers();
1056        if (mesh.getNumLodLevels() > 0) {
1057            indices = mesh.getLodLevel(lod);
1058        } else {
1059            indices = buffers.get(Type.Index.ordinal());
1060        }
1061        for (Entry<VertexBuffer> entry : buffers) {
1062            VertexBuffer vb = entry.getValue();
1063
1064            if (vb.getBufferType() == Type.InterleavedData
1065                    || vb.getUsage() == Usage.CpuOnly // ignore cpu-only buffers
1066                    || vb.getBufferType() == Type.Index) {
1067                continue;
1068            }
1069
1070            if (vb.getStride() == 0) {
1071                // not interleaved
1072                setVertexAttrib(vb);
1073            } else {
1074                // interleaved
1075                setVertexAttrib(vb, interleavedData);
1076            }
1077        }
1078
1079        if (indices != null) {
1080            drawTriangleList(indices, mesh, count);
1081        } else {
1082            glDrawArrays(convertElementMode(mesh.getMode()), 0, mesh.getVertexCount());
1083        }
1084
1085        // TODO: Fix these to use IDList??
1086        clearVertexAttribs();
1087        clearTextureUnits();
1088        resetFixedFuncBindings();
1089    }
1090
1091    public void renderMesh(Mesh mesh, int lod, int count) {
1092        if (mesh.getVertexCount() == 0) {
1093            return;
1094        }
1095
1096        if (context.pointSize != mesh.getPointSize()) {
1097            glPointSize(mesh.getPointSize());
1098            context.pointSize = mesh.getPointSize();
1099        }
1100        if (context.lineWidth != mesh.getLineWidth()) {
1101            glLineWidth(mesh.getLineWidth());
1102            context.lineWidth = mesh.getLineWidth();
1103        }
1104
1105        boolean dynamic = false;
1106        if (mesh.getBuffer(Type.InterleavedData) != null) {
1107            throw new UnsupportedOperationException("Interleaved meshes are not supported");
1108        }
1109
1110        if (mesh.getNumLodLevels() == 0) {
1111            IntMap<VertexBuffer> bufs = mesh.getBuffers();
1112            for (Entry<VertexBuffer> entry : bufs) {
1113                if (entry.getValue().getUsage() != VertexBuffer.Usage.Static) {
1114                    dynamic = true;
1115                    break;
1116                }
1117            }
1118        } else {
1119            dynamic = true;
1120        }
1121
1122        statistics.onMeshDrawn(mesh, lod);
1123
1124//        if (!dynamic) {
1125        // dealing with a static object, generate display list
1126//            renderMeshDisplayList(mesh);
1127//        } else {
1128        renderMeshDefault(mesh, lod, count);
1129//        }
1130
1131
1132    }
1133
1134    public void setAlphaToCoverage(boolean value) {
1135    }
1136
1137    public void setShader(Shader shader) {
1138    }
1139
1140    public void deleteShader(Shader shader) {
1141    }
1142
1143    public void deleteShaderSource(ShaderSource source) {
1144    }
1145
1146    public void copyFrameBuffer(FrameBuffer src, FrameBuffer dst) {
1147    }
1148
1149    public void copyFrameBuffer(FrameBuffer src, FrameBuffer dst, boolean copyDepth) {
1150    }
1151
1152    public void setMainFrameBufferOverride(FrameBuffer fb){
1153    }
1154
1155    public void setFrameBuffer(FrameBuffer fb) {
1156    }
1157
1158    public void readFrameBuffer(FrameBuffer fb, ByteBuffer byteBuf) {
1159    }
1160
1161    public void deleteFrameBuffer(FrameBuffer fb) {
1162    }
1163
1164    public void updateBufferData(VertexBuffer vb) {
1165    }
1166
1167    public void deleteBuffer(VertexBuffer vb) {
1168    }
1169}
1170