diff --git a/engine/src/main/java/org/terasology/rendering/dag/BackdropNode.java b/engine/src/main/java/org/terasology/rendering/dag/BackdropNode.java new file mode 100644 index 00000000000..b8294886604 --- /dev/null +++ b/engine/src/main/java/org/terasology/rendering/dag/BackdropNode.java @@ -0,0 +1,104 @@ +/* + * Copyright 2016 MovingBlocks + * + * 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 org.terasology.rendering.dag; + +import org.terasology.config.Config; +import org.terasology.config.RenderingDebugConfig; +import org.terasology.monitoring.PerformanceMonitor; +import org.terasology.registry.In; +import org.terasology.rendering.backdrop.BackdropRenderer; +import org.terasology.rendering.cameras.Camera; +import org.terasology.rendering.opengl.FBO; +import org.terasology.rendering.opengl.FrameBuffersManager; +import org.terasology.rendering.world.WorldRenderer; +import static org.terasology.rendering.opengl.OpenGLUtils.bindDisplay; +import static org.terasology.rendering.opengl.OpenGLUtils.disableWireframeIf; +import static org.terasology.rendering.opengl.OpenGLUtils.enableWireframeIf; +import static org.terasology.rendering.opengl.OpenGLUtils.setRenderBufferMask; +import static org.lwjgl.opengl.GL11.GL_COLOR_BUFFER_BIT; +import static org.lwjgl.opengl.GL11.GL_DEPTH_BUFFER_BIT; +import static org.lwjgl.opengl.GL11.GL_STENCIL_BUFFER_BIT; +import static org.lwjgl.opengl.GL11.glClear; + + +/** + * TODO: Diagram of this node + */ +public class BackdropNode implements Node { + + @In + private Config config; + + @In + private BackdropRenderer backdropRenderer; + + @In + private WorldRenderer worldRenderer; + + @In + private FrameBuffersManager frameBuffersManager; + + private RenderingDebugConfig renderingDebugConfig; + private Camera playerCamera; + private FBO sceneOpaque; + + @Override + public void initialise() { + renderingDebugConfig = config.getRendering().getDebug(); + playerCamera = worldRenderer.getActiveCamera(); + } + + @Override + public void process() { + PerformanceMonitor.startActivity("rendering/backdrop"); + enableWireframeIf(renderingDebugConfig.isWireframe()); + + sceneOpaque = frameBuffersManager.getFBO("sceneOpaque"); + + initialClearing(); + + sceneOpaque.bind(); + setRenderBufferMask(sceneOpaque, true, true, true); + + playerCamera.lookThroughNormalized(); + /** + * Sets the state to render the Backdrop. At this stage the backdrop is the SkySphere + * plus the SkyBands passes. + * + * The backdrop is the only rendering that has three state-changing methods. + * This is due to the SkySphere requiring a state and the SkyBands requiring a slightly + * different one. + */ + setRenderBufferMask(sceneOpaque, true, false, false); + backdropRenderer.render(playerCamera); + + disableWireframeIf(renderingDebugConfig.isWireframe()); + PerformanceMonitor.endActivity(); + } + + /** + * Initial clearing of a couple of important Frame Buffers. Then binds back the Display. + */ + // It's unclear why these buffers need to be cleared while all the others don't... + private void initialClearing() { + FBO sceneReflectiveRefractive = frameBuffersManager.getFBO("sceneReflectiveRefractive"); + sceneOpaque.bind(); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + sceneReflectiveRefractive.bind(); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + bindDisplay(); + } +} diff --git a/engine/src/main/java/org/terasology/rendering/dag/ShadowMapNode.java b/engine/src/main/java/org/terasology/rendering/dag/ShadowMapNode.java index b2312dd3b6b..48d59f405cd 100644 --- a/engine/src/main/java/org/terasology/rendering/dag/ShadowMapNode.java +++ b/engine/src/main/java/org/terasology/rendering/dag/ShadowMapNode.java @@ -15,7 +15,7 @@ */ package org.terasology.rendering.dag; -import org.lwjgl.opengl.GL11; + import org.terasology.config.Config; import org.terasology.config.RenderingConfig; import org.terasology.math.TeraMath; @@ -33,8 +33,13 @@ import org.terasology.rendering.world.RenderableWorld; import org.terasology.rendering.world.WorldRenderer; import org.terasology.rendering.world.WorldRendererImpl; -import static org.lwjgl.opengl.GL11.*; -import static org.terasology.rendering.opengl.OpenGLUtils.*; +import org.lwjgl.opengl.GL11; +import static org.lwjgl.opengl.GL11.GL_COLOR_BUFFER_BIT; +import static org.lwjgl.opengl.GL11.GL_CULL_FACE; +import static org.lwjgl.opengl.GL11.GL_DEPTH_BUFFER_BIT; +import static org.lwjgl.opengl.GL11.glClear; +import static org.terasology.rendering.opengl.OpenGLUtils.bindDisplay; +import static org.terasology.rendering.opengl.OpenGLUtils.setViewportToSizeOf; /** @@ -69,7 +74,6 @@ public class ShadowMapNode implements Node { private RenderingConfig renderingConfig; private Camera playerCamera; - @Override public void initialise() { this.playerCamera = worldRenderer.getActiveCamera(); @@ -81,7 +85,7 @@ public void initialise() { @Override public void process() { - this.shadowMap = frameBuffersManager.getFBO("sceneShadowMap"); + shadowMap = frameBuffersManager.getFBO("sceneShadowMap"); positionShadowMapCamera(); // TODO: find an elegant way to fetch isFirstRenderingStageForCurrentFrame @@ -90,7 +94,7 @@ public void process() { return; } - PerformanceMonitor.startActivity("Render World (Shadow Map)"); + PerformanceMonitor.startActivity("rendering/shadowmap"); // preRenderSetupSceneShadowMap shadowMap.bind(); setViewportToSizeOf(shadowMap); diff --git a/engine/src/main/java/org/terasology/rendering/dag/SkyBandsNode.java b/engine/src/main/java/org/terasology/rendering/dag/SkyBandsNode.java new file mode 100644 index 00000000000..7d2e7adad86 --- /dev/null +++ b/engine/src/main/java/org/terasology/rendering/dag/SkyBandsNode.java @@ -0,0 +1,114 @@ +/* + * Copyright 2016 MovingBlocks + * + * 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 org.terasology.rendering.dag; + +import org.terasology.config.Config; +import org.terasology.config.RenderingConfig; +import org.terasology.config.RenderingDebugConfig; +import org.terasology.monitoring.PerformanceMonitor; +import org.terasology.registry.In; +import org.terasology.rendering.assets.material.Material; +import org.terasology.rendering.cameras.Camera; +import org.terasology.rendering.opengl.FBO; +import org.terasology.rendering.opengl.FrameBuffersManager; +import org.terasology.rendering.world.WorldRenderer; +import static org.terasology.rendering.opengl.OpenGLUtils.bindDisplay; +import static org.terasology.rendering.opengl.OpenGLUtils.setViewportToSizeOf; +import static org.terasology.rendering.opengl.OpenGLUtils.disableWireframeIf; +import static org.terasology.rendering.opengl.OpenGLUtils.enableWireframeIf; +import static org.terasology.rendering.opengl.OpenGLUtils.renderFullscreenQuad; +import static org.terasology.rendering.opengl.OpenGLUtils.setRenderBufferMask; +import static org.lwjgl.opengl.GL11.GL_COLOR_BUFFER_BIT; +import static org.lwjgl.opengl.GL11.GL_DEPTH_BUFFER_BIT; +import static org.lwjgl.opengl.GL11.glClear; + + +/** + * TODO: Diagram of this node + * TODO: Separate this node into multiple SkyBandNode's + */ +public class SkyBandsNode implements Node { + + @In + private Config config; + + @In + private WorldRenderer worldRenderer; + + @In + private FrameBuffersManager frameBuffersManager; + + private RenderingConfig renderingConfig; + private Material blurShader; + private FBO sceneOpaque; + private FBO sceneSkyBand0; + private RenderingDebugConfig renderingDebugConfig; + private Camera playerCamera; + + @Override + public void initialise() { + renderingConfig = config.getRendering(); + renderingDebugConfig = renderingConfig.getDebug(); + blurShader = worldRenderer.getMaterial("engine:prog.blur"); + playerCamera = worldRenderer.getActiveCamera(); + } + + @Override + public void process() { + PerformanceMonitor.startActivity("rendering/skybands"); + enableWireframeIf(renderingDebugConfig.isWireframe()); + + sceneOpaque = frameBuffersManager.getFBO("sceneOpaque"); + + setRenderBufferMask(sceneOpaque, true, true, true); + if (renderingConfig.isInscattering()) { + sceneSkyBand0 = frameBuffersManager.getFBO("sceneSkyBand0"); + FBO sceneSkyBand1 = frameBuffersManager.getFBO("sceneSkyBand1"); + + generateSkyBand(sceneSkyBand0); + generateSkyBand(sceneSkyBand1); + } + + sceneOpaque.bind(); + + playerCamera.lookThrough(); + disableWireframeIf(renderingDebugConfig.isWireframe()); + PerformanceMonitor.endActivity(); + } + + private void generateSkyBand(FBO skyBand) { + blurShader.enable(); + blurShader.setFloat("radius", 8.0f, true); + blurShader.setFloat2("texelSize", 1.0f / skyBand.width(), 1.0f / skyBand.height(), true); + + if (skyBand == sceneSkyBand0) { + sceneOpaque.bindTexture(); + } else { + sceneSkyBand0.bindTexture(); + } + + skyBand.bind(); + setRenderBufferMask(skyBand, true, false, false); + + setViewportToSizeOf(skyBand); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // TODO: verify this is necessary + + renderFullscreenQuad(); + + bindDisplay(); // TODO: verify this is necessary + setViewportToSizeOf(sceneOpaque); // TODO: verify this is necessary + } +} diff --git a/engine/src/main/java/org/terasology/rendering/dag/WorldReflectionNode.java b/engine/src/main/java/org/terasology/rendering/dag/WorldReflectionNode.java index 4ab826c0c48..492aff17d4f 100644 --- a/engine/src/main/java/org/terasology/rendering/dag/WorldReflectionNode.java +++ b/engine/src/main/java/org/terasology/rendering/dag/WorldReflectionNode.java @@ -34,7 +34,8 @@ import static org.lwjgl.opengl.GL11.GL_COLOR_BUFFER_BIT; import static org.lwjgl.opengl.GL11.GL_DEPTH_BUFFER_BIT; import static org.lwjgl.opengl.GL11.glClear; -import static org.terasology.rendering.opengl.OpenGLUtils.*; +import static org.terasology.rendering.opengl.OpenGLUtils.bindDisplay; +import static org.terasology.rendering.opengl.OpenGLUtils.setViewportToSizeOf; /** * Diagram of this node can be viewed from: @@ -61,6 +62,8 @@ public class WorldReflectionNode implements Node { private Camera playerCamera; private Material chunkShader; private RenderingConfig renderingConfig; + private FBO sceneReflected; + private FBO sceneOpaque; @Override public void initialise() { @@ -71,9 +74,9 @@ public void initialise() { @Override public void process() { - PerformanceMonitor.startActivity("Render World (Reflection)"); - FBO sceneReflected = frameBuffersManager.getFBO("sceneReflected"); - FBO sceneOpaque = frameBuffersManager.getFBO("sceneOpaque"); + PerformanceMonitor.startActivity("rendering/worldreflection"); + sceneReflected = frameBuffersManager.getFBO("sceneReflected"); + sceneOpaque = frameBuffersManager.getFBO("sceneOpaque"); sceneReflected.bind(); setViewportToSizeOf(sceneReflected); diff --git a/engine/src/main/java/org/terasology/rendering/opengl/GraphicState.java b/engine/src/main/java/org/terasology/rendering/opengl/GraphicState.java index fdd72babe53..c49a7f3772a 100644 --- a/engine/src/main/java/org/terasology/rendering/opengl/GraphicState.java +++ b/engine/src/main/java/org/terasology/rendering/opengl/GraphicState.java @@ -15,45 +15,13 @@ */ package org.terasology.rendering.opengl; -import org.lwjgl.BufferUtils; import org.lwjgl.opengl.GL11; -import org.lwjgl.opengl.GL20; import org.terasology.math.geom.Vector3f; -import java.nio.IntBuffer; - -import static org.lwjgl.opengl.EXTFramebufferObject.GL_COLOR_ATTACHMENT0_EXT; -import static org.lwjgl.opengl.GL11.GL_ALWAYS; -import static org.lwjgl.opengl.GL11.GL_BACK; -import static org.lwjgl.opengl.GL11.GL_BLEND; -import static org.lwjgl.opengl.GL11.GL_COLOR_BUFFER_BIT; -import static org.lwjgl.opengl.GL11.GL_CULL_FACE; -import static org.lwjgl.opengl.GL11.GL_DECR; -import static org.lwjgl.opengl.GL11.GL_DEPTH_BUFFER_BIT; -import static org.lwjgl.opengl.GL11.GL_DEPTH_TEST; -import static org.lwjgl.opengl.GL11.GL_FILL; -import static org.lwjgl.opengl.GL11.GL_FRONT; -import static org.lwjgl.opengl.GL11.GL_FRONT_AND_BACK; -import static org.lwjgl.opengl.GL11.GL_INCR; -import static org.lwjgl.opengl.GL11.GL_KEEP; -import static org.lwjgl.opengl.GL11.GL_LEQUAL; -import static org.lwjgl.opengl.GL11.GL_LINE; -import static org.lwjgl.opengl.GL11.GL_NOTEQUAL; -import static org.lwjgl.opengl.GL11.GL_ONE; -import static org.lwjgl.opengl.GL11.GL_ONE_MINUS_SRC_ALPHA; -import static org.lwjgl.opengl.GL11.GL_ONE_MINUS_SRC_COLOR; -import static org.lwjgl.opengl.GL11.GL_SRC_ALPHA; -import static org.lwjgl.opengl.GL11.GL_STENCIL_BUFFER_BIT; -import static org.lwjgl.opengl.GL11.GL_STENCIL_TEST; -import static org.lwjgl.opengl.GL11.glBlendFunc; -import static org.lwjgl.opengl.GL11.glClear; -import static org.lwjgl.opengl.GL11.glCullFace; -import static org.lwjgl.opengl.GL11.glDepthMask; -import static org.lwjgl.opengl.GL11.glDisable; -import static org.lwjgl.opengl.GL11.glEnable; -import static org.lwjgl.opengl.GL11.glStencilFunc; +import static org.lwjgl.opengl.GL11.*; import static org.lwjgl.opengl.GL20.glStencilOpSeparate; import static org.terasology.rendering.opengl.OpenGLUtils.bindDisplay; +import static org.terasology.rendering.opengl.OpenGLUtils.setRenderBufferMask; /** * The GraphicState class aggregates a number of methods setting the OpenGL state @@ -144,30 +112,6 @@ public void setSceneShadowMap(FBO newShadowMap) { buffers.sceneShadowMap = newShadowMap; } - /** - * Initial clearing of a couple of important Frame Buffers. Then binds back the Display. - */ - // It's unclear why these buffers need to be cleared while all the others don't... - public void initialClearing() { - buffers.sceneOpaque.bind(); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); - buffers.sceneReflectiveRefractive.bind(); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - bindDisplay(); - } - - /** - * Readies the state to render the Opaque scene. - * - * The opaque scene includes a number of successive passes including the backdrop (i.e. the skysphere), - * the landscape (chunks/blocks), additional objects associated with the landscape (i.e. other players/fauna), - * overlays (i.e. the cursor-cube) and the geometry associated with the first person view, i.e. the objects - * held in hand. - */ - public void preRenderSetupSceneOpaque() { - buffers.sceneOpaque.bind(); - setRenderBufferMask(buffers.sceneOpaque, true, true, true); - } /** * Resets the state after the rendering of the Opaque scene. @@ -180,59 +124,6 @@ public void postRenderCleanupSceneOpaque() { bindDisplay(); } - /** - * Sets the state to render in wireframe. - * - * @param wireframeIsEnabledInRenderingDebugConfig If True enables wireframe rendering. False, does nothing. - */ - public void enableWireframeIf(boolean wireframeIsEnabledInRenderingDebugConfig) { - if (wireframeIsEnabledInRenderingDebugConfig) { - GL11.glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); - } - } - - /** - * Disables wireframe rendering. Used together with enableWireFrameIf(). - * - * @param wireframeIsEnabledInRenderingDebugConfig If True disables wireframe rendering. False, does nothing. - */ - public void disableWireframeIf(boolean wireframeIsEnabledInRenderingDebugConfig) { - if (wireframeIsEnabledInRenderingDebugConfig) { - GL11.glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); - } - } - - - /** - * Sets the state to render the Backdrop. At this stage the backdrop is the SkySphere - * plus the SkyBands passes. - * - * The backdrop is the only rendering that has three state-changing methods. - * This is due to the SkySphere requiring a state and the SkyBands requiring a slightly - * different one. - */ - public void preRenderSetupBackdrop() { - setRenderBufferMask(buffers.sceneOpaque, true, false, false); - } - - /** - * Sets the state to generate the SkyBands. - * - * See preRenderSetupBackdrop() for some more information. - */ - public void midRenderChangesBackdrop() { - setRenderBufferMask(buffers.sceneOpaque, true, true, true); - } - - /** - * Resets the state after the rendering of the Backdrop. - * - * See preRenderSetupBackdrop() for some more information. - */ - public void postRenderCleanupBackdrop() { - buffers.sceneOpaque.bind(); - } - /** * Sets the state to render the First Person View. * @@ -417,48 +308,7 @@ public void postRenderCleanupChunk() { GL11.glPopMatrix(); } - /** - * Once an FBO is bound, opengl commands will act on it, i.e. by drawing on it. - * Meanwhile shaders might output not just colors but additional per-pixel data. This method establishes on which - * of an FBOs attachments, subsequent opengl commands and shaders will draw on. - * - * @param fbo The FBO holding the attachments to be set or unset for drawing. - * @param color If True the color buffer is set as drawable. If false subsequent commands and shaders won't be able to draw on it. - * @param normal If True the normal buffer is set as drawable. If false subsequent commands and shaders won't be able to draw on it. - * @param lightBuffer If True the light buffer is set as drawable. If false subsequent commands and shaders won't be able to draw on it. - */ - // TODO: verify if this can become part of the FBO.bind() method. - public void setRenderBufferMask(FBO fbo, boolean color, boolean normal, boolean lightBuffer) { - if (fbo == null) { - return; - } - - int attachmentId = 0; - - IntBuffer bufferIds = BufferUtils.createIntBuffer(3); - if (fbo.colorBufferTextureId != 0) { - if (color) { - bufferIds.put(GL_COLOR_ATTACHMENT0_EXT + attachmentId); - } - attachmentId++; - } - if (fbo.normalsBufferTextureId != 0) { - if (normal) { - bufferIds.put(GL_COLOR_ATTACHMENT0_EXT + attachmentId); - } - attachmentId++; - } - if (fbo.lightBufferTextureId != 0) { - if (lightBuffer) { - bufferIds.put(GL_COLOR_ATTACHMENT0_EXT + attachmentId); - } - } - - bufferIds.flip(); - - GL20.glDrawBuffers(bufferIds); - } private class Buffers { public FBO sceneOpaque; diff --git a/engine/src/main/java/org/terasology/rendering/opengl/OpenGLUtils.java b/engine/src/main/java/org/terasology/rendering/opengl/OpenGLUtils.java index 06b04b7fd42..14cc1932f16 100644 --- a/engine/src/main/java/org/terasology/rendering/opengl/OpenGLUtils.java +++ b/engine/src/main/java/org/terasology/rendering/opengl/OpenGLUtils.java @@ -18,17 +18,23 @@ import org.lwjgl.BufferUtils; import org.lwjgl.opengl.GL11; +import org.lwjgl.opengl.GL20; import java.nio.FloatBuffer; +import java.nio.IntBuffer; +import static org.lwjgl.opengl.EXTFramebufferObject.GL_COLOR_ATTACHMENT0_EXT; import static org.lwjgl.opengl.EXTFramebufferObject.GL_FRAMEBUFFER_EXT; import static org.lwjgl.opengl.EXTFramebufferObject.glBindFramebufferEXT; -import static org.lwjgl.opengl.GL11.glViewport; +import static org.lwjgl.opengl.GL11.*; +import static org.lwjgl.opengl.GL11.GL_MODELVIEW; +import static org.lwjgl.opengl.GL11.glPopMatrix; /** */ public final class OpenGLUtils { + private static int displayListQuad = -1; private OpenGLUtils() { // Utility class, no instance required @@ -74,4 +80,121 @@ public static void setViewportToSizeOf(FBO fbo) { public static void bindDisplay() { glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); } + + /** + * Sets the state to render in wireframe. + * + * @param wireframeIsEnabledInRenderingDebugConfig If True enables wireframe rendering. False, does nothing. + */ + public static void enableWireframeIf(boolean wireframeIsEnabledInRenderingDebugConfig) { + if (wireframeIsEnabledInRenderingDebugConfig) { + GL11.glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + } + } + + /** + * Disables wireframe rendering. Used together with enableWireFrameIf(). + * + * @param wireframeIsEnabledInRenderingDebugConfig If True disables wireframe rendering. False, does nothing. + */ + public static void disableWireframeIf(boolean wireframeIsEnabledInRenderingDebugConfig) { + if (wireframeIsEnabledInRenderingDebugConfig) { + GL11.glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + } + } + + + /** + * Once an FBO is bound, opengl commands will act on it, i.e. by drawing on it. + * Meanwhile shaders might output not just colors but additional per-pixel data. This method establishes on which + * of an FBOs attachments, subsequent opengl commands and shaders will draw on. + * + * @param fbo The FBO holding the attachments to be set or unset for drawing. + * @param color If True the color buffer is set as drawable. If false subsequent commands and shaders won't be able to draw on it. + * @param normal If True the normal buffer is set as drawable. If false subsequent commands and shaders won't be able to draw on it. + * @param lightBuffer If True the light buffer is set as drawable. If false subsequent commands and shaders won't be able to draw on it. + */ + // TODO: verify if this can become part of the FBO.bind() method. + public static void setRenderBufferMask(FBO fbo, boolean color, boolean normal, boolean lightBuffer) { + if (fbo == null) { + return; + } + + int attachmentId = 0; + + IntBuffer bufferIds = BufferUtils.createIntBuffer(3); + + if (fbo.colorBufferTextureId != 0) { + if (color) { + bufferIds.put(GL_COLOR_ATTACHMENT0_EXT + attachmentId); + } + attachmentId++; + } + if (fbo.normalsBufferTextureId != 0) { + if (normal) { + bufferIds.put(GL_COLOR_ATTACHMENT0_EXT + attachmentId); + } + attachmentId++; + } + if (fbo.lightBufferTextureId != 0) { + if (lightBuffer) { + bufferIds.put(GL_COLOR_ATTACHMENT0_EXT + attachmentId); + } + } + + bufferIds.flip(); + + GL20.glDrawBuffers(bufferIds); + } + + /** + * Renders a quad filling the whole currently set viewport. + */ + public static void renderFullscreenQuad() { + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + + renderQuad(); + + glPopMatrix(); + + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); + } + + + // TODO: replace with a proper resident buffer with interleaved vertex and uv coordinates + public static void renderQuad() { + if (displayListQuad == -1) { + displayListQuad = glGenLists(1); + + glNewList(displayListQuad, GL11.GL_COMPILE); + + glBegin(GL_QUADS); + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + + glTexCoord2d(0.0, 0.0); + glVertex3i(-1, -1, -1); + + glTexCoord2d(1.0, 0.0); + glVertex3i(1, -1, -1); + + glTexCoord2d(1.0, 1.0); + glVertex3i(1, 1, -1); + + glTexCoord2d(0.0, 1.0); + glVertex3i(-1, 1, -1); + + glEnd(); + + glEndList(); + } + + glCallList(displayListQuad); + } } diff --git a/engine/src/main/java/org/terasology/rendering/opengl/PostProcessor.java b/engine/src/main/java/org/terasology/rendering/opengl/PostProcessor.java index 9ddb7ce1914..c0f80861a3f 100644 --- a/engine/src/main/java/org/terasology/rendering/opengl/PostProcessor.java +++ b/engine/src/main/java/org/terasology/rendering/opengl/PostProcessor.java @@ -51,23 +51,13 @@ import static org.lwjgl.opengl.GL11.GL_DEPTH_BUFFER_BIT; import static org.lwjgl.opengl.GL11.GL_MODELVIEW; import static org.lwjgl.opengl.GL11.GL_PROJECTION; -import static org.lwjgl.opengl.GL11.GL_QUADS; -import static org.lwjgl.opengl.GL11.glBegin; -import static org.lwjgl.opengl.GL11.glCallList; import static org.lwjgl.opengl.GL11.glClear; -import static org.lwjgl.opengl.GL11.glColor4f; -import static org.lwjgl.opengl.GL11.glEnd; -import static org.lwjgl.opengl.GL11.glEndList; -import static org.lwjgl.opengl.GL11.glGenLists; import static org.lwjgl.opengl.GL11.glLoadIdentity; import static org.lwjgl.opengl.GL11.glMatrixMode; -import static org.lwjgl.opengl.GL11.glNewList; import static org.lwjgl.opengl.GL11.glPopMatrix; import static org.lwjgl.opengl.GL11.glPushMatrix; -import static org.lwjgl.opengl.GL11.glTexCoord2d; -import static org.lwjgl.opengl.GL11.glVertex3i; import static org.lwjgl.opengl.GL11.glViewport; -import static org.terasology.rendering.opengl.OpenGLUtils.bindDisplay; +import static org.terasology.rendering.opengl.OpenGLUtils.*; /** * The term "Post Processing" is in analogy to what occurs in the world of Photography: @@ -133,11 +123,9 @@ public class PostProcessor { private float currentExposure = 2.0f; private float currentSceneLuminance = 1.0f; - private int displayListQuad = -1; private FBO.Dimensions fullScale; private FrameBuffersManager buffersManager; - private GraphicState graphicState; private Materials materials = new Materials(); private Buffers buffers = new Buffers(); @@ -158,11 +146,9 @@ public class PostProcessor { * Method refreshDynamicFBOs() must be called at least once to initialize all other FBOs references. * * @param buffersManager An FrameBuffersManager instance, required to obtain FBO references. - * @param graphicState A GraphicState instance, providing opengl state-changing methods. */ - public PostProcessor(FrameBuffersManager buffersManager, GraphicState graphicState) { + public PostProcessor(FrameBuffersManager buffersManager) { this.buffersManager = buffersManager; - this.graphicState = graphicState; } /** @@ -287,47 +273,9 @@ public void refreshSceneOpaqueFBOs() { // instance's lifecycle. public void dispose() { buffersManager = null; - graphicState = null; fullScale = null; } - /** - * Generates SkyBands and stores them into their specific FBOs - * if inscattering is enabled in the rendering config. - * - * SkyBands visually fade the far landscape and its entities into the color of - * the sky, effectively constituting a form of depth cue. - */ - public void generateSkyBands() { - if (renderingConfig.isInscattering()) { - generateSkyBand(buffers.sceneSkyBand0); - generateSkyBand(buffers.sceneSkyBand1); - } - } - - private void generateSkyBand(FBO skyBand) { - materials.blur.enable(); - materials.blur.setFloat("radius", 8.0f, true); - materials.blur.setFloat2("texelSize", 1.0f / skyBand.width(), 1.0f / skyBand.height(), true); - - if (skyBand == buffers.sceneSkyBand0) { - buffers.sceneOpaque.bindTexture(); - } else { - buffers.sceneSkyBand0.bindTexture(); - } - - skyBand.bind(); - graphicState.setRenderBufferMask(skyBand, true, false, false); - - setViewportTo(skyBand.dimensions()); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // TODO: verify this is necessary - - renderFullscreenQuad(); - - bindDisplay(); // TODO: verify this is necessary - setViewportToWholeDisplay(); // TODO: verify this is necessary - } - /** * Part of the deferred lighting technique, this method applies lighting through screen-space * calculations to the previously flat-lit world rendering stored in the primary FBO. // TODO: rename sceneOpaque* FBOs to primaryA/B @@ -355,9 +303,9 @@ public void applyLightBufferPass() { materials.lightBufferPass.setInt("texSceneOpaqueLightBuffer", texId, true); buffers.sceneOpaquePingPong.bind(); - graphicState.setRenderBufferMask(buffers.sceneOpaquePingPong, true, true, true); + setRenderBufferMask(buffers.sceneOpaquePingPong, true, true, true); - setViewportTo(buffers.sceneOpaquePingPong.dimensions()); + setViewportToSizeOf(buffers.sceneOpaquePingPong); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // TODO: verify this is necessary renderFullscreenQuad(); @@ -387,7 +335,7 @@ public void generateOutline() { // TODO: verify inputs: shouldn't there be a texture binding here? buffers.outline.bind(); - setViewportTo(buffers.outline.dimensions()); + setViewportToSizeOf(buffers.outline); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // TODO: verify this is necessary renderFullscreenQuad(); @@ -419,7 +367,7 @@ private void generateSSAO() { // TODO: verify if some textures should be bound here buffers.ssao.bind(); - setViewportTo(buffers.ssao.dimensions()); + setViewportToSizeOf(buffers.ssao); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // TODO: verify this is necessary renderFullscreenQuad(); @@ -436,7 +384,7 @@ private void generateBlurredSSAO() { buffers.ssaoBlurred.bind(); - setViewportTo(buffers.ssaoBlurred.dimensions()); + setViewportToSizeOf(buffers.ssaoBlurred); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // TODO: verify this is necessary renderFullscreenQuad(); @@ -455,7 +403,7 @@ public void generatePrePostComposite() { // TODO: verify if there should be bound textures here. buffers.sceneOpaquePingPong.bind(); - setViewportTo(buffers.sceneOpaquePingPong.dimensions()); + setViewportToSizeOf(buffers.sceneOpaquePingPong); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // TODO: verify this is necessary renderFullscreenQuad(); @@ -478,7 +426,7 @@ public void generateLightShafts() { // TODO: verify what the inputs are buffers.lightShafts.bind(); - setViewportTo(buffers.lightShafts.dimensions()); + setViewportToSizeOf(buffers.lightShafts); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // TODO: verify this is necessary renderFullscreenQuad(); @@ -501,7 +449,7 @@ public void initialPostProcessing() { // TODO: verify what the inputs are buffers.scenePrePost.bind(); // TODO: see if we could write this straight into sceneOpaque - setViewportTo(buffers.scenePrePost.dimensions()); + setViewportToSizeOf(buffers.scenePrePost); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // TODO: verify this is necessary renderFullscreenQuad(); @@ -525,7 +473,7 @@ private void downsampleSceneInto1x1pixelsBuffer() { downSampledFBO.bind(); - setViewportTo(downSampledFBO.dimensions()); + setViewportToSizeOf(downSampledFBO); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // TODO: move this block above, for consistency @@ -614,7 +562,7 @@ public void generateToneMappedScene() { buffers.sceneToneMapped.bind(); - setViewportTo(buffers.sceneToneMapped.dimensions()); + setViewportToSizeOf(buffers.sceneToneMapped); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // TODO: verify this is necessary renderFullscreenQuad(); @@ -660,7 +608,7 @@ private void generateHighPass() { buffers.sceneHighPass.bind(); - setViewportTo(buffers.sceneHighPass.dimensions()); + setViewportToSizeOf(buffers.sceneHighPass); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); renderFullscreenQuad(); @@ -684,7 +632,7 @@ private void generateBloom(FBO sceneBloom) { sceneBloom.bind(); - setViewportTo(sceneBloom.dimensions()); + setViewportToSizeOf(sceneBloom); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // TODO: verify this is necessary renderFullscreenQuad(); @@ -722,7 +670,7 @@ private void generateBlur(FBO sceneBlur) { sceneBlur.bind(); - setViewportTo(sceneBlur.dimensions()); + setViewportToSizeOf(sceneBlur); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); renderFullscreenQuad(); @@ -908,44 +856,11 @@ public void renderFullscreenQuad(int x, int y, int viewportWidth, int viewportHe renderFullscreenQuad(); } - // TODO: replace with a proper resident buffer with interleaved vertex and uv coordinates - private void renderQuad() { - if (displayListQuad == -1) { - displayListQuad = glGenLists(1); - - glNewList(displayListQuad, GL11.GL_COMPILE); - - glBegin(GL_QUADS); - glColor4f(1.0f, 1.0f, 1.0f, 1.0f); - - glTexCoord2d(0.0, 0.0); - glVertex3i(-1, -1, -1); - - glTexCoord2d(1.0, 0.0); - glVertex3i(1, -1, -1); - - glTexCoord2d(1.0, 1.0); - glVertex3i(1, 1, -1); - - glTexCoord2d(0.0, 1.0); - glVertex3i(-1, 1, -1); - - glEnd(); - - glEndList(); - } - - glCallList(displayListQuad); - } private void setViewportToWholeDisplay() { glViewport(0, 0, fullScale.width(), fullScale.height()); } - private void setViewportTo(FBO.Dimensions dimensions) { - glViewport(0, 0, dimensions.width(), dimensions.height()); - } - /** * Returns the current exposure value (set in downsampleSceneAndUpdateExposure()). * diff --git a/engine/src/main/java/org/terasology/rendering/world/WorldRendererImpl.java b/engine/src/main/java/org/terasology/rendering/world/WorldRendererImpl.java index 09ac113be8c..e8bac7b16ad 100644 --- a/engine/src/main/java/org/terasology/rendering/world/WorldRendererImpl.java +++ b/engine/src/main/java/org/terasology/rendering/world/WorldRendererImpl.java @@ -40,10 +40,11 @@ import org.terasology.rendering.assets.material.Material; import org.terasology.rendering.assets.shader.ShaderProgramFeature; import org.terasology.rendering.backdrop.BackdropProvider; -import org.terasology.rendering.backdrop.BackdropRenderer; import org.terasology.rendering.cameras.Camera; import org.terasology.rendering.cameras.OculusStereoCamera; import org.terasology.rendering.cameras.PerspectiveCamera; +import org.terasology.rendering.dag.BackdropNode; +import org.terasology.rendering.dag.SkyBandsNode; import org.terasology.rendering.logic.LightComponent; import org.terasology.rendering.opengl.FrameBuffersManager; import org.terasology.rendering.opengl.GraphicState; @@ -63,6 +64,8 @@ import java.util.List; import java.util.PriorityQueue; +import static org.terasology.rendering.opengl.OpenGLUtils.disableWireframeIf; + /** * Renders the 3D world, including background, overlays and first person/in hand objects. 2D UI elements are dealt with elsewhere. * @@ -81,7 +84,6 @@ public final class WorldRendererImpl implements WorldRenderer { private boolean isFirstRenderingStageForCurrentFrame; private final RenderQueuesHelper renderQueues; private final Context context; - private final BackdropRenderer backdropRenderer; private final BackdropProvider backdropProvider; private final WorldProvider worldProvider; private final RenderableWorld renderableWorld; @@ -140,7 +142,6 @@ public WorldRendererImpl(Context context, GLBufferPool bufferPool) { this.context = context; this.worldProvider = context.get(WorldProvider.class); this.backdropProvider = context.get(BackdropProvider.class); - this.backdropRenderer = context.get(BackdropRenderer.class); this.renderingConfig = context.get(Config.class).getRendering(); this.renderingDebugConfig = renderingConfig.getDebug(); this.systemManager = context.get(ComponentSystemManager.class); @@ -180,7 +181,7 @@ private void initRenderingSupport() { context.put(FrameBuffersManager.class, buffersManager); graphicState = new GraphicState(buffersManager); - postProcessor = new PostProcessor(buffersManager, graphicState); + postProcessor = new PostProcessor(buffersManager); context.put(PostProcessor.class, postProcessor); buffersManager.setGraphicState(graphicState); @@ -207,10 +208,14 @@ private void initPipeline() { // FIXME: init pipeline without specifying them as a field in this class shadowMapNode = createInstance(ShadowMapNode.class, context); Node worldReflectionNode = createInstance(WorldReflectionNode.class, context); + Node backdropNode = createInstance(BackdropNode.class, context); + Node skybandsNode = createInstance(SkyBandsNode.class, context); renderingPipeline = Lists.newArrayList(); renderingPipeline.add(shadowMapNode); renderingPipeline.add(worldReflectionNode); + renderingPipeline.add(backdropNode); + renderingPipeline.add(skybandsNode); } private static T createInstance(Class type, Context context) { @@ -314,12 +319,6 @@ public void render(RenderingStage renderingStage) { renderingPipeline.forEach(Node::process); - graphicState.enableWireframeIf(renderingDebugConfig.isWireframe()); - graphicState.initialClearing(); - - graphicState.preRenderSetupSceneOpaque(); - renderBackdrop(); // into sceneOpaque and skyBands[0-1] buffers - try (Activity ignored = PerformanceMonitor.startActivity("Render World")) { renderObjectsOpaque(); // renderChunksOpaque(); // @@ -333,7 +332,7 @@ public void render(RenderingStage renderingStage) { renderChunksRefractiveReflective(); // into sceneReflectiveRefractive buffer } - graphicState.disableWireframeIf(renderingDebugConfig.isWireframe()); + disableWireframeIf(renderingDebugConfig.isWireframe()); PerformanceMonitor.startActivity("Pre-post composite"); postProcessor.generateOutline(); // into outline buffer @@ -362,20 +361,6 @@ public void render(RenderingStage renderingStage) { playerCamera.updatePrevViewProjectionMatrix(); } - private void renderBackdrop() { - PerformanceMonitor.startActivity("Render Sky"); - playerCamera.lookThroughNormalized(); - graphicState.preRenderSetupBackdrop(); - - backdropRenderer.render(playerCamera); - graphicState.midRenderChangesBackdrop(); - postProcessor.generateSkyBands(); - - graphicState.postRenderCleanupBackdrop(); - - playerCamera.lookThrough(); - PerformanceMonitor.endActivity(); - } private void renderObjectsOpaque() { PerformanceMonitor.startActivity("Render Objects (Opaque)");