Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Wrapping ShadowMap and WorldReflection into a "Node" #2325

Merged
merged 17 commits into from
Jun 18, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
/*
* Copyright 2014 MovingBlocks
* 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
* 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,
Expand All @@ -18,13 +18,15 @@
import com.google.common.collect.Lists;
import org.terasology.config.Config;
import org.terasology.context.Context;
import org.terasology.logic.players.LocalPlayer;
import org.terasology.logic.players.LocalPlayerSystem;
import org.terasology.math.Region3i;
import org.terasology.math.geom.Vector3f;
import org.terasology.math.geom.Vector3i;
import org.terasology.monitoring.PerformanceMonitor;
import org.terasology.rendering.assets.material.Material;
import org.terasology.rendering.cameras.Camera;
import org.terasology.rendering.primitives.ChunkMesh;
import org.terasology.rendering.world.WorldRendererImpl;
import org.terasology.rendering.world.viewDistance.ViewDistance;
import org.terasology.rendering.world.WorldRenderer;
import org.terasology.world.WorldProvider;
Expand All @@ -36,6 +38,7 @@
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.PriorityQueue;

public class HeadlessWorldRenderer implements WorldRenderer {

Expand All @@ -61,6 +64,26 @@ public HeadlessWorldRenderer(Context context) {
config = context.get(Config.class);
}

@Override
public float getSecondsSinceLastFrame() {
return 0;
}

@Override
public Material getMaterial(String assetId) {
return null;
}

@Override
public boolean isFirstRenderingStageForCurrentFrame() {
return false;
}

@Override
public void renderChunks(PriorityQueue<RenderableChunk> chunks, ChunkMesh.RenderPhase phase, Camera camera, WorldRendererImpl.ChunkRenderMode mode) {

}

@Override
public void onChunkLoaded(Vector3i pos) {

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2013 MovingBlocks
* 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.
Expand Down Expand Up @@ -39,7 +39,7 @@
import org.terasology.rendering.assets.material.Material;
import org.terasology.rendering.assets.texture.Texture;
import org.terasology.rendering.logic.NearestSortingList;
import org.terasology.rendering.opengl.OpenGLUtil;
import org.terasology.rendering.opengl.OpenGLUtils;
import org.terasology.rendering.world.WorldRenderer;
import org.terasology.utilities.random.FastRandom;
import org.terasology.utilities.random.Random;
Expand Down Expand Up @@ -296,7 +296,7 @@ private void renderBlockParticles(Vector3f worldPos, Vector3f cameraPosition, Bl
for (Particle particle : particleEffect.particles) {
glPushMatrix();
glTranslatef(particle.position.x, particle.position.y, particle.position.z);
OpenGLUtil.applyBillboardOrientation();
OpenGLUtils.applyBillboardOrientation();
glScalef(particle.size, particle.size, particle.size);

float light = worldRenderer.getRenderingLightIntensityAt(new Vector3f(worldPos.x + particle.position.x,
Expand All @@ -314,7 +314,7 @@ private void renderParticles(Vector3f worldPos, Vector3f cameraPosition, BlockPa
for (Particle particle : particleEffect.particles) {
glPushMatrix();
glTranslatef(particle.position.x, particle.position.y, particle.position.z);
OpenGLUtil.applyBillboardOrientation();
OpenGLUtils.applyBillboardOrientation();
glScalef(particle.size, particle.size, particle.size);

float light = worldRenderer.getRenderingLightIntensityAt(new Vector3f(worldPos.x + particle.position.x,
Expand Down
24 changes: 24 additions & 0 deletions engine/src/main/java/org/terasology/rendering/dag/Node.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* 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;


public interface Node {

void initialise();

void process();
}
148 changes: 148 additions & 0 deletions engine/src/main/java/org/terasology/rendering/dag/ShadowMapNode.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
/*
* 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.lwjgl.opengl.GL11;
import org.terasology.config.Config;
import org.terasology.config.RenderingConfig;
import org.terasology.math.TeraMath;
import org.terasology.math.geom.Vector3f;
import org.terasology.monitoring.PerformanceMonitor;
import org.terasology.registry.In;
import org.terasology.rendering.assets.material.Material;
import org.terasology.rendering.backdrop.BackdropProvider;
import org.terasology.rendering.cameras.Camera;
import org.terasology.rendering.cameras.OrthographicCamera;
import org.terasology.rendering.opengl.FBO;
import org.terasology.rendering.opengl.FrameBuffersManager;
import org.terasology.rendering.primitives.ChunkMesh;
import org.terasology.rendering.world.RenderQueuesHelper;
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.*;


/**
* Diagram of this node can be viewed from:
* TODO: move diagram to the wiki when this part of the code is stable
* - https://docs.google.com/drawings/d/13I0GM9jDFlZv1vNrUPlQuBbaF86RPRNpVfn5q8Wj2lc/edit?usp=sharing
*/
public class ShadowMapNode implements Node {
private static final int SHADOW_FRUSTUM_BOUNDS = 500;
public Camera shadowMapCamera = new OrthographicCamera(-SHADOW_FRUSTUM_BOUNDS, SHADOW_FRUSTUM_BOUNDS, SHADOW_FRUSTUM_BOUNDS, -SHADOW_FRUSTUM_BOUNDS);

@In
private RenderableWorld renderableWorld;

@In
private RenderQueuesHelper renderQueues;

@In
private Config config;

@In
private FrameBuffersManager frameBuffersManager;

@In
private WorldRenderer worldRenderer;

@In
private BackdropProvider backdropProvider;

private Material shadowMapShader;
private FBO shadowMap;
private RenderingConfig renderingConfig;
private Camera playerCamera;


@Override
public void initialise() {
this.playerCamera = worldRenderer.getActiveCamera();
this.shadowMap = frameBuffersManager.getFBO("sceneShadowMap");
this.shadowMapShader = worldRenderer.getMaterial("engine:prog.shadowMap");
this.renderingConfig = config.getRendering();
renderableWorld.setShadowMapCamera(shadowMapCamera);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My only suggestion here would be that you can get the playerCamera via WorldRenderer.getActiveCamera().

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would use the InjectionHelper here. This enables the @In annotation for the fields that are known to the context.


@Override
public void process() {
this.shadowMap = frameBuffersManager.getFBO("sceneShadowMap");
positionShadowMapCamera();

// TODO: find an elegant way to fetch isFirstRenderingStageForCurrentFrame
boolean processCondition = renderingConfig.isDynamicShadows() && worldRenderer.isFirstRenderingStageForCurrentFrame();
if (!processCondition) {
return;
}

PerformanceMonitor.startActivity("Render World (Shadow Map)");
// preRenderSetupSceneShadowMap
shadowMap.bind();
setViewportToSizeOf(shadowMap);
// TODO: verify the need to clear color buffer
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
GL11.glDisable(GL_CULL_FACE);

// render
shadowMapCamera.lookThrough();

shadowMapShader.enable();
// FIXME: storing chuncksOpaqueShadow or a mechanism for requesting a chunk queue for nodes which calls renderChunks method?
worldRenderer.renderChunks(renderQueues.chunksOpaqueShadow, ChunkMesh.RenderPhase.OPAQUE, shadowMapCamera, WorldRendererImpl.ChunkRenderMode.SHADOW_MAP);
playerCamera.lookThrough(); //FIXME: not strictly needed: just defensive programming here.

// postRenderCleanupSceneShadowMap
GL11.glEnable(GL_CULL_FACE);
bindDisplay();

PerformanceMonitor.endActivity();
}

private void positionShadowMapCamera() {
// Shadows are rendered around the player so...
Vector3f lightPosition = new Vector3f(playerCamera.getPosition().x, 0.0f, playerCamera.getPosition().z);

// Project the shadowMapCamera position to light space and make sure it is only moved in texel steps (avoids flickering when moving the shadowMapCamera)
float texelSize = 1.0f / renderingConfig.getShadowMapResolution();
texelSize *= 2.0f;

shadowMapCamera.getViewProjectionMatrix().transformPoint(lightPosition);
lightPosition.set(TeraMath.fastFloor(lightPosition.x / texelSize) * texelSize, 0.0f, TeraMath.fastFloor(lightPosition.z / texelSize) * texelSize);
shadowMapCamera.getInverseViewProjectionMatrix().transformPoint(lightPosition);

// ... we position our new shadowMapCamera at the position of the player and move it
// quite a bit into the direction of the sun (our main light).

// Make sure the sun does not move too often since it causes massive shadow flickering (from hell to the max)!
float stepSize = 50f;
Vector3f sunDirection = backdropProvider.getQuantizedSunDirection(stepSize);

Vector3f sunPosition = new Vector3f(sunDirection);
sunPosition.scale(256.0f + 64.0f);
lightPosition.add(sunPosition);
shadowMapCamera.getPosition().set(lightPosition);

// and adjust it to look from the sun direction into the direction of our player
Vector3f negSunDirection = new Vector3f(sunDirection);
negSunDirection.scale(-1.0f);

shadowMapCamera.getViewingDirection().set(negSunDirection);
shadowMapCamera.update(worldRenderer.getSecondsSinceLastFrame());
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/*
* 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.lwjgl.opengl.GL11;
import org.terasology.config.Config;
import org.terasology.config.RenderingConfig;
import org.terasology.monitoring.PerformanceMonitor;
import org.terasology.registry.In;
import org.terasology.rendering.assets.material.Material;
import org.terasology.rendering.assets.shader.ShaderProgramFeature;
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.primitives.ChunkMesh;
import org.terasology.rendering.world.RenderQueuesHelper;
import org.terasology.rendering.world.WorldRenderer;
import org.terasology.rendering.world.WorldRendererImpl;

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.*;

/**
* Diagram of this node can be viewed from:
* TODO: move diagram to the wiki when this part of the code is stable
* - https://docs.google.com/drawings/d/1Iz7MA8Y5q7yjxxcgZW-0antv5kgx6NYkvoInielbwGU/edit?usp=sharing
*/
public class WorldReflectionNode implements Node {

@In
private FrameBuffersManager frameBuffersManager;

@In
private RenderQueuesHelper renderQueues;

@In
private Config config;

@In
private WorldRenderer worldRenderer;

@In
private BackdropRenderer backdropRenderer;

private Camera playerCamera;
private Material chunkShader;
private RenderingConfig renderingConfig;

@Override
public void initialise() {
this.renderingConfig = config.getRendering();
this.chunkShader = worldRenderer.getMaterial("engine:prog.chunk");
this.playerCamera = worldRenderer.getActiveCamera();
}

@Override
public void process() {
PerformanceMonitor.startActivity("Render World (Reflection)");
FBO sceneReflected = frameBuffersManager.getFBO("sceneReflected");
FBO sceneOpaque = frameBuffersManager.getFBO("sceneOpaque");

sceneReflected.bind();
setViewportToSizeOf(sceneReflected);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
GL11.glCullFace(GL11.GL_FRONT);
playerCamera.setReflected(true);

playerCamera.lookThroughNormalized(); // we don't want the reflected scene to be bobbing or moving with the player
// TODO: convert backdropRenderer into a BackdropNode.
backdropRenderer.render(playerCamera);
Copy link
Contributor

@emanuele3d emanuele3d Jun 14, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's add:

TODO: convert backdropRenderer into a BackdropNode.

Note for @tdgunes: this will be an interesting use case for GL state management and order of execution of nodes: both the the BackdropNode and the WorldReflectionNode need similar/identical states, so the nodes should be processed one after another and the first node to be processed should not be allowed to reset to default the states the second node needs. Also interesting, the BackdropNode will need to be used twice, one here, to render the reflection, and then later, for the proper renderer. Something to put in the back of our minds.

playerCamera.lookThrough();

if (renderingConfig.isReflectiveWater()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmmm.... let's put a TODO here:

TODO: the isReflectiveWater() block should include content of the whole process() method. Eventually the check will be removed, when node insertion will signify that the feature is enabled.

// TODO: the isReflectiveWater() block should include content of the whole process() method.
// TODO: Eventually the check will be removed, when node insertion will signify that the feature is enabled.
chunkShader.activateFeature(ShaderProgramFeature.FEATURE_USE_FORWARD_LIGHTING);
worldRenderer.renderChunks(renderQueues.chunksOpaqueReflection, ChunkMesh.RenderPhase.OPAQUE, playerCamera, WorldRendererImpl.ChunkRenderMode.REFLECTION);
chunkShader.deactivateFeature(ShaderProgramFeature.FEATURE_USE_FORWARD_LIGHTING);
}

playerCamera.setReflected(false);

GL11.glCullFace(GL11.GL_BACK);
bindDisplay();
setViewportToSizeOf(sceneOpaque);

PerformanceMonitor.endActivity();
}
}
Loading