From 5356b3e325a06524aef62e5a50062dcfb6b810da Mon Sep 17 00:00:00 2001 From: austin Date: Thu, 29 Feb 2024 19:14:17 -0500 Subject: [PATCH] Proper frustum culling --- buildNumber.properties | 4 +- docs/src/rendering/actors/actorsindex.md | 9 ++ docs/src/rendering/actors/animationmask.md | 5 ++ docs/src/rendering/actors/bonerotators.md | 5 ++ docs/src/rendering/actors/meshmask.md | 5 ++ docs/src/rendering/actors/shadermask.md | 5 ++ docs/src/rendering/actors/staticmorph.md | 5 ++ docs/src/rendering/actors/texturemask.md | 3 + .../client/foliagemanager/FoliageCell.java | 2 +- .../client/instancing/InstanceUpdater.java | 29 +++++++ .../client/sim/ClientFunctions.java | 2 + .../client/sim/ClientSimulation.java | 4 - .../collision/CollisionBodyCreation.java | 4 +- .../collision/CollisionEngine.java | 10 +++ .../java/electrosphere/renderer/Mesh.java | 35 +++++++- .../java/electrosphere/renderer/Model.java | 31 +++++++ .../renderer/RenderPipelineState.java | 26 ++++++ .../renderer/RenderingEngine.java | 83 +++++++++---------- .../electrosphere/renderer/actor/Actor.java | 69 ++++++++++----- .../renderer/actor/ActorBoneRotator.java | 4 + .../renderer/actor/ActorStaticMorph.java | 4 + .../renderer/actor/instance/InstanceData.java | 11 ++- .../actor/instance/InstancedActor.java | 20 +++++ .../meshgen/TerrainChunkModelGeneration.java | 9 ++ 24 files changed, 303 insertions(+), 81 deletions(-) create mode 100644 docs/src/rendering/actors/actorsindex.md create mode 100644 docs/src/rendering/actors/animationmask.md create mode 100644 docs/src/rendering/actors/bonerotators.md create mode 100644 docs/src/rendering/actors/meshmask.md create mode 100644 docs/src/rendering/actors/shadermask.md create mode 100644 docs/src/rendering/actors/staticmorph.md create mode 100644 docs/src/rendering/actors/texturemask.md create mode 100644 src/main/java/electrosphere/client/instancing/InstanceUpdater.java diff --git a/buildNumber.properties b/buildNumber.properties index 05f0dd2d..66bab587 100644 --- a/buildNumber.properties +++ b/buildNumber.properties @@ -1,3 +1,3 @@ #maven.buildNumber.plugin properties file -#Thu Feb 29 11:18:06 EST 2024 -buildNumber=29 +#Thu Feb 29 18:57:13 EST 2024 +buildNumber=30 diff --git a/docs/src/rendering/actors/actorsindex.md b/docs/src/rendering/actors/actorsindex.md new file mode 100644 index 00000000..90ca4763 --- /dev/null +++ b/docs/src/rendering/actors/actorsindex.md @@ -0,0 +1,9 @@ +@page actorsindex Actors + +[TOC] +- @subpage texturemask +- @subpage bonerotators +- @subpage staticmorph +- @subpage shadermask +- @subpage animationmask +- @subpage meshmask \ No newline at end of file diff --git a/docs/src/rendering/actors/animationmask.md b/docs/src/rendering/actors/animationmask.md new file mode 100644 index 00000000..48c3ff2e --- /dev/null +++ b/docs/src/rendering/actors/animationmask.md @@ -0,0 +1,5 @@ +@page animationmask Animation Mask + +TODO + +use example: play running animation for whole actor, but then mask crafting animation on top of bones from torso up \ No newline at end of file diff --git a/docs/src/rendering/actors/bonerotators.md b/docs/src/rendering/actors/bonerotators.md new file mode 100644 index 00000000..08487c03 --- /dev/null +++ b/docs/src/rendering/actors/bonerotators.md @@ -0,0 +1,5 @@ +@page bonerotators Actor Bone Rotators + +TODO + +use example: rotate torso to tilt based on whatever the player is locked on to, so that they swing at goblins below them and harpies above them \ No newline at end of file diff --git a/docs/src/rendering/actors/meshmask.md b/docs/src/rendering/actors/meshmask.md new file mode 100644 index 00000000..6b3499dc --- /dev/null +++ b/docs/src/rendering/actors/meshmask.md @@ -0,0 +1,5 @@ +@page meshmask Mesh Mask + +TODO + +use example: overwriting a mesh without clothing to one with clothing \ No newline at end of file diff --git a/docs/src/rendering/actors/shadermask.md b/docs/src/rendering/actors/shadermask.md new file mode 100644 index 00000000..07ffdef7 --- /dev/null +++ b/docs/src/rendering/actors/shadermask.md @@ -0,0 +1,5 @@ +@page shadermask Shader Mask + +TODO + +use example: override default cube shader to use a shader that does raymarching \ No newline at end of file diff --git a/docs/src/rendering/actors/staticmorph.md b/docs/src/rendering/actors/staticmorph.md new file mode 100644 index 00000000..0e9ee711 --- /dev/null +++ b/docs/src/rendering/actors/staticmorph.md @@ -0,0 +1,5 @@ +@page staticmorph Actor Static Morph + +TODO + +use example: scale something that was exported from blender waayyyyyy too large \ No newline at end of file diff --git a/docs/src/rendering/actors/texturemask.md b/docs/src/rendering/actors/texturemask.md new file mode 100644 index 00000000..22adcce0 --- /dev/null +++ b/docs/src/rendering/actors/texturemask.md @@ -0,0 +1,3 @@ +@page texturemask Actor Texture Mask + +TODO \ No newline at end of file diff --git a/src/main/java/electrosphere/client/foliagemanager/FoliageCell.java b/src/main/java/electrosphere/client/foliagemanager/FoliageCell.java index 96a08a10..a676f529 100644 --- a/src/main/java/electrosphere/client/foliagemanager/FoliageCell.java +++ b/src/main/java/electrosphere/client/foliagemanager/FoliageCell.java @@ -90,7 +90,7 @@ public class FoliageCell { instancedActor.setPriority((int)grassPosition.distance(playerPosition)); //draw - instancedActor.draw(Globals.renderingEngine.getRenderPipelineState()); + instancedActor.draw(Globals.renderingEngine.getRenderPipelineState(), new Vector3d(cameraModifiedPosition)); } } } diff --git a/src/main/java/electrosphere/client/instancing/InstanceUpdater.java b/src/main/java/electrosphere/client/instancing/InstanceUpdater.java new file mode 100644 index 00000000..b88fdb1a --- /dev/null +++ b/src/main/java/electrosphere/client/instancing/InstanceUpdater.java @@ -0,0 +1,29 @@ +package electrosphere.client.instancing; + +import org.joml.Vector3d; + +import electrosphere.engine.Globals; +import electrosphere.entity.Entity; +import electrosphere.entity.EntityTags; +import electrosphere.entity.EntityUtils; +import electrosphere.entity.types.camera.CameraEntityUtils; +import electrosphere.renderer.actor.instance.InstancedActor; + +/** + * Used for updating instance priority + */ +public class InstanceUpdater { + + /** + * Updates all instanced actors to have priority based on distance from camera + */ + public static void updateInstancedActorPriority(){ + Vector3d eyePosition = new Vector3d(CameraEntityUtils.getCameraEye(Globals.playerCamera)); + for(Entity entity : Globals.clientScene.getEntitiesWithTag(EntityTags.DRAW_INSTANCED)){ + //set priority equal to distance + Vector3d entityPosition = EntityUtils.getPosition(entity); + InstancedActor.getInstancedActor(entity).setPriority((int)entityPosition.distance(eyePosition)); + } + } + +} diff --git a/src/main/java/electrosphere/client/sim/ClientFunctions.java b/src/main/java/electrosphere/client/sim/ClientFunctions.java index 03d8c2cb..f3cf4097 100644 --- a/src/main/java/electrosphere/client/sim/ClientFunctions.java +++ b/src/main/java/electrosphere/client/sim/ClientFunctions.java @@ -1,5 +1,6 @@ package electrosphere.client.sim; +import electrosphere.client.instancing.InstanceUpdater; import electrosphere.client.terrain.manager.ClientTerrainManager; import electrosphere.engine.Globals; import electrosphere.entity.EntityUtils; @@ -28,6 +29,7 @@ public class ClientFunctions { ClientTerrainManager.generateTerrainChunkGeometry(); updateSkyboxPos(); Globals.clientSceneWrapper.destroyEntitiesOutsideSimRange(); + InstanceUpdater.updateInstancedActorPriority(); // updateCellManager(); } diff --git a/src/main/java/electrosphere/client/sim/ClientSimulation.java b/src/main/java/electrosphere/client/sim/ClientSimulation.java index 18c10927..15fd3620 100644 --- a/src/main/java/electrosphere/client/sim/ClientSimulation.java +++ b/src/main/java/electrosphere/client/sim/ClientSimulation.java @@ -74,10 +74,6 @@ public class ClientSimulation { } //clear collidable impulse lists Globals.clientSceneWrapper.getCollisionEngine().clearCollidableImpulseLists(); - //delete all client side entities that aren't in visible chunks - if(Globals.clientEntityCullingManager != null){ - Globals.clientEntityCullingManager.clearOutOfBoundsEntities(); - } } /** diff --git a/src/main/java/electrosphere/collision/CollisionBodyCreation.java b/src/main/java/electrosphere/collision/CollisionBodyCreation.java index d2319dfd..6c892cc9 100644 --- a/src/main/java/electrosphere/collision/CollisionBodyCreation.java +++ b/src/main/java/electrosphere/collision/CollisionBodyCreation.java @@ -23,7 +23,7 @@ import electrosphere.entity.types.terrain.TerrainChunkData; public class CollisionBodyCreation { //Matrix for correcting initial axis of eg cylinders or capsules - static final DMatrix3 AXIS_CORRECTION_MATRIX = new DMatrix3( + public static final DMatrix3 AXIS_CORRECTION_MATRIX = new DMatrix3( 1.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, -1.0000000, 0.0000000, 1.0000000, 0.0000000 @@ -65,7 +65,7 @@ public class CollisionBodyCreation { public static DBody createCylinderBody(CollisionEngine collisionEngine, double radius, double length){ DCylinder geom = collisionEngine.createCylinderGeom(radius,length); DBody returnBody = collisionEngine.createDBody(geom); - geom.setOffsetRotation(AXIS_CORRECTION_MATRIX); //ode4j required geom to already be on body before rotating for some reason + collisionEngine.setOffsetRotation(geom); //ode4j required geom to already be on body before rotating for some reason return returnBody; } diff --git a/src/main/java/electrosphere/collision/CollisionEngine.java b/src/main/java/electrosphere/collision/CollisionEngine.java index d9a3c2ca..4ff7b220 100644 --- a/src/main/java/electrosphere/collision/CollisionEngine.java +++ b/src/main/java/electrosphere/collision/CollisionEngine.java @@ -671,6 +671,16 @@ public class CollisionEngine { spaceLock.release(); } + /** + * Corrects the initial axis of eg cylinders or capsules + * @param geom the geometry to correct + */ + protected void setOffsetRotation(DGeom geom){ + spaceLock.acquireUninterruptibly(); + geom.setOffsetRotation(CollisionBodyCreation.AXIS_CORRECTION_MATRIX); + spaceLock.release(); + } + /** * Gets the position of the body in a thread-safe way * @param body The body to get the position of diff --git a/src/main/java/electrosphere/renderer/Mesh.java b/src/main/java/electrosphere/renderer/Mesh.java index 8b1ba100..ad8f7cde 100644 --- a/src/main/java/electrosphere/renderer/Mesh.java +++ b/src/main/java/electrosphere/renderer/Mesh.java @@ -25,6 +25,7 @@ import java.util.Map; import org.joml.Matrix4d; import org.joml.Matrix4f; import org.joml.Quaternionf; +import org.joml.Sphered; import org.joml.Vector3d; import org.joml.Vector3f; import org.joml.Vector4d; @@ -113,6 +114,9 @@ public class Mesh { int shaderBoneArrayLocation; Material material; + + //the bounding sphere for this mesh + Sphered boundingSphere; public static Mesh create_mesh_from_aimesh(AIMesh mesh, ModelPretransforms.MeshMetadata metadata){ boolean has_bones = false; @@ -184,6 +188,7 @@ public class Mesh { float x = vertex.x(); float y = vertex.y(); float z = vertex.z(); + //store dimensions of the model if(definedDimensions){ if(x < minX){ minX = x; } if(x > maxX){ maxX = x; } @@ -197,6 +202,12 @@ public class Mesh { minY = maxY = y; minZ = maxZ = z; } + //update bounding sphere + double dist = Math.sqrt(x*x+y*y+z*z); + if(dist > rVal.boundingSphere.r){ + rVal.boundingSphere.r = dist; + } + //store vertex data Vector4d transformedVertex = vertexPretransform.transform(new Vector4d(x,y,z,1.0)); transformedVertex.w = 1.0; temp[0] = (float)transformedVertex.x; @@ -456,7 +467,7 @@ public class Mesh { public Mesh(){ - + this.boundingSphere = new Sphered(); } @@ -745,4 +756,26 @@ public class Mesh { } glBindVertexArray(0); } + + /** + * Updates the bounding sphere of the mesh + * @param x + * @param y + * @param z + * @param r + */ + public void updateBoundingSphere(float x, float y, float z, float r){ + this.boundingSphere.x = x; + this.boundingSphere.y = y; + this.boundingSphere.z = z; + this.boundingSphere.r = r; + } + + /** + * Gets the bounding sphere of this mesh + * @return The bounding sphere + */ + public Sphered getBoundingSphere(){ + return this.boundingSphere; + } } diff --git a/src/main/java/electrosphere/renderer/Model.java b/src/main/java/electrosphere/renderer/Model.java index 33f9a844..287d5834 100644 --- a/src/main/java/electrosphere/renderer/Model.java +++ b/src/main/java/electrosphere/renderer/Model.java @@ -44,6 +44,7 @@ import java.util.LinkedList; import java.util.Map; import java.util.Stack; import org.joml.Quaternionf; +import org.joml.Sphered; import org.joml.Vector3d; import org.lwjgl.assimp.AIAnimation; import org.lwjgl.assimp.AIBone; @@ -71,7 +72,16 @@ public class Model { ActorMeshMask meshMask; Map shaderMask = new HashMap(); Map textureMap = null; + + //The bounding sphere for this particular model + Sphered boundingSphere; + /** + * Loads a model from an ai scene object + * @param path The path of the model + * @param s the ai scene + * @return The model object + */ public static Model createModelFromAiscene(String path, AIScene s){ Model rVal = new Model(); // @@ -100,6 +110,10 @@ public class Model { Mesh currentMesh = Mesh.create_mesh_from_aimesh(aiMesh, meshMetadata); rVal.meshes.add(currentMesh); currentMesh.parent = rVal; + //update model bounding sphere + if(currentMesh.boundingSphere.r > rVal.boundingSphere.r){ + rVal.boundingSphere.r = currentMesh.boundingSphere.r; + } } // //register bones @@ -211,6 +225,7 @@ public class Model { modelMatrix = new Matrix4d(); program = null; globalInverseTransform = null; + this.boundingSphere = new Sphered(); } public void free() { @@ -532,4 +547,20 @@ public class Model { mesh.setMaterial(material); } } + + /** + * Gets the bounding sphere for this model + * @return The bounding sphere + */ + public Sphered getBoundingSphere(){ + return boundingSphere; + } + + /** + * Sets the bounding sphere + * @param boundingSphere The bounding sphere to be stored + */ + public void setBoundingSphere(Sphered boundingSphere){ + this.boundingSphere = boundingSphere; + } } diff --git a/src/main/java/electrosphere/renderer/RenderPipelineState.java b/src/main/java/electrosphere/renderer/RenderPipelineState.java index 9832be9f..c4d88f53 100644 --- a/src/main/java/electrosphere/renderer/RenderPipelineState.java +++ b/src/main/java/electrosphere/renderer/RenderPipelineState.java @@ -1,5 +1,8 @@ package electrosphere.renderer; +import org.joml.FrustumIntersection; +import org.joml.Matrix4f; + import electrosphere.renderer.actor.instance.InstanceData; /** @@ -53,6 +56,9 @@ public class RenderPipelineState { //The pointer to the current shader program bound int currentShaderPointer; + //JOML-provided object to perform frustum culling + FrustumIntersection frustumInt = new FrustumIntersection(); + public boolean getUseMeshShader(){ return this.useMeshShader; } @@ -141,4 +147,24 @@ public class RenderPipelineState { this.currentShaderPointer = currentShaderPointer; } + /** + * Updates the frustum intersection with the provided projection and view matrices + * @param projectionMatrix the projection matrix + * @param viewMatrix the view matrix + */ + public void updateFrustumIntersection(Matrix4f projectionMatrix, Matrix4f viewMatrix){ + Matrix4f projectionViewMatrix = new Matrix4f(); + projectionViewMatrix.set(projectionMatrix); + projectionViewMatrix.mul(viewMatrix); + this.frustumInt.set(projectionViewMatrix); + } + + /** + * Gets the current render pipeline's frustum intersection object + * @return The frustum intersection object + */ + public FrustumIntersection getFrustumIntersection(){ + return frustumInt; + } + } diff --git a/src/main/java/electrosphere/renderer/RenderingEngine.java b/src/main/java/electrosphere/renderer/RenderingEngine.java index 15c00ddf..15c73ae0 100644 --- a/src/main/java/electrosphere/renderer/RenderingEngine.java +++ b/src/main/java/electrosphere/renderer/RenderingEngine.java @@ -65,6 +65,7 @@ import org.joml.Matrix4d; import org.joml.Matrix4f; import org.joml.Quaterniond; import org.joml.Quaternionf; +import org.joml.Sphered; import org.joml.Vector3d; import org.joml.Vector3f; import org.lwjgl.BufferUtils; @@ -182,8 +183,6 @@ public class RenderingEngine { LightManager lightManager; - static float currentViewPlanarAngle; - ShaderProgram activeProgram; static int outputFramebuffer = 0; @@ -192,6 +191,7 @@ public class RenderingEngine { static float aspectRatio = 1.0f; static float verticalFOV = 90.0f; + //the current state of the rendering pipeline static RenderPipelineState renderPipelineState = new RenderPipelineState(); @@ -409,32 +409,22 @@ public class RenderingEngine { return origin.distance(target); } - - static boolean drawPoint(Vector3f cameraPos, Vector3f position){ - boolean rVal = true; - float phi = (float)Math.abs((calculateAngle(cameraPos,position) - currentViewPlanarAngle) % (Math.PI * 2.0f)); - if(phi > Math.PI){ - phi = (float)(Math.PI * 2) - phi; - } - float rotationalDiff = phi; - float dist = calculateDist(new Vector3f(cameraPos.x,0,cameraPos.z), new Vector3f(position.x,0,position.z)); - if(rotationalDiff > (Globals.verticalFOV / 180 * Math.PI) && dist > 300){ - rVal = false; - } - return rVal; - } - - void calculateRenderingAngle(){ - currentViewPlanarAngle = calculateAngle(CameraEntityUtils.getCameraEye(Globals.playerCamera), new Vector3f(0,0,0)); -// System.out.println(currentViewPlanarAngle); + /** + * Updates the frustum box of the render pipeline + */ + void updateFrustumBox(){ + renderPipelineState.updateFrustumIntersection(Globals.projectionMatrix, Globals.viewMatrix); } + /** + * Main function to draw the screen + */ public void drawScreen(){ //calculate render angle for frustum culling if(Globals.RENDER_FLAG_RENDER_SCREEN_FRAMEBUFFER_CONTENT){ - calculateRenderingAngle(); + updateFrustumBox(); } // @@ -581,7 +571,6 @@ public class RenderingEngine { Vector3d position = EntityUtils.getPosition(currentEntity); if( (boolean)currentEntity.getData(EntityDataStrings.DATA_STRING_DRAW) && - drawPoint(cameraPos,new Vector3f((float)position.x,(float)position.y,(float)position.z)) && currentEntity.containsKey(EntityDataStrings.DRAW_CAST_SHADOW) ){ //fetch actor @@ -658,8 +647,7 @@ public class RenderingEngine { Vector3d position = EntityUtils.getPosition(currentEntity); if( (boolean)currentEntity.getData(EntityDataStrings.DATA_STRING_DRAW) && - currentEntity.getData(EntityDataStrings.DRAW_SOLID_PASS) != null && - drawPoint(cameraPos,new Vector3f((float)position.x,(float)position.y,(float)position.z)) + currentEntity.getData(EntityDataStrings.DRAW_SOLID_PASS) != null ){ //fetch actor Actor currentActor = EntityUtils.getActor(currentEntity); @@ -679,8 +667,7 @@ public class RenderingEngine { Vector3d position = EntityUtils.getPosition(currentEntity); if( currentEntity.getData(EntityDataStrings.DATA_STRING_DRAW) != null && - currentEntity.getData(EntityDataStrings.DRAW_SOLID_PASS) != null && - drawPoint(cameraPos,new Vector3f((float)position.x,(float)position.y,(float)position.z)) + currentEntity.getData(EntityDataStrings.DRAW_SOLID_PASS) != null ){ //fetch actor InstancedActor currentActor = InstancedActor.getInstancedActor(currentEntity); @@ -696,15 +683,13 @@ public class RenderingEngine { new Quaternionf((float)rotation.x,(float)rotation.y,(float)rotation.z,(float)rotation.w), new Vector3f(EntityUtils.getScale(currentEntity)) ); - // modelTransformMatrix.translate(cameraModifiedPosition); - // modelTransformMatrix.rotate(rotation); - // modelTransformMatrix.scale(new Vector3d(EntityUtils.getScale(currentEntity))); //set actor value currentActor.setAttribute(modelAttribute, new Matrix4f(modelTransformMatrix)); - // ((Matrix4f)currentActor.getAttributeValue(modelAttribute)).set(modelTransformMatrix); + //draw + currentActor.draw(renderPipelineState, new Vector3d(cameraModifiedPosition)); + } else { + currentActor.draw(renderPipelineState); } - //draw - currentActor.draw(renderPipelineState); } } //draw all instanced models @@ -747,8 +732,7 @@ public class RenderingEngine { Vector3d position = EntityUtils.getPosition(currentEntity); if( (boolean)currentEntity.getData(EntityDataStrings.DATA_STRING_DRAW) && - currentEntity.getData(EntityDataStrings.DRAW_TRANSPARENT_PASS) != null && - drawPoint(cameraPos,new Vector3f((float)position.x,(float)position.y,(float)position.z)) + currentEntity.getData(EntityDataStrings.DRAW_TRANSPARENT_PASS) != null ){ //fetch actor Actor currentActor = EntityUtils.getActor(currentEntity); @@ -768,13 +752,27 @@ public class RenderingEngine { Vector3d position = EntityUtils.getPosition(currentEntity); if( currentEntity.getData(EntityDataStrings.DATA_STRING_DRAW) != null && - currentEntity.getData(EntityDataStrings.DRAW_TRANSPARENT_PASS) != null && - drawPoint(cameraPos,new Vector3f((float)position.x,(float)position.y,(float)position.z)) + currentEntity.getData(EntityDataStrings.DRAW_TRANSPARENT_PASS) != null ){ //fetch actor InstancedActor currentActor = InstancedActor.getInstancedActor(currentEntity); - //draw - if(currentActor != null){ + //if the shader attribute for model matrix exists, calculate the model matrix and apply + if(InstancedActor.getInstanceModelAttribute(currentEntity) != null){ + ShaderAttribute modelAttribute = InstancedActor.getInstanceModelAttribute(currentEntity); + //calculate model matrix + Vector3f cameraModifiedPosition = new Vector3f((float)position.x,(float)position.y,(float)position.z).sub(CameraEntityUtils.getCameraCenter(Globals.playerCamera)); + Quaterniond rotation = EntityUtils.getRotation(currentEntity); + // modelTransformMatrix.identity(); + modelTransformMatrix.identity().translationRotateScale( + cameraModifiedPosition, + new Quaternionf((float)rotation.x,(float)rotation.y,(float)rotation.z,(float)rotation.w), + new Vector3f(EntityUtils.getScale(currentEntity)) + ); + //set actor value + currentActor.setAttribute(modelAttribute, new Matrix4f(modelTransformMatrix)); + //draw + currentActor.draw(renderPipelineState, new Vector3d(cameraModifiedPosition)); + } else { currentActor.draw(renderPipelineState); } } @@ -833,8 +831,7 @@ public class RenderingEngine { for(Entity currentEntity : Globals.clientScene.getEntitiesWithTag(EntityTags.DRAWABLE)){ Vector3d position = EntityUtils.getPosition(currentEntity); if( - (boolean)currentEntity.getData(EntityDataStrings.DATA_STRING_DRAW) && - drawPoint(cameraPos,new Vector3f((float)position.x,(float)position.y,(float)position.z)) + (boolean)currentEntity.getData(EntityDataStrings.DATA_STRING_DRAW) ){ //fetch actor Actor currentActor = EntityUtils.getActor(currentEntity); @@ -1080,8 +1077,7 @@ public class RenderingEngine { if( (boolean)currentEntity.getData(EntityDataStrings.DATA_STRING_DRAW) && currentEntity.getData(EntityDataStrings.DRAW_SOLID_PASS) != null && - currentEntity.getData(EntityDataStrings.DRAW_OUTLINE) != null && - drawPoint(cameraPos,new Vector3f((float)position.x,(float)position.y,(float)position.z)) + currentEntity.getData(EntityDataStrings.DRAW_OUTLINE) != null ){ //fetch actor Actor currentActor = EntityUtils.getActor(currentEntity); @@ -1334,7 +1330,6 @@ public class RenderingEngine { Vector3d position = EntityUtils.getPosition(currentEntity); if( (boolean)currentEntity.getData(EntityDataStrings.DATA_STRING_DRAW) && - drawPoint(cameraPos,new Vector3f((float)position.x,(float)position.y,(float)position.z)) && currentEntity.containsKey(EntityDataStrings.DRAW_VOLUMETRIC) ){ //fetch actor @@ -1372,7 +1367,6 @@ public class RenderingEngine { Vector3d position = EntityUtils.getPosition(currentEntity); if( (boolean)currentEntity.getData(EntityDataStrings.DATA_STRING_DRAW) && - drawPoint(cameraPos,new Vector3f((float)position.x,(float)position.y,(float)position.z)) && !currentEntity.containsKey(EntityDataStrings.DRAW_VOLUMETRIC) ){ //fetch actor @@ -1421,7 +1415,6 @@ public class RenderingEngine { Vector3d position = EntityUtils.getPosition(currentEntity); if( (boolean)currentEntity.getData(EntityDataStrings.DATA_STRING_DRAW) && - drawPoint(cameraPos,new Vector3f((float)position.x,(float)position.y,(float)position.z)) && currentEntity.containsKey(EntityDataStrings.DRAW_VOLUMETRIC) ){ //fetch actor diff --git a/src/main/java/electrosphere/renderer/actor/Actor.java b/src/main/java/electrosphere/renderer/actor/Actor.java index aa3be23c..9ae9d554 100644 --- a/src/main/java/electrosphere/renderer/actor/Actor.java +++ b/src/main/java/electrosphere/renderer/actor/Actor.java @@ -17,23 +17,50 @@ import org.joml.Matrix4d; import org.joml.Matrix4f; import org.joml.Quaterniond; import org.joml.Quaternionf; +import org.joml.Sphered; +import org.joml.Vector3d; import org.joml.Vector3f; import org.joml.Vector4d; import org.joml.Vector4f; /** - * - * @author amaterasu + * An actor + * Container for a model that keeps track of: + * - Animation state + * - Mesh overrides + * - Shader overrides + * - Texture overrides + * - Bone overrides + * - Static Morphs (think scaling something, but from creatures.json) */ public class Actor { + + //the model path of the model backing the actor String modelPath; + + //used for statically binding textures in pipelines that dont bind textures on a per-mesh level + //ie when in the ui, this can be set to bind a texture at the actor level String textureOverride; + + //scales the time that animations are played at float animationScalar = 1.0f; + + //The stack of animations being applied to a given actor PriorityQueue animationQueue = new PriorityQueue(); + + //Mask for overwriting meshes in a given actor ActorMeshMask meshMask = new ActorMeshMask(); + + //optional overrides for specific shaders List shaderMasks = new LinkedList(); + + //optional overrides for textures Map textureMap = null; + + //bone rotators Map boneRotators = new HashMap(); + + //static morph for this specific actor ActorStaticMorph staticMorph; public Actor(String modelPath){ @@ -52,18 +79,6 @@ public class Actor { for(ActorAnimationMask mask : toRemoveMasks){ animationQueue.remove(mask); } - // if(playingAnimation){ - // animationTime = animationTime + deltaTime * animationScalar; - // } - // if(model != null){ - // if(animation != null){ - // model.playAnimation(animation); - // model.incrementTime(animationTime); - // if(model.currentAnimation == null){ - // playingAnimation = false; - // } - // } - // } } public double getAnimationTime(String animationName){ @@ -100,10 +115,6 @@ public class Actor { } } - // public String getCurrentAnimation(){ - // return animation; - // } - public void playAnimation(String animationName, int priority){ // animationTime = 0; // playingAnimation = true; @@ -170,10 +181,6 @@ public class Actor { model.updateNodeTransform(boneRotators,staticMorph); } - // public boolean isPlayingAnimation(){ - // return playingAnimation; - // } - public void setAnimationScalar(float animationScalar) { this.animationScalar = animationScalar; } @@ -185,10 +192,14 @@ public class Actor { } } + /** + * Draws an actor + * @param renderPipelineState The render pipeline state to draw within + */ public void draw(RenderPipelineState renderPipelineState){ Model model = Globals.assetManager.fetchModel(modelPath); boolean hasDrawn = false; - if(model != null){ + if(model != null && isWithinFrustumBox(renderPipelineState,model)){ applyAnimationMasks(model); meshMask.processMeshMaskQueue(); model.setMeshMask(meshMask); @@ -353,5 +364,17 @@ public class Actor { return this.staticMorph; } + /** + * Checks if a given model is within the render pipeline state's frustum box + * @param renderPipelineState The render pipeline state + * @param model The model + * @return true if it is within the box, false otherwise + */ + static boolean isWithinFrustumBox(RenderPipelineState renderPipelineState, Model model){ + Sphered sphere = model.getBoundingSphere(); + Vector3d modelPosition = model.modelMatrix.getTranslation(new Vector3d()); + return renderPipelineState.getFrustumIntersection().testSphere((float)(sphere.x + modelPosition.x), (float)(sphere.y + modelPosition.y), (float)(sphere.z + modelPosition.z), (float)sphere.r); + } + } diff --git a/src/main/java/electrosphere/renderer/actor/ActorBoneRotator.java b/src/main/java/electrosphere/renderer/actor/ActorBoneRotator.java index 8932ca7c..7697bad4 100644 --- a/src/main/java/electrosphere/renderer/actor/ActorBoneRotator.java +++ b/src/main/java/electrosphere/renderer/actor/ActorBoneRotator.java @@ -2,6 +2,10 @@ package electrosphere.renderer.actor; import org.joml.Quaternionf; +/** + * Optional rotation to be applied to a bone that can be programmatically controlled and applied alongside another animation. + * For instance, having a character look at something, turn their torso mid animation, or hair rotating around. + */ public class ActorBoneRotator { Quaternionf rotation = new Quaternionf().identity(); diff --git a/src/main/java/electrosphere/renderer/actor/ActorStaticMorph.java b/src/main/java/electrosphere/renderer/actor/ActorStaticMorph.java index 7484ce2f..3056d780 100644 --- a/src/main/java/electrosphere/renderer/actor/ActorStaticMorph.java +++ b/src/main/java/electrosphere/renderer/actor/ActorStaticMorph.java @@ -7,6 +7,10 @@ import org.joml.Matrix4f; import org.joml.Quaternionf; import org.joml.Vector3f; +/** + * A static morph that is applied to an actor if it is defined in a pipeline creating an actor. + * For instance, if you provide a static morph for the model in an item in creatures.json, this is the structure that will actually hold that data + */ public class ActorStaticMorph { Map boneTransformMap = new HashMap(); diff --git a/src/main/java/electrosphere/renderer/actor/instance/InstanceData.java b/src/main/java/electrosphere/renderer/actor/instance/InstanceData.java index 619c86af..afb9a788 100644 --- a/src/main/java/electrosphere/renderer/actor/instance/InstanceData.java +++ b/src/main/java/electrosphere/renderer/actor/instance/InstanceData.java @@ -3,11 +3,15 @@ package electrosphere.renderer.actor.instance; import java.nio.DoubleBuffer; import java.nio.FloatBuffer; import java.nio.IntBuffer; +import java.util.Comparator; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.NavigableSet; import java.util.PriorityQueue; +import java.util.TreeSet; +import java.util.concurrent.ConcurrentSkipListSet; import org.joml.Matrix4d; import org.joml.Matrix4f; @@ -37,8 +41,8 @@ public class InstanceData { //the number of draw calls since the last clear operation int drawCalls = 0; - //The priority queue of instanced actors to draw - PriorityQueue actorQueue = null; + //The set of instanced actors to draw + List actorQueue = null; //Map of actor to index in the buffers that are emitted Map actorIndexMap = new HashMap(); //Map of index -> actor used for buffer evictions @@ -60,7 +64,7 @@ public class InstanceData { this.capacity = capacity; this.vertexShaderPath = vertexPath; this.fragmentShaderPath = fragmentPath; - actorQueue = new PriorityQueue(this.capacity); + actorQueue = new LinkedList(); } /** @@ -179,6 +183,7 @@ public class InstanceData { } break; } } + actorQueue.sort(Comparator.naturalOrder()); //buffer data for(InstancedActor actor : actorQueue){ //push values to attribute buffers diff --git a/src/main/java/electrosphere/renderer/actor/instance/InstancedActor.java b/src/main/java/electrosphere/renderer/actor/instance/InstancedActor.java index 70ca5fa0..ae737393 100644 --- a/src/main/java/electrosphere/renderer/actor/instance/InstancedActor.java +++ b/src/main/java/electrosphere/renderer/actor/instance/InstancedActor.java @@ -6,11 +6,14 @@ import java.util.Map; import org.joml.Matrix4d; import org.joml.Matrix4f; +import org.joml.Sphered; +import org.joml.Vector3d; import org.joml.Vector3f; import electrosphere.engine.Globals; import electrosphere.entity.Entity; import electrosphere.entity.EntityDataStrings; +import electrosphere.entity.EntityUtils; import electrosphere.logger.LoggerInterface; import electrosphere.renderer.Model; import electrosphere.renderer.RenderPipelineState; @@ -41,6 +44,23 @@ public class InstancedActor implements Comparable { /** * Draws the instanced actor. Should be called normally in a loop as if this was a regular actor. * @param renderPipelineState The pipeline state of the instanced actor + * @param position The position used for frustum checking + */ + public void draw(RenderPipelineState renderPipelineState, Vector3d position){ + Model model = Globals.assetManager.fetchModel(modelPath); + if(model != null){ + Sphered boundingSphere = model.getBoundingSphere(); + //frustum check if the model matrix exists (and we therefore can get position) + boolean frustumCheck = renderPipelineState.getFrustumIntersection().testSphere((float)(position.x + boundingSphere.x), (float)(position.y + boundingSphere.y), (float)(position.z + boundingSphere.z), (float)boundingSphere.r); + if(frustumCheck){ + Globals.clientInstanceManager.addToQueue(this); + } + } + } + + /** + * Draws the instanced actor WITHOUT frustum checking. Otherwise identical to the call with frustum checking + * @param renderPipelineState */ public void draw(RenderPipelineState renderPipelineState){ Model model = Globals.assetManager.fetchModel(modelPath); diff --git a/src/main/java/electrosphere/renderer/meshgen/TerrainChunkModelGeneration.java b/src/main/java/electrosphere/renderer/meshgen/TerrainChunkModelGeneration.java index 037dfd33..51af068a 100644 --- a/src/main/java/electrosphere/renderer/meshgen/TerrainChunkModelGeneration.java +++ b/src/main/java/electrosphere/renderer/meshgen/TerrainChunkModelGeneration.java @@ -25,6 +25,7 @@ import electrosphere.renderer.Material; import electrosphere.renderer.Mesh; import electrosphere.renderer.Model; import electrosphere.renderer.ShaderProgram; +import electrosphere.server.terrain.manager.ServerTerrainChunk; public class TerrainChunkModelGeneration { @@ -775,6 +776,13 @@ public class TerrainChunkModelGeneration { ex.printStackTrace(); } + float halfChunk = ServerTerrainChunk.CHUNK_DIMENSION / 2.0f; + mesh.updateBoundingSphere( + halfChunk, + halfChunk, + halfChunk, + (float)Math.sqrt(halfChunk * halfChunk + halfChunk * halfChunk + halfChunk * halfChunk) + ); @@ -805,6 +813,7 @@ public class TerrainChunkModelGeneration { m.parent = rVal; rVal.meshes.add(m); + rVal.setBoundingSphere(m.getBoundingSphere()); return rVal; }