/** * Copyright 2014 Jason Sorensen (sorensenj@smert.net) * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * specific language governing permissions and limitations under the License. */ package net.smert.frameworkgl.examples.gl2multipassvertexlit; import java.io.IOException; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.List; import net.smert.frameworkgl.Fw; import net.smert.frameworkgl.Screen; import net.smert.frameworkgl.examples.common.DynamicMeshWorld; import net.smert.frameworkgl.examples.common.MultipleLightsOfTheSameType; import net.smert.frameworkgl.examples.common.VertexLitGuiScreen; import net.smert.frameworkgl.gameobjects.AABBGameObject; import net.smert.frameworkgl.gameobjects.GameObject; import net.smert.frameworkgl.gameobjects.SimpleOrientationAxisGameObject; import net.smert.frameworkgl.gameobjects.SkyboxGameObject; import net.smert.frameworkgl.gameobjects.ViewFrustumGameObject; import net.smert.frameworkgl.helpers.Keyboard; import net.smert.frameworkgl.math.AABB; import net.smert.frameworkgl.math.Vector3f; import net.smert.frameworkgl.math.Vector4f; import net.smert.frameworkgl.opengl.GL; import net.smert.frameworkgl.opengl.GLLight; import net.smert.frameworkgl.opengl.camera.Camera; import net.smert.frameworkgl.opengl.camera.CameraController; import net.smert.frameworkgl.opengl.camera.FrustumCullingClipSpaceSymmetrical; import net.smert.frameworkgl.opengl.constants.GetString; import net.smert.frameworkgl.opengl.shader.basic.DiffuseTextureShader; import net.smert.frameworkgl.opengl.shader.basic.SkyboxShader; import net.smert.frameworkgl.opengl.shader.vertexlit.single.AbstractDiffuseShader; import net.smert.frameworkgl.opengl.shader.vertexlit.single.BlinnPhongSpecularDirectionalShader; import net.smert.frameworkgl.opengl.shader.vertexlit.single.BlinnPhongSpecularPointShader; import net.smert.frameworkgl.opengl.shader.vertexlit.single.BlinnPhongSpecularSpotShader; import net.smert.frameworkgl.opengl.shader.vertexlit.single.DiffuseDirectionalShader; import net.smert.frameworkgl.opengl.shader.vertexlit.single.DiffusePointShader; import net.smert.frameworkgl.opengl.shader.vertexlit.single.DiffuseSpotShader; import net.smert.frameworkgl.opengl.shader.vertexlit.single.PhongSpecularDirectionalShader; import net.smert.frameworkgl.opengl.shader.vertexlit.single.PhongSpecularPointShader; import net.smert.frameworkgl.opengl.shader.vertexlit.single.PhongSpecularSpotShader; import net.smert.frameworkgl.utils.FpsTimer; import net.smert.frameworkgl.utils.MemoryUsage; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * * @author Jason Sorensen <sorensenj@smert.net> */ public class MultipassVertexLit extends Screen { private final static Logger log = LoggerFactory.getLogger(MultipassVertexLit.class); private boolean renderAabbs; private boolean renderSimpleOrientationAxis; private boolean wireframe; private AbstractDiffuseShader currentShader; private AABBGameObject aabbGameObject; private BlinnPhongSpecularDirectionalShader vertexLitSingleBlinnPhongSpecularDirectionalShader; private BlinnPhongSpecularPointShader vertexLitSingleBlinnPhongSpecularPointShader; private BlinnPhongSpecularSpotShader vertexLitSingleBlinnPhongSpecularSpotShader; private Camera camera; private CameraController cameraController; private DiffuseDirectionalShader vertexLitSingleDiffuseDirectionalShader; private DiffusePointShader vertexLitSingleDiffusePointShader; private DiffuseSpotShader vertexLitSingleDiffuseSpotShader; private DiffuseTextureShader diffuseTextureShader; private DynamicMeshWorld dynamicMeshesWorld; private FpsTimer fpsTimer; private final List<GameObject> gameObjectsToRender; private List<GLLight> currentLights; private MemoryUsage memoryUsage; private MultipleLightsOfTheSameType multipleLightsOfTheSameType; private PhongSpecularDirectionalShader vertexLitSinglePhongSpecularDirectionalShader; private PhongSpecularPointShader vertexLitSinglePhongSpecularPointShader; private PhongSpecularSpotShader vertexLitSinglePhongSpecularSpotShader; private SimpleOrientationAxisGameObject simpleOrientationAxisGameObject; private SkyboxGameObject skyboxGameObject; private SkyboxShader skyboxShader; private VertexLitGuiScreen vertexLitGuiScreen; private ViewFrustumGameObject viewFrustumGameObject; public MultipassVertexLit(String[] args) { renderAabbs = false; renderSimpleOrientationAxis = false; wireframe = false; gameObjectsToRender = new ArrayList<>(); } private void handleInput() { if (Fw.input.isKeyDown(Keyboard.ESCAPE)) { Fw.app.stopRunning(); } if (Fw.input.isKeyDown(Keyboard.F1) && !Fw.input.wasKeyDown(Keyboard.F1)) { wireframe = !wireframe; if (wireframe) { GL.o1.setPolygonModeFrontLine(); } else { GL.o1.setPolygonModeFrontFill(); } } if (Fw.input.isKeyDown(Keyboard.B) && !Fw.input.wasKeyDown(Keyboard.B)) { renderAabbs = !renderAabbs; } if (Fw.input.isKeyDown(Keyboard.O) && !Fw.input.wasKeyDown(Keyboard.O)) { renderSimpleOrientationAxis = !renderSimpleOrientationAxis; } if (Fw.input.isKeyDown(Keyboard.F)) { camera.updatePlanes(); viewFrustumGameObject.getRenderableState().setInFrustum(true); viewFrustumGameObject.getWorldTransform().getRotation().set(camera.getRotationMatrix()); viewFrustumGameObject.setWorldPosition(camera.getPosition()); viewFrustumGameObject.update(camera.getAspectRatio(), camera.getFieldOfView(), camera.getZNear(), camera.getZFar()); Fw.graphics.updateAabb(viewFrustumGameObject); Fw.graphics.performCulling(camera, dynamicMeshesWorld.getGameObjects()); updateGameObjectsToRender(); } float spotOuterCutoff = vertexLitGuiScreen.getSpotOuterCutoff(); if (Fw.input.isKeyDown(Keyboard.C)) { if ((spotOuterCutoff == 90f) && !Fw.input.wasKeyDown(Keyboard.C)) { spotOuterCutoff = 180f; } else if ((spotOuterCutoff == 180f) && !Fw.input.wasKeyDown(Keyboard.C)) { spotOuterCutoff = 0f; } else if ((spotOuterCutoff != 90f) && (spotOuterCutoff != 180f)) { spotOuterCutoff += Fw.timer.getDelta() * 3f; if (spotOuterCutoff > 180f) { spotOuterCutoff = 0f; } if (spotOuterCutoff > 90f) { spotOuterCutoff = 90f; } } for (GLLight light : multipleLightsOfTheSameType.getSpotLights()) { light.setSpotOuterCutoff(spotOuterCutoff); } } if (Fw.input.isKeyDown(Keyboard.LEFT_BRACKET) && !Fw.input.wasKeyDown(Keyboard.LEFT_BRACKET)) { vertexLitGuiScreen.decrementShaderIndex(); updateCurrentShader(); } if (Fw.input.isKeyDown(Keyboard.RIGHT_BRACKET) && !Fw.input.wasKeyDown(Keyboard.RIGHT_BRACKET)) { vertexLitGuiScreen.incrementShaderIndex(); updateCurrentShader(); } vertexLitGuiScreen.setSpotOuterCutoff(spotOuterCutoff); cameraController.update(); } private void updateCurrentLights() { switch (vertexLitGuiScreen.getShaderIndex()) { case 0: case 3: case 6: currentLights = multipleLightsOfTheSameType.getDirectionalLights(); break; case 1: case 4: case 7: currentLights = multipleLightsOfTheSameType.getPointLights(); break; case 2: case 5: case 8: currentLights = multipleLightsOfTheSameType.getSpotLights(); break; } } private void updateCurrentShader() { switch (vertexLitGuiScreen.getShaderIndex()) { case 0: currentShader = vertexLitSingleBlinnPhongSpecularDirectionalShader; break; case 1: currentShader = vertexLitSingleBlinnPhongSpecularPointShader; break; case 2: currentShader = vertexLitSingleBlinnPhongSpecularSpotShader; break; case 3: currentShader = vertexLitSingleDiffuseDirectionalShader; break; case 4: currentShader = vertexLitSingleDiffusePointShader; break; case 5: currentShader = vertexLitSingleDiffuseSpotShader; break; case 6: currentShader = vertexLitSinglePhongSpecularDirectionalShader; break; case 7: currentShader = vertexLitSinglePhongSpecularPointShader; break; case 8: currentShader = vertexLitSinglePhongSpecularSpotShader; break; } } private void updateGameObjectsToRender() { gameObjectsToRender.clear(); for (GameObject gameObject : dynamicMeshesWorld.getGameObjects()) { if (gameObject.getRenderableState().isInFrustum()) { gameObjectsToRender.add(gameObject); } } } @Override public void destroy() { for (GameObject gameObject : dynamicMeshesWorld.getGameObjects()) { gameObject.destroy(); } viewFrustumGameObject.destroy(); Fw.input.removeInputProcessor(cameraController); Fw.input.releaseMouseCursor(); } @Override public void init() { // Register assets try { Fw.files.registerAssets("/net/smert/frameworkgl/examples/assets", true); } catch (IOException | URISyntaxException ex) { throw new RuntimeException(ex); } // Switch renderer and factory to OpenGL 2 Fw.graphics.switchOpenGLVersion(2); // Create timer fpsTimer = new FpsTimer(); // Setup camera and controller camera = GL.cameraFactory.createCamera(); camera.lookAt(new Vector3f(0f, 2f, 5f), new Vector3f(0f, 2f, -1f), Vector3f.WORLD_Y_AXIS); camera.setPerspectiveProjection( 70f, (float) Fw.config.getCurrentWidth() / (float) Fw.config.getCurrentHeight(), .05f, 128f); cameraController = GL.cameraFactory.createCameraController(); cameraController.setCamera(camera); // Memory usage memoryUsage = new MemoryUsage(); // Create glLights and material light multipleLightsOfTheSameType = new MultipleLightsOfTheSameType(); multipleLightsOfTheSameType.init(); GL.uniformVariables.getDefaultMaterialLight().setShininess(16); GL.uniformVariables.getDefaultMaterialLight().setSpecular(new Vector4f(.3f, .3f, .3f, 1f)); // Load textures try { // Texture must be loaded before the renderable is created // Parameters ("skybox/violentdays", "png") will create a texture named "skybox/violentdays/cubemap.png" // from "skybox/violentdays/xpos.png", "skybox/violentdays/xneg.png", "skybox/violentdays/ypos.png" // "skybox/violentdays/yneg.png", "skybox/violentdays/zpos.png" and "skybox/violentdays/zneg.png" Fw.graphics.loadCubeMapTexture("skybox/violentdays", "png"); } catch (IOException ex) { throw new RuntimeException(ex); } // AABB game object aabbGameObject = new AABBGameObject(); aabbGameObject.getColor0().set("yellow"); aabbGameObject.init(new AABB()); // Empty AABB // Create dynamic mesh world dynamicMeshesWorld = new DynamicMeshWorld(); dynamicMeshesWorld.init(); // Simple axis game object simpleOrientationAxisGameObject = new SimpleOrientationAxisGameObject(); simpleOrientationAxisGameObject.getColor0().set("red"); simpleOrientationAxisGameObject.getColor1().set("green"); simpleOrientationAxisGameObject.getColor2().set("blue"); simpleOrientationAxisGameObject.init(); // Skybox game object skyboxGameObject = new SkyboxGameObject(); skyboxGameObject.init("skybox/violentdays/cubemap.png"); // View frustum game object viewFrustumGameObject = new ViewFrustumGameObject(); viewFrustumGameObject.getColor0().set("black"); viewFrustumGameObject.getColor1().set("yellow"); viewFrustumGameObject.getColor2().set("yellow"); viewFrustumGameObject.getColor3().set("white"); viewFrustumGameObject.getColor3().setA(.4f); viewFrustumGameObject.getRenderableState().setInFrustum(false); viewFrustumGameObject.init(camera.getAspectRatio(), camera.getFieldOfView(), camera.getZNear(), camera.getZFar()); // Frustum culling FrustumCullingClipSpaceSymmetrical frustumCulling = GL.cameraFactory.createFrustumCullingClipSpaceSymmetrical(); camera.setFrustumCulling(frustumCulling); updateGameObjectsToRender(); // Update AABBs Fw.graphics.updateAabb(dynamicMeshesWorld.getGameObjects()); // Initialize GUI Fw.gui.init(); // Create GUI screen vertexLitGuiScreen = new VertexLitGuiScreen(); vertexLitGuiScreen.setSpotOuterCutoff(180f); vertexLitGuiScreen.init(Fw.graphics.getRenderer()); Fw.gui.setScreen(vertexLitGuiScreen); // Build shaders try { diffuseTextureShader = DiffuseTextureShader.Factory.Create(); diffuseTextureShader.init(); skyboxShader = SkyboxShader.Factory.Create(); skyboxShader.init(); vertexLitSingleBlinnPhongSpecularDirectionalShader = BlinnPhongSpecularDirectionalShader.Factory.Create(); vertexLitSingleBlinnPhongSpecularDirectionalShader.init(); vertexLitSingleBlinnPhongSpecularPointShader = BlinnPhongSpecularPointShader.Factory.Create(); vertexLitSingleBlinnPhongSpecularPointShader.init(); vertexLitSingleBlinnPhongSpecularSpotShader = BlinnPhongSpecularSpotShader.Factory.Create(); vertexLitSingleBlinnPhongSpecularSpotShader.init(); vertexLitSingleDiffuseDirectionalShader = DiffuseDirectionalShader.Factory.Create(); vertexLitSingleDiffuseDirectionalShader.init(); vertexLitSingleDiffusePointShader = DiffusePointShader.Factory.Create(); vertexLitSingleDiffusePointShader.init(); vertexLitSingleDiffuseSpotShader = DiffuseSpotShader.Factory.Create(); vertexLitSingleDiffuseSpotShader.init(); vertexLitSinglePhongSpecularDirectionalShader = PhongSpecularDirectionalShader.Factory.Create(); vertexLitSinglePhongSpecularDirectionalShader.init(); vertexLitSinglePhongSpecularPointShader = PhongSpecularPointShader.Factory.Create(); vertexLitSinglePhongSpecularPointShader.init(); vertexLitSinglePhongSpecularSpotShader = PhongSpecularSpotShader.Factory.Create(); vertexLitSinglePhongSpecularSpotShader.init(); } catch (IOException ex) { throw new RuntimeException(ex); } // Set current shader updateCurrentShader(); // OpenGL settings GL.o1.setBlendingFunctionSrcAlphaAndOneMinusSrcAlpha(); GL.o1.enableCulling(); GL.o1.cullBackFaces(); GL.o1.enableDepthTest(); GL.o1.setDepthFuncLess(); GL.o1.enableDepthMask(); GL.o1.setClearDepth(1f); GL.o1.clear(); log.info("OpenGL version: " + GL.o1.getString(GetString.VERSION)); // Add camera controller to input Fw.input.addInputProcessor(cameraController); Fw.input.grabMouseCursor(); } @Override public void pause() { } @Override public void render() { fpsTimer.update(); memoryUsage.update(); if (Fw.timer.isGameTick()) { // Do nothing } if (Fw.timer.isRenderTick()) { handleInput(); // Clear screen GL.o1.clear(); // Update camera Fw.graphics.setCamera(camera); // Update current lights updateCurrentLights(); // Bind shader Fw.graphics.switchShader(skyboxShader); // Render skybox Fw.graphics.color(1f, 1f, 1f, 1f); skyboxGameObject.getWorldTransform().setPosition(camera.getPosition()); GL.o1.disableCulling(); GL.o1.disableDepthTest(); Fw.graphics.render(skyboxGameObject); GL.o1.enableDepthTest(); GL.o1.enableCulling(); // Unbind shader Fw.graphics.unbindShader(); // Bind shader Fw.graphics.switchShader(currentShader); // Enable blending GL.o1.enableBlending(); // Loop through all lights boolean firstPass = true; for (GLLight light : currentLights) { if (firstPass) { GL.uniformVariables.getAmbientLight().reset(); GL.o1.setBlendingFunctionOneAndZero(); GL.o1.setDepthFuncLess(); GL.o1.enableDepthMask(); firstPass = false; } else { // Ambient only enabled on the first pass GL.uniformVariables.getAmbientLight().setAmbient(new Vector4f()); GL.o1.setBlendingFunctionOneAndOne(); GL.o1.setDepthFuncEqual(); GL.o1.disableDepthMask(); } // Update shader uniforms currentShader.getUniforms().setLight(light); // Render directly Fw.graphics.render(gameObjectsToRender); } // Disable blending, then restore blending function, depth test function and mask GL.o1.disableBlending(); GL.o1.setBlendingFunctionSrcAlphaAndOneMinusSrcAlpha(); GL.o1.setDepthFuncLess(); GL.o1.enableDepthMask(); // Unbind shader Fw.graphics.unbindShader(); // Bind diffuse texture shader (no lighting) Fw.graphics.switchShader(diffuseTextureShader); // View frustum if (viewFrustumGameObject.getRenderableState().isInFrustum()) { Fw.graphics.renderBlend(viewFrustumGameObject); } // AABBs if (renderAabbs) { for (GameObject gameObject : gameObjectsToRender) { AABB worldAabb = gameObject.getWorldAabb(); // Updating AABBs this way is costly aabbGameObject.update(worldAabb); // AABB is already in world coordinates so we don't translate Fw.graphics.render(aabbGameObject.getRenderable(), 0f, 0f, 0f); } } // Orientation axis if (renderSimpleOrientationAxis) { GL.o1.disableDepthTest(); for (GameObject gameObject : gameObjectsToRender) { simpleOrientationAxisGameObject.setWorldTransform(gameObject.getWorldTransform()); Fw.graphics.render(simpleOrientationAxisGameObject); } GL.o1.enableDepthTest(); } // Render 2D GL.o1.enableBlending(); GL.o1.disableDepthTest(); Fw.graphics.set2DMode(); Fw.gui.update(); Fw.gui.render(); GL.o1.enableDepthTest(); GL.o1.disableBlending(); // Unbind shader Fw.graphics.unbindShader(); } } @Override public void resize(int width, int height) { GL.o1.setViewport(0, 0, width, height); } @Override public void resume() { } }