From 0372a5ed6d6dfe6c4e44abbbe75c37669a061318 Mon Sep 17 00:00:00 2001 From: "sha..rd" Date: Thu, 13 Oct 2011 04:04:33 +0000 Subject: [PATCH] * Add lighting support to OpenGL1 renderer git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@8387 75d07b2b-3a1a-0410-a2c5-0572b91ccdca --- .../com/jme3/material/FixedFuncBinding.java | 9 +- .../core/com/jme3/renderer/RenderContext.java | 35 ++ .../jme3/renderer/lwjgl/LwjglGL1Renderer.java | 334 ++++++++++++++---- 3 files changed, 313 insertions(+), 65 deletions(-) diff --git a/engine/src/core/com/jme3/material/FixedFuncBinding.java b/engine/src/core/com/jme3/material/FixedFuncBinding.java index 1f6ba1181..e316ad803 100644 --- a/engine/src/core/com/jme3/material/FixedFuncBinding.java +++ b/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 } diff --git a/engine/src/core/com/jme3/renderer/RenderContext.java b/engine/src/core/com/jme3/renderer/RenderContext.java index 426a405a9..ee9ff9c0f 100644 --- a/engine/src/core/com/jme3/renderer/RenderContext.java +++ b/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; } } diff --git a/engine/src/lwjgl-ogl/com/jme3/renderer/lwjgl/LwjglGL1Renderer.java b/engine/src/lwjgl-ogl/com/jme3/renderer/lwjgl/LwjglGL1Renderer.java index d7116406c..08522dc9a 100644 --- a/engine/src/lwjgl-ogl/com/jme3/renderer/lwjgl/LwjglGL1Renderer.java +++ b/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 = 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 lightList = new ArrayList(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");