Skip to content

Commit

Permalink
Merge pull request #84 from JamesTKhan/frustum-culling
Browse files Browse the repository at this point in the history
Add frustum culling
  • Loading branch information
JamesTKhan authored Jul 24, 2022
2 parents 13dff39 + 7741861 commit dee18f7
Show file tree
Hide file tree
Showing 17 changed files with 334 additions and 39 deletions.
20 changes: 9 additions & 11 deletions commons/src/main/com/mbrlabs/mundus/commons/Scene.java
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,10 @@ public class Scene implements Disposable {
private long id;

public SceneGraph sceneGraph;
public SceneSettings settings;
public MundusEnvironment environment;
public Skybox skybox;
public String skyboxAssetId;
public float waterHeight = 0f;
public WaterResolution waterResolution = WaterResolution.DEFAULT_WATER_RESOLUTION;

public PerspectiveCamera cam;
public ModelBatch batch;
Expand All @@ -80,10 +79,9 @@ public class Scene implements Disposable {
protected Vector3 clippingPlaneReflection = new Vector3(0.0f, 1f, 0.0f);
protected Vector3 clippingPlaneRefraction = new Vector3(0.0f, -1f, 0.0f);

private final float distortionEdgeCorrection = 1f;

public Scene() {
environment = new MundusEnvironment();
settings = new SceneSettings();

cam = new PerspectiveCamera(CameraSettings.DEFAULT_FOV, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
cam.position.set(0, 1, -3);
Expand Down Expand Up @@ -131,7 +129,7 @@ public void render() {

public void render(float delta) {
if (fboWaterReflection == null) {
Vector2 res = waterResolution.getResolutionValues();
Vector2 res = settings.waterResolution.getResolutionValues();
initFrameBuffers((int) res.x, (int) res.y);
}

Expand Down Expand Up @@ -196,7 +194,7 @@ private void initFrameBuffers(int width, int height) {

private void captureReflectionFBO(float delta) {
// Calc vertical distance for camera for reflection FBO
float camReflectionDistance = 2 * (cam.position.y - waterHeight);
float camReflectionDistance = 2 * (cam.position.y - settings.waterHeight);

// Save current cam positions
Vector3 camPos = cam.position.cpy();
Expand All @@ -212,7 +210,7 @@ private void captureReflectionFBO(float delta) {
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT);
renderSkybox();
batch.begin(cam);
sceneGraph.render(delta, clippingPlaneReflection, -waterHeight + distortionEdgeCorrection);
sceneGraph.render(delta, clippingPlaneReflection, -settings.waterHeight + settings.distortionEdgeCorrection);
batch.end();
fboWaterReflection.end();

Expand All @@ -227,7 +225,7 @@ private void captureDepth(float delta) {
fboDepthRefraction.begin();
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT);
depthBatch.begin(cam);
sceneGraph.renderDepth(delta, clippingPlaneRefraction, waterHeight + distortionEdgeCorrection, depthShader);
sceneGraph.renderDepth(delta, clippingPlaneRefraction, settings.waterHeight + settings.distortionEdgeCorrection, depthShader);
depthBatch.end();
fboDepthRefraction.end();
}
Expand All @@ -246,7 +244,7 @@ private void captureRefractionFBO(float delta) {
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT);
renderSkybox();
batch.begin(cam);
sceneGraph.render(delta, clippingPlaneRefraction, waterHeight + distortionEdgeCorrection);
sceneGraph.render(delta, clippingPlaneRefraction, settings.waterHeight + settings.distortionEdgeCorrection);
batch.end();
fboWaterRefraction.end();
}
Expand Down Expand Up @@ -294,8 +292,8 @@ public void setShadowMapShader(ShadowMapShader shadowMapShader) {
* @param resolution the resolution to use
*/
public void setWaterResolution(WaterResolution resolution) {
this.waterResolution = resolution;
Vector2 res = waterResolution.getResolutionValues();
settings.waterResolution = resolution;
Vector2 res = settings.waterResolution.getResolutionValues();
initFrameBuffers((int) res.x, (int) res.y);
}

Expand Down
19 changes: 19 additions & 0 deletions commons/src/main/com/mbrlabs/mundus/commons/SceneSettings.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.mbrlabs.mundus.commons;

import com.mbrlabs.mundus.commons.water.WaterResolution;

/**
* Settings specific to a Scene.
* @author JamesTKhan
* @version July 23, 2022
*/
public class SceneSettings {

// Water
public float waterHeight = 0f;
public final float distortionEdgeCorrection = 1f;
public WaterResolution waterResolution = WaterResolution.DEFAULT_WATER_RESOLUTION;

// Performance
public boolean useFrustumCulling = true;
}
9 changes: 9 additions & 0 deletions commons/src/main/com/mbrlabs/mundus/commons/dto/SceneDTO.java
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ public class SceneDTO implements Json.Serializable {
private float camFarPlane;
private float camFieldOfView;
private float waterHeight;
private boolean useFrustumCulling;
private WaterResolution waterResolution;

public SceneDTO() {
Expand Down Expand Up @@ -187,6 +188,14 @@ public float getWaterHeight() {
return waterHeight;
}

public boolean isUseFrustumCulling() {
return useFrustumCulling;
}

public void setUseFrustumCulling(boolean useFrustumCulling) {
this.useFrustumCulling = useFrustumCulling;
}

public void setSkyboxAssetId(String skyboxAssetId) {
this.skyboxAssetId = skyboxAssetId;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ public class GameObject extends SimpleNode<GameObject> implements Iterable<GameO

public String name;
public boolean active;
public boolean scaleChanged;
private Array<String> tags;
private Array<Component> components;

Expand Down Expand Up @@ -169,6 +170,9 @@ public void update(float delta) {
}
}
}

// Reset after each update, after components have updated that might need to know about it
scaleChanged = false;
}

/**
Expand Down Expand Up @@ -310,6 +314,25 @@ public void remove() {
}
}

@Override
public void setLocalScale(float x, float y, float z) {
super.setLocalScale(x, y, z);
// We track when the scale has changed, for recalculating bounds for things like frustum culling
scaleChanged = true;
}

@Override
public void scale(Vector3 v) {
super.scale(v);
scaleChanged = true;
}

@Override
public void scale(float x, float y, float z) {
super.scale(x,y,z);
scaleChanged = true;
}

@Override
public Iterator<GameObject> iterator() {
return new DepthFirstIterator(this);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/*
* Copyright (c) 2022. See AUTHORS file.
*
* 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 com.mbrlabs.mundus.commons.scene3d.components;

import com.badlogic.gdx.graphics.Camera;
import com.badlogic.gdx.graphics.g3d.ModelInstance;
import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.math.collision.BoundingBox;
import com.mbrlabs.mundus.commons.env.lights.DirectionalLight;
import com.mbrlabs.mundus.commons.scene3d.GameObject;
import com.mbrlabs.mundus.commons.shadows.ShadowMapper;
import com.mbrlabs.mundus.commons.utils.LightUtils;

import static com.mbrlabs.mundus.commons.utils.ModelUtils.isVisible;

/**
* Components that can be Culled via Frustum Culling should extend
* this class and call setDimensions once they have access to a modelInstance as well as super
* for render() and update()
*
* The isCulled value will be set accordingly and components can check if is isCulled == true
* before rendering.
*
* @author JamesTKhan
* @version July 18, 2022
*/
public abstract class CullableComponent extends AbstractComponent {
private final static BoundingBox tmpBounds = new BoundingBox();
private final static Vector3 tmpScale = new Vector3();
private static DirectionalLight directionalLight;

public final Vector3 center = new Vector3();
public final Vector3 dimensions = new Vector3();
public float radius;

// Is it offscreen?
protected boolean isCulled = false;
private ModelInstance modelInstance = null;

public CullableComponent(GameObject go) {
super(go);

// Update out reference on creation of new component
directionalLight = LightUtils.getDirectionalLight(go.sceneGraph.scene.environment);
}

@Override
public void render(float delta) {
if (modelInstance == null) return;

if (!gameObject.sceneGraph.scene.settings.useFrustumCulling) {
isCulled = false;
return;
}

boolean visibleToPerspective;
boolean visibleToShadowMap = false;

Camera sceneCam = gameObject.sceneGraph.scene.cam;

visibleToPerspective = isVisible(sceneCam, modelInstance, center, radius);

// If not visible to main cam, check if it's visible to shadow map (to prevent shadows popping out)
if (!visibleToPerspective) {
if (directionalLight.castsShadows && gameObject.sceneGraph.scene.environment.shadowMap instanceof ShadowMapper) {
ShadowMapper shadowMapper = (ShadowMapper) gameObject.sceneGraph.scene.environment.shadowMap;
visibleToShadowMap = isVisible(shadowMapper.getCam(), modelInstance, center, radius);
}
}

isCulled = !visibleToPerspective && !visibleToShadowMap;
}

@Override
public void update(float delta) {
if (gameObject.scaleChanged) {
setDimensions(modelInstance);
}
}

protected void setDimensions(ModelInstance modelInstance) {
this.modelInstance = modelInstance;
modelInstance.calculateBoundingBox(tmpBounds);
tmpBounds.getCenter(center);
tmpBounds.getDimensions(dimensions);
gameObject.getScale(tmpScale);
center.scl(tmpScale);
dimensions.scl(tmpScale);
radius = dimensions.len() / 2f;
}

public boolean isCulled() {
return isCulled;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
* @author Marcus Brummer
* @version 17-01-2016
*/
public class ModelComponent extends AbstractComponent implements AssetUsage, ClippableComponent {
public class ModelComponent extends CullableComponent implements AssetUsage, ClippableComponent {

protected ModelAsset modelAsset;
protected ModelInstance modelInstance;
Expand Down Expand Up @@ -76,6 +76,8 @@ public void setModel(ModelAsset model, boolean inheritMaterials) {
}
}
applyMaterials();

setDimensions(modelInstance);
}

public ObjectMap<String, MaterialAsset> getMaterials() {
Expand All @@ -101,6 +103,9 @@ public ModelInstance getModelInstance() {

@Override
public void render(float delta) {
super.render(delta);
if (isCulled) return;

modelInstance.transform.set(gameObject.getTransform());
if (shader != null) {
gameObject.sceneGraph.scene.batch.render(modelInstance, gameObject.sceneGraph.scene.environment, shader);
Expand All @@ -111,6 +116,8 @@ public void render(float delta) {

@Override
public void renderDepth(float delta, Vector3 clippingPlane, float clipHeight, Shader depthShader) {
if (isCulled) return;

if (depthShader instanceof ClippableShader) {
((ClippableShader) depthShader).setClippingPlane(clippingPlane);
((ClippableShader) depthShader).setClippingHeight(clipHeight);
Expand Down Expand Up @@ -139,11 +146,6 @@ public void render(float delta, Vector3 clippingPlane, float clipHeight) {
render(delta);
}

@Override
public void update(float delta) {

}

@Override
public Component clone(GameObject go) {
ModelComponent mc = new ModelComponent(go, shader);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
* @author Marcus Brummer
* @version 18-01-2016
*/
public class TerrainComponent extends AbstractComponent implements AssetUsage, ClippableComponent {
public class TerrainComponent extends CullableComponent implements AssetUsage, ClippableComponent {

private static final String TAG = TerrainComponent.class.getSimpleName();

Expand All @@ -49,6 +49,7 @@ public void updateUVs(Vector2 uvScale) {

public void setTerrain(TerrainAsset terrain) {
this.terrain = terrain;
setDimensions(terrain.getTerrain().modelInstance);
}

public TerrainAsset getTerrain() {
Expand All @@ -65,6 +66,8 @@ public void setShader(Shader shader) {

@Override
public void render(float delta) {
super.render(delta);
if (isCulled) return;
gameObject.sceneGraph.scene.batch.render(terrain.getTerrain(), gameObject.sceneGraph.scene.environment, shader);
}

Expand All @@ -79,6 +82,8 @@ public void render(float delta, Vector3 clippingPlane, float clipHeight) {

@Override
public void renderDepth(float delta, Vector3 clippingPlane, float clipHeight, Shader shader) {
if (isCulled) return;

if (shader instanceof ClippableShader) {
((ClippableShader) shader).setClippingPlane(clippingPlane);
((ClippableShader) shader).setClippingHeight(clipHeight);
Expand All @@ -87,11 +92,6 @@ public void renderDepth(float delta, Vector3 clippingPlane, float clipHeight, Sh
gameObject.sceneGraph.scene.depthBatch.render(terrain.getTerrain(), gameObject.sceneGraph.scene.environment, shader);
}

@Override
public void update(float delta) {

}

@Override
public Component clone(GameObject go) {
// Cant be cloned right now
Expand Down
Loading

0 comments on commit dee18f7

Please sign in to comment.