Browse Source

* Add lighting support to OpenGL1 renderer

git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@8387 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
sha..rd 14 years ago
parent
commit
0372a5ed6d

+ 8 - 1
engine/src/core/com/jme3/material/FixedFuncBinding.java

@@ -69,5 +69,12 @@ public enum FixedFuncBinding {
      * 
      * Same as GL_SHININESS for OpenGL.
      */
-    MaterialShininess
+    MaterialShininess,
+    
+    /**
+     * Use vertex color as an additional diffuse color, if lighting is enabled.
+     * If lighting is disabled, vertex color is modulated with
+     * {@link #Color material color}.
+     */
+    UseVertexColor
 }

+ 35 - 0
engine/src/core/com/jme3/renderer/RenderContext.java

@@ -33,6 +33,7 @@
 package com.jme3.renderer;
 
 import com.jme3.material.RenderState;
+import com.jme3.math.ColorRGBA;
 import com.jme3.scene.Mesh;
 import com.jme3.scene.VertexBuffer;
 import com.jme3.texture.FrameBuffer;
@@ -233,6 +234,36 @@ public class RenderContext {
      * IDList for vertex attributes
      */
     public IDList attribIndexList = new IDList();
+    
+    /**
+     * Ambient color (GL1 only)
+     */
+    public ColorRGBA ambient;
+    
+    /**
+     * Diffuse color (GL1 only)
+     */
+    public ColorRGBA diffuse;
+    
+    /**
+     * Specular color (GL1 only)
+     */
+    public ColorRGBA specular;
+    
+    /**
+     * Material color (GL1 only)
+     */
+    public ColorRGBA color;
+    
+    /**
+     * Shininess (GL1 only)
+     */
+    public float shininess;
+    
+    /**
+     * Use vertex color (GL1 only)
+     */
+    public boolean useVertexColor;
 
     /**
      * Reset the RenderContext to default GL state
@@ -280,5 +311,9 @@ public class RenderContext {
         backStencilDepthPassOperation = RenderState.StencilOperation.Keep;
         frontStencilFunction = RenderState.TestFunction.Always;
         backStencilFunction = RenderState.TestFunction.Always;
+        
+        ambient = diffuse = specular = color = null;
+        shininess = 0;
+        useVertexColor = false;
     }
 }

+ 270 - 64
engine/src/lwjgl-ogl/com/jme3/renderer/lwjgl/LwjglGL1Renderer.java

@@ -1,5 +1,11 @@
 package com.jme3.renderer.lwjgl;
 
+import com.jme3.light.SpotLight;
+import java.util.ArrayList;
+import com.jme3.light.PointLight;
+import com.jme3.math.Vector3f;
+import com.jme3.light.DirectionalLight;
+import com.jme3.light.Light;
 import org.lwjgl.opengl.GL14;
 import com.jme3.math.FastMath;
 import com.jme3.renderer.GL1Renderer;
@@ -48,6 +54,7 @@ public class LwjglGL1Renderer implements GL1Renderer {
     private final IntBuffer ib1 = BufferUtils.createIntBuffer(1);
     private final IntBuffer intBuf16 = BufferUtils.createIntBuffer(16);
     private final FloatBuffer fb16 = BufferUtils.createFloatBuffer(16);
+    private final FloatBuffer fb4Null = BufferUtils.createFloatBuffer(4);
     private final RenderContext context = new RenderContext();
     private final GLObjectManager objManager = new GLObjectManager();
     private final EnumSet<Caps> caps = EnumSet.noneOf(Caps.class);
@@ -55,15 +62,18 @@ public class LwjglGL1Renderer implements GL1Renderer {
     private int maxCubeTexSize;
     private int maxVertCount;
     private int maxTriCount;
+    private int maxLights;
     private boolean gl12 = false;
     private final Statistics statistics = new Statistics();
     private int vpX, vpY, vpW, vpH;
     private int clipX, clipY, clipW, clipH;
-//    private Matrix4f worldMatrix = new Matrix4f();
+    
+    private Matrix4f worldMatrix = new Matrix4f();
     private Matrix4f viewMatrix = new Matrix4f();
-//    private Matrix4f projMatrix = new Matrix4f();
-    private boolean colorSet = false;
-    private boolean materialSet = false;
+    
+    private ArrayList<Light> lightList = new ArrayList<Light>(8);
+    private ColorRGBA materialAmbientColor = new ColorRGBA();
+    private Vector3f tempVec = new Vector3f();
 
     protected void updateNameBuffer() {
         int len = stringBuf.length();
@@ -86,9 +96,22 @@ public class LwjglGL1Renderer implements GL1Renderer {
     }
 
     public void initialize() {
-        //glDisable(GL_DEPTH_TEST);
+        if (GLContext.getCapabilities().OpenGL12){
+            gl12 = true;
+        }
+        
+        // Default values for certain GL state.
         glShadeModel(GL_SMOOTH);
+        glColorMaterial(GL_FRONT_AND_BACK, GL_DIFFUSE);
         glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
+        
+		// Enable rescaling/normaling of normal vectors.
+		// Fixes lighting issues with scaled models.
+        if (gl12){
+            glEnable(GL12.GL_RESCALE_NORMAL);
+        }else{
+            glEnable(GL_NORMALIZE);
+        }
 
         if (GLContext.getCapabilities().GL_ARB_texture_non_power_of_two) {
             caps.add(Caps.NonPowerOfTwoTextures);
@@ -98,26 +121,26 @@ public class LwjglGL1Renderer implements GL1Renderer {
                     + "Some features might not work.");
         }
         
-        if (GLContext.getCapabilities().OpenGL12){
-            gl12 = true;
-        }
+        maxLights = glGetInteger(GL_MAX_LIGHTS);
+        
     }
-
+    
     public void invalidateState() {
         context.reset();
     }
 
     public void resetGLObjects() {
-        colorSet = false;
-
+        logger.log(Level.INFO, "Reseting objects and invalidating state");
         objManager.resetObjects();
         statistics.clearMemory();
-        context.reset();
+        invalidateState();
     }
 
     public void cleanup() {
+        logger.log(Level.INFO, "Deleting objects and invalidating state");
         objManager.deleteAllObjects(this);
         statistics.clearMemory();
+        invalidateState();
     }
 
     public void setDepthRange(float start, float end) {
@@ -127,9 +150,23 @@ public class LwjglGL1Renderer implements GL1Renderer {
     public void clearBuffers(boolean color, boolean depth, boolean stencil) {
         int bits = 0;
         if (color) {
+            //See explanations of the depth below, we must enable color write to be able to clear the color buffer
+            if (context.colorWriteEnabled == false) {
+                glColorMask(true, true, true, true);
+                context.colorWriteEnabled = true;
+            }
             bits = GL_COLOR_BUFFER_BIT;
         }
         if (depth) {
+
+            //glClear(GL_DEPTH_BUFFER_BIT) seems to not work when glDepthMask is false
+            //here s some link on openl board
+            //http://www.opengl.org/discussion_boards/ubbthreads.php?ubb=showflat&Number=257223
+            //if depth clear is requested, we enable the depthMask
+            if (context.depthWriteEnabled == false) {
+                glDepthMask(true);
+                context.depthWriteEnabled = true;
+            }
             bits |= GL_DEPTH_BUFFER_BIT;
         }
         if (stencil) {
@@ -144,65 +181,77 @@ public class LwjglGL1Renderer implements GL1Renderer {
         glClearColor(color.r, color.g, color.b, color.a);
     }
 
-    private void setMaterialColor(int type, ColorRGBA color) {
-        if (!materialSet) {
-            materialSet = true;
-            glEnable(GL_COLOR_MATERIAL);
+    private void setMaterialColor(int type, ColorRGBA color, ColorRGBA defaultColor) {
+        if (color != null){
+            fb16.put(color.r).put(color.g).put(color.b).put(color.a).flip();
+        }else{
+            fb16.put(defaultColor.r).put(defaultColor.g).put(defaultColor.b).put(defaultColor.a).flip();
         }
-
-        fb16.clear();
-        fb16.put(color.r).put(color.g).put(color.b).put(color.a);
-        fb16.clear();
         glMaterial(GL_FRONT_AND_BACK, type, fb16);
     }
-
-    private void setMaterialFloat(int type, float value){
-        if (!materialSet) {
-            materialSet = true;
-            glEnable(GL_COLOR_MATERIAL);
+    
+    /**
+     * Applies fixed function bindings from the context to OpenGL
+     */
+    private void applyFixedFuncBindings(boolean forLighting){
+        if (forLighting){
+            glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, context.shininess);
+            setMaterialColor(GL_AMBIENT,  context.ambient,  ColorRGBA.DarkGray);
+            setMaterialColor(GL_DIFFUSE,  context.diffuse,  ColorRGBA.White);
+            setMaterialColor(GL_SPECULAR, context.specular, ColorRGBA.Black);
+            
+            if (context.useVertexColor){
+                glEnable(GL_COLOR_MATERIAL);
+            }else{
+                glDisable(GL_COLOR_MATERIAL);
+            }
+        }else{
+            // Ignore other values as they have no effect when 
+            // GL_LIGHTING is disabled.
+            ColorRGBA color = context.color;
+            if (color != null){
+                glColor4f(color.r, color.g, color.b, color.a);
+            }else{
+                glColor4f(1,1,1,1);
+            }
         }
-
-        glMaterialf(GL_FRONT_AND_BACK, type, value);
+    }
+    
+    /**
+     * Reset fixed function bindings to default values.
+     */
+    private void resetFixedFuncBindings(){
+        context.color = null;
+        context.ambient = null;
+        context.diffuse = null;
+        context.specular = null;
+        context.shininess = 0;
+        context.useVertexColor = false;
     }
     
     public void setFixedFuncBinding(FixedFuncBinding ffBinding, Object val) {
         switch (ffBinding) {
             case Color:
-                ColorRGBA color = (ColorRGBA) val;
-                glColor4f(color.r, color.g, color.b, color.a);
-                colorSet = true;
+                context.color = (ColorRGBA) val;
                 break;
             case MaterialAmbient:
-                ColorRGBA ambient = (ColorRGBA) val;
-                setMaterialColor(GL_AMBIENT, ambient);
+                context.ambient = (ColorRGBA) val;
                 break;
             case MaterialDiffuse:
-                ColorRGBA diffuse = (ColorRGBA) val;
-                setMaterialColor(GL_DIFFUSE, diffuse);
+                context.diffuse = (ColorRGBA) val;
                 break;
             case MaterialSpecular:
-                ColorRGBA specular = (ColorRGBA) val;
-                setMaterialColor(GL_SPECULAR, specular);
+                context.specular = (ColorRGBA) val;
                 break;
             case MaterialShininess:
-                float shiny = (Float) val;
-                setMaterialFloat(GL_SPECULAR, shiny);
+                context.shininess = (Float) val;
+                break;
+            case UseVertexColor:
+                context.useVertexColor = (Boolean) val;
                 break;
-                
-        }
-    }
-
-    public void clearSetFixedFuncBindings() {
-        if (colorSet) {
-            glColor4f(1, 1, 1, 1);
-            colorSet = false;
-        }
-        if (materialSet) {
-            glDisable(GL_COLOR_MATERIAL);
-            materialSet = false; // TODO: not efficient
         }
     }
-
+    
     public void applyRenderState(RenderState state) {
         if (state.isWireframe() && !context.wireframe) {
             glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
@@ -393,37 +442,194 @@ public class LwjglGL1Renderer implements GL1Renderer {
         store.clear();
         return store;
     }
-
-    public void setWorldMatrix(Matrix4f worldMatrix) {
+    
+    private void setModelView(Matrix4f modelMatrix, Matrix4f viewMatrix){
         if (context.matrixMode != GL_MODELVIEW) {
             glMatrixMode(GL_MODELVIEW);
             context.matrixMode = GL_MODELVIEW;
         }
 
         glLoadMatrix(storeMatrix(viewMatrix, fb16));
-        glMultMatrix(storeMatrix(worldMatrix, fb16));
+        glMultMatrix(storeMatrix(modelMatrix, fb16));
     }
-
-    public void setViewProjectionMatrices(Matrix4f viewMatrix, Matrix4f projMatrix) {
+    
+    private void setProjection(Matrix4f projMatrix){
         if (context.matrixMode != GL_PROJECTION) {
             glMatrixMode(GL_PROJECTION);
             context.matrixMode = GL_PROJECTION;
         }
 
-        storeMatrix(projMatrix, fb16);
-        glLoadMatrix(fb16);
+        glLoadMatrix(storeMatrix(projMatrix, fb16));
+    }
 
+    public void setWorldMatrix(Matrix4f worldMatrix) {
+        this.worldMatrix.set(worldMatrix);
+    }
+
+    public void setViewProjectionMatrices(Matrix4f viewMatrix, Matrix4f projMatrix) {
         this.viewMatrix.set(viewMatrix);
+        setProjection(projMatrix);
     }
 
     public void setLighting(LightList list) {
-        if (list == null || list.size() == 0) {
-            // turn off lighting
-            //glDisable(GL_LIGHTING);
+        // XXX: This is abuse of setLighting() to
+		// apply fixed function bindings
+        // and do other book keeping.
+        if (list == null || list.size() == 0){
+            glDisable(GL_LIGHTING);
+            applyFixedFuncBindings(false);
+            setModelView(worldMatrix, viewMatrix);
             return;
         }
+        
+        // Number of lights set previously
+        int numLightsSetPrev = lightList.size();
+        
+        // If more than maxLights are defined, they will be ignored.
+        // The GL1 renderer is not permitted to crash due to a 
+        // GL1 limitation. It must render anything that the GL2 renderer
+        // can render (even incorrectly).
+        lightList.clear();
+        materialAmbientColor.set(0, 0, 0, 0);
+        
+        for (int i = 0; i < list.size(); i++){
+            Light l = list.get(i);
+            if (l.getType() == Light.Type.Ambient){
+                // Gather
+                materialAmbientColor.addLocal(l.getColor());
+            }else{
+                // Add to list
+                lightList.add(l);
+                
+                // Once maximum lights reached, exit loop.
+                if (lightList.size() >= maxLights){
+                    break;
+                }
+            }
+        }
+        
+        applyFixedFuncBindings(true);
+        
+        glEnable(GL_LIGHTING);
+        
+        fb16.clear();
+        fb16.put(materialAmbientColor.r)
+            .put(materialAmbientColor.g)
+            .put(materialAmbientColor.b)
+            .put(1).flip();
+        
+        glLightModel(GL_LIGHT_MODEL_AMBIENT, fb16);
+        
+        if (context.matrixMode != GL_MODELVIEW) {
+            glMatrixMode(GL_MODELVIEW);
+            context.matrixMode = GL_MODELVIEW;
+        }
+        // Lights are already in world space, so just convert
+        // them to view space.
+        glLoadMatrix(storeMatrix(viewMatrix, fb16));
+        
+        for (int i = 0; i < lightList.size(); i++){
+            int glLightIndex = GL_LIGHT0 + i;
+            Light light = lightList.get(i);
+            Light.Type lightType = light.getType();
+            ColorRGBA col = light.getColor();
+            Vector3f pos;
+            
+            // Enable the light
+            glEnable(glLightIndex);
+            
+            // OGL spec states default value for light ambient is black
+            switch (lightType){
+                case Directional:
+                    DirectionalLight dLight = (DirectionalLight) light;
+
+                    fb16.clear();
+                    fb16.put(col.r).put(col.g).put(col.b).put(col.a).flip();
+                    glLight(glLightIndex, GL_DIFFUSE, fb16);
+                    glLight(glLightIndex, GL_SPECULAR, fb16);
+
+                    pos = tempVec.set(dLight.getDirection()).negateLocal().normalizeLocal();
+                    fb16.clear();
+                    fb16.put(pos.x).put(pos.y).put(pos.z).put(0.0f).flip();
+                    glLight(glLightIndex, GL_POSITION, fb16);
+                    glLightf(glLightIndex, GL_SPOT_CUTOFF, 180);
+                    break;
+                case Point:
+                    PointLight pLight = (PointLight) light;
+      
+                    fb16.clear();
+                    fb16.put(col.r).put(col.g).put(col.b).put(col.a).flip();
+                    glLight(glLightIndex, GL_DIFFUSE, fb16);
+                    glLight(glLightIndex, GL_SPECULAR, fb16);
+
+                    pos = pLight.getPosition();
+                    fb16.clear();
+                    fb16.put(pos.x).put(pos.y).put(pos.z).put(1.0f).flip();
+                    glLight(glLightIndex, GL_POSITION, fb16);
+                    glLightf(glLightIndex, GL_SPOT_CUTOFF, 180);
+
+                    if (pLight.getRadius() > 0) {
+                        // Note: this doesn't follow the same attenuation model
+                        // as the one used in the lighting shader.
+                        glLightf(glLightIndex, GL_CONSTANT_ATTENUATION,  1);
+                        glLightf(glLightIndex, GL_LINEAR_ATTENUATION,    pLight.getInvRadius() * 2);
+                        glLightf(glLightIndex, GL_QUADRATIC_ATTENUATION, pLight.getInvRadius() * pLight.getInvRadius()); 
+                    }else{
+                        glLightf(glLightIndex, GL_CONSTANT_ATTENUATION,  1);
+                        glLightf(glLightIndex, GL_LINEAR_ATTENUATION,    0);
+                        glLightf(glLightIndex, GL_QUADRATIC_ATTENUATION, 0);
+                    }
 
-        //glEnable(GL_LIGHTING);
+                    break;
+                case Spot:
+                    SpotLight sLight = (SpotLight) light;
+
+                    fb16.clear();
+                    fb16.put(col.r).put(col.g).put(col.b).put(col.a).flip();
+                    glLight(glLightIndex, GL_DIFFUSE, fb16);
+                    glLight(glLightIndex, GL_SPECULAR, fb16);
+
+                    pos = sLight.getPosition();
+                    fb16.clear();
+                    fb16.put(pos.x).put(pos.y).put(pos.z).put(1.0f).flip();
+                    glLight(glLightIndex, GL_POSITION, fb16);
+
+                    Vector3f dir = sLight.getDirection();
+                    fb16.clear();
+                    fb16.put(dir.x).put(dir.y).put(dir.z).put(1.0f).flip();
+                    glLight(glLightIndex, GL_SPOT_DIRECTION, fb16);
+
+                    float outerAngleRad = sLight.getSpotOuterAngle();
+                    float innerAngleRad = sLight.getSpotInnerAngle();
+                    float spotCut = outerAngleRad * FastMath.RAD_TO_DEG;
+                    float spotExpo = 0.0f;
+                    if (outerAngleRad > 0) {
+                        spotExpo = (1.0f - (innerAngleRad / outerAngleRad)) * 128.0f;
+                    }
+
+                    glLightf(glLightIndex, GL_SPOT_CUTOFF, spotCut);
+                    glLightf(glLightIndex, GL_SPOT_EXPONENT, spotExpo);
+
+                    if (sLight.getSpotRange() > 0) {
+                        glLightf(glLightIndex, GL_LINEAR_ATTENUATION, sLight.getInvSpotRange());
+                    }else{
+                        glLightf(glLightIndex, GL_LINEAR_ATTENUATION, 0);
+                    }
+
+                    break;
+                default:
+                    throw new UnsupportedOperationException(
+                            "Unrecognized light type: " + lightType);
+            }
+        }
+        
+        // Disable lights after the index
+        for (int i = lightList.size(); i < numLightsSetPrev; i++){
+            glDisable(GL_LIGHT0 + i);
+        }
+        
+        // This will set view matrix as well.
+        setModelView(worldMatrix, viewMatrix);
     }
 
     private int convertTextureType(Texture.Type type) {
@@ -889,7 +1095,7 @@ public class LwjglGL1Renderer implements GL1Renderer {
         // TODO: Fix these to use IDList??
         clearVertexAttribs();
         clearTextureUnits();
-        clearSetFixedFuncBindings();
+        resetFixedFuncBindings();
     }
 
     public void renderMesh(Mesh mesh, int lod, int count) {
@@ -905,7 +1111,7 @@ public class LwjglGL1Renderer implements GL1Renderer {
             glLineWidth(mesh.getLineWidth());
             context.lineWidth = mesh.getLineWidth();
         }
-
+        
         boolean dynamic = false;
         if (mesh.getBuffer(Type.InterleavedData) != null) {
             throw new UnsupportedOperationException("Interleaved meshes are not supported");