+ // *** ViewPort ***
+ glViewport(x, y, w, h);
+
+ GLMatrixMode(MatrixMode::texture);
+ if(!display.display3D.camera)
+ GLPushMatrix();
+ GLLoadIdentity();
+
+ // *** Projection Matrix ***
+ GLMatrixMode(MatrixMode::projection);
+ if(!display.display3D.camera)
+ GLPushMatrix();
+
+ if(display.display3D.collectingHits)
+ {
+ float pickX = display.display3D.pickX + surface.offset.x;
+ float pickY = display.height - (display.display3D.pickY + surface.offset.y) - 1;
+ Matrix pickMatrix
+ {
+ {
+ w / display.display3D.pickWidth, 0, 0, 0,
+ 0, h / display.display3D.pickHeight, 0, 0,
+ 0, 0, 1, 0,
+ (w + 2.0f * (x - pickX)) / display.display3D.pickWidth,
+ (h + 2.0f * (y - pickY)) / display.display3D.pickHeight, 0, 1
+ }
+ };
+ GLLoadMatrixd(pickMatrix.array);
+ }
+ else
+ GLLoadIdentity();
+ GLFrustum(
+ (left - origX) * camera.zMin / camera.focalX,
+ (right - origX) * camera.zMin / camera.focalX,
+ (bottom - origY) * camera.zMin / camera.focalY,
+ (top - origY) * camera.zMin / camera.focalY,
+ camera.zMin, camera.zMax);
+
+ glDisable(GL_BLEND);
+
+ // *** Z Inverted Identity Matrix ***
+ GLMatrixMode(MatrixMode::modelView);
+ if(!display.display3D.camera)
+ GLPushMatrix();
+
+ GLLoadIdentity();
+
+ GLScaled(1.0/nearPlane, 1.0/nearPlane, -1.0/nearPlane);
+
+ // *** View Matrix ***
+ GLMultMatrixd(camera.viewMatrix.array);
+
+#if ENABLE_GL_SHADERS
+ if(glCaps_shaders)
+ {
+ defaultShader.select();
+ defaultShader.setCamera(camera);
+ }
+#endif
+
+ // *** Lights ***
+ // ...
+
+ glEnable(GL_DEPTH_TEST);
+
+ GLSetupLighting(true);
+#if ENABLE_GL_FFP
+ if(!glCaps_shaders)
+ glShadeModel(GL_SMOOTH);
+#endif
+ glDepthMask((byte)bool::true);
+ oglDisplay.depthWrite = true;
+
+#ifndef __EMSCRIPTEN__
+ glEnable(GL_MULTISAMPLE);
+#endif
+ }
+ else if(surface && display.display3D.camera)
+ {
+ nearPlane = 1;
+ oglDisplay.depthWrite = false;
+ glViewport(0,0,display.width,display.height);
+
+ glDisable(GL_CULL_FACE);
+ glDisable(GL_DEPTH_TEST);
+
+ GLDisableClientState(COLORS);
+#if ENABLE_GL_SHADERS
+ if(glCaps_shaders)
+ {
+ GLDisableClientState(TANGENTS1);
+ GLDisableClientState(TANGENTS2);
+ }
+#endif
+ GLDisableClientState(NORMALS);
+#if ENABLE_GL_FFP
+ if(!glCaps_shaders)
+ GLDisableClientState(LIGHTVECTORS);
+#endif
+
+ // *** Restore 2D MODELVIEW Matrix ***
+ GLMatrixMode(MatrixMode::modelView);
+ GLPopMatrix();
+
+ // *** Restore 2D TEXTURE Matrix ***
+ GLMatrixMode(MatrixMode::texture);
+ GLPopMatrix();
+
+ // *** Restore 2D PROJECTION Matrix ***
+ GLMatrixMode(MatrixMode::projection);
+ GLPopMatrix();
+
+ // NOTE: We expect the 2D projection matrix to be the active one for GetSurface to call glOrtho()
+
+#if ENABLE_GL_SHADERS
+ if(glCaps_shaders)
+ defaultShader.select();
+#endif
+
+#if ENABLE_GL_FFP
+ if(!glCaps_shaders)
+ {
+ disableRemainingTMUs(display, 0);
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+
+ glDisable(GL_TEXTURE_CUBE_MAP);
+ #if _GLES
+ glDisable(GL_TEXTURE_GEN_STR);
+ #else
+ glDisable(GL_TEXTURE_GEN_R);
+ glDisable(GL_TEXTURE_GEN_S);
+ glDisable(GL_TEXTURE_GEN_T);
+ #endif
+ }
+#endif
+
+ GLSetupTexturing(false);
+ GLSetupLighting(false);
+ GLSetupFog(false);
+
+#if ENABLE_GL_SHADERS
+ if(glCaps_shaders)
+ {
+ defaultShader.setPerVertexColor(false);
+ defaultShader.setMaterial(null, 0);
+ }
+#endif
+
+#if ENABLE_GL_FFP
+ if(!glCaps_shaders)
+ glShadeModel(GL_FLAT);
+#endif
+ glEnable(GL_BLEND);
+#if !defined(__EMSCRIPTEN__)
+ glDisable(GL_MULTISAMPLE);
+#endif
+ }
+ }
+
+ void ApplyMaterial(Display display, Material material, Mesh mesh)
+ {
+ Shader shader = material.shader ? material.shader : defaultShader;
+ MaterialFlags flags = material.flags;
+#if ENABLE_GL_FFP
+ static int lastSeparate = 0;
+ int tmu = 0;
+ bool normalMapped = false;
+ OGLMesh oglMesh = mesh ? mesh.data : null;
+#endif
+
+#if ENABLE_GL_SHADERS
+ if(glCaps_shaders && shader)
+ shader.select();
+#endif
+
+ // Basic Properties
+ if(flags.doubleSided)
+ {
+#if ENABLE_GL_FFP
+ if(!glCaps_shaders)
+ GLLightModeli(GL_LIGHT_MODEL_TWO_SIDE, !flags.singleSideLight);
+#endif
+ glDisable(GL_CULL_FACE);
+ }
+ else
+ {
+#if ENABLE_GL_FFP
+ if(!glCaps_shaders)
+ GLLightModeli(GL_LIGHT_MODEL_TWO_SIDE, bool::false);
+#endif
+ glEnable(GL_CULL_FACE);
+ }
+
+ // Fog
+ GLSetupFog(!flags.noFog);
+
+#if ENABLE_GL_SHADERS
+ if(glCaps_shaders)
+ activeShader.setMaterial(material, mesh.flags);
+#endif
+
+#if ENABLE_GL_FFP
+ if(!glCaps_shaders)
+ {
+ if(material.bumpMap && mesh.lightVectors)
+ {
+ float color[4] = { 1,1,1,1 };
+ glActiveTexture(GL_TEXTURE0 + tmu);
+ glClientActiveTexture(GL_TEXTURE0 + tmu++);
+ glBindTexture(GL_TEXTURE_2D, (GLuint)(uintptr)material.bumpMap.driverData);
+ glDisable(GL_TEXTURE_CUBE_MAP);
+ glEnable(GL_TEXTURE_2D);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_DOT3_RGB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB, GL_PRIMARY_COLOR);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
+ glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_RGB, GL_TEXTURE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
+
+ if(0) //((DefaultShaderBits)defaultShader.state).debugging)
+ {
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_INTERPOLATE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB, GL_PRIMARY_COLOR);
+ glTexEnvi(GL_TEXTURE_ENV, GL_SRC2_RGB, GL_CONSTANT);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_RGB, GL_SRC_COLOR);
+ glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, color );
+ }
+
+ #if _GLES
+ glDisable(GL_TEXTURE_GEN_STR);
+ #else
+ glDisable(GL_TEXTURE_GEN_R);
+ glDisable(GL_TEXTURE_GEN_S);
+ glDisable(GL_TEXTURE_GEN_T);
+ #endif
+ glDisable(GL_LIGHTING);
+ lightingEnabled = false;
+
+ GLMatrixMode(GL_TEXTURE);
+ GLLoadIdentity();
+ if(material.uScale && material.vScale)
+ GLScalef(material.uScale, material.vScale, 1);
+ GLMatrixMode(MatrixMode::modelView);
+
+ if(flags.tile)
+ {
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+ }
+ else
+ {
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ }
+
+ glActiveTexture(GL_TEXTURE0 + tmu);
+ glClientActiveTexture(GL_TEXTURE0 + tmu);
+
+ normalMapped = true;
+
+ // Modulate base color
+ if(material.diffuse.r < 1 || material.diffuse.g < 1 || material.diffuse.b < 1)
+ {
+ tmu++;
+ glDisable(GL_TEXTURE_CUBE_MAP);
+ glEnable(GL_TEXTURE_2D);
+ glBindTexture(GL_TEXTURE_2D, (GLuint)(uintptr)material.bumpMap.driverData);
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB, GL_PREVIOUS);
+ glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_RGB, GL_CONSTANT);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_PREVIOUS);
+ glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_ALPHA, GL_CONSTANT);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA);
+
+ color[0] = material.diffuse.r, color[1] = material.diffuse.g, color[2] = material.diffuse.b, color[3] = 1.0;
+ glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, color );
+ glActiveTexture(GL_TEXTURE0 + tmu);
+ glClientActiveTexture(GL_TEXTURE0 + tmu);
+ }
+
+ // Add ambient light
+ {
+ ColorRGB ambient { material.ambient.r * 0.2f, material.ambient.g * 0.2f, material.ambient.g * 0.2f };
+ if(ambient.r > 0 || ambient.g > 0 || ambient.b > 0)
+ {
+ tmu++;
+ glDisable(GL_TEXTURE_CUBE_MAP);
+ glEnable(GL_TEXTURE_2D);
+ glBindTexture(GL_TEXTURE_2D, (GLuint)(uintptr)material.bumpMap.driverData);
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_ADD);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB, GL_PREVIOUS);
+ glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_RGB, GL_CONSTANT);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_PREVIOUS);
+ glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_ALPHA, GL_CONSTANT);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA);
+
+ color[0] = ambient.r, color[1] = ambient.g, color[2] = ambient.b, color[3] = 1.0;
+ glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, color );
+ glActiveTexture(GL_TEXTURE0 + tmu);
+ glClientActiveTexture(GL_TEXTURE0 + tmu);
+ }
+ }
+ }
+ else
+ {
+ GLDisableClientState(LIGHTVECTORS);
+ if(!lightingEnabled)
+ {
+ glEnable(GL_LIGHTING);
+ lightingEnabled = true;
+ }
+ }
+ }
+#endif
+ // Maps
+ if(flags.cubeMap || (material.baseMap && (mesh.texCoords || mesh.flags.texCoords1)))
+ {
+ Bitmap map = material.baseMap;
+ int diffuseTarget = flags.cubeMap ? GL_TEXTURE_CUBE_MAP : GL_TEXTURE_2D;
+
+#if ENABLE_GL_FFP
+ if(!glCaps_shaders)
+ {
+ glActiveTexture(GL_TEXTURE0 + tmu);
+ glClientActiveTexture(GL_TEXTURE0 + tmu++);
+ glEnable(diffuseTarget);
+ glDisable(flags.cubeMap ? GL_TEXTURE_2D : GL_TEXTURE_CUBE_MAP);
+ }
+#endif
+
+#if ENABLE_GL_SHADERS
+ if(glCaps_shaders && !flags.cubeMap)
+ GLSetupTexturing(true);
+#endif
+
+ glBindTexture(diffuseTarget, (GLuint)(uintptr)map.driverData);
+
+#if ENABLE_GL_FFP
+ if(!glCaps_shaders)
+ {
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+ /* // This did not have the desired effect with a GL_ALPHA texture
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB, GL_PREVIOUS);
+ glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_RGB, GL_TEXTURE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_PREVIOUS);
+ glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_ALPHA, GL_TEXTURE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA);
+ */
+
+ if(flags.cubeMap)
+ {
+ #if _GLES
+ glEnable(GL_TEXTURE_GEN_STR);
+ // GL_OBJECT_LINEAR: No extension support?
+ // glTexGeni(GL_TEXTURE_GEN_STR_OES, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+ #else
+ GLfloat xPlane[] = { 1.0f, 0.0f, 0.0f, 0 };
+ GLfloat yPlane[] = { 0.0f,-1.0f, 0.0f, 0 };
+ GLfloat zPlane[] = { 0.0f, 0.0f,-1.0f, 0 };
+ glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+ glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+ glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+ glTexGenfv(GL_R, GL_OBJECT_PLANE, zPlane);
+ glTexGenfv(GL_S, GL_OBJECT_PLANE, xPlane);
+ glTexGenfv(GL_T, GL_OBJECT_PLANE, yPlane);
+
+ glEnable(GL_TEXTURE_GEN_R);
+ glEnable(GL_TEXTURE_GEN_S);
+ glEnable(GL_TEXTURE_GEN_T);
+ #endif
+
+ GLDisableClientState(TEXCOORDS);
+ }
+ else
+ {
+ #if _GLES
+ glDisable(GL_TEXTURE_GEN_STR);
+ #else
+ glDisable(GL_TEXTURE_GEN_R);
+ glDisable(GL_TEXTURE_GEN_S);
+ glDisable(GL_TEXTURE_GEN_T);
+ #endif
+
+ if(tmu > 1)
+ oglMesh.texCoords.use(texCoord, 2, GL_FLOAT, 0, oglMesh.texCoords.buffer ? null : mesh.texCoords);
+ GLEnableClientState(TEXCOORDS);
+ }
+ glClientActiveTexture(GL_TEXTURE0);
+ }
+#endif
+ if(flags.tile)
+ {
+ glTexParameteri(diffuseTarget, GL_TEXTURE_WRAP_S, GL_REPEAT);
+ glTexParameteri(diffuseTarget, GL_TEXTURE_WRAP_T, GL_REPEAT);
+ }
+ else
+ {
+ glTexParameteri(diffuseTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(diffuseTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ }
+ }
+ else
+ {
+ GLSetupTexturing(false);
+ }
+
+#if ENABLE_GL_FFP && !defined(_GLES)
+ if(!glCaps_shaders)
+ {
+ int separate = material.flags.separateSpecular ? GL_SEPARATE_SPECULAR_COLOR : GL_SINGLE_COLOR;
+ if(separate != lastSeparate)
+ {
+ GLLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL, separate);
+ lastSeparate = separate;
+ }
+ }
+#endif