Proper frustum culling
All checks were successful
studiorailgun/Renderer/pipeline/head This commit looks good

This commit is contained in:
austin 2024-02-29 19:14:17 -05:00
parent 1883ffddf3
commit 5356b3e325
24 changed files with 303 additions and 81 deletions

View File

@ -1,3 +1,3 @@
#maven.buildNumber.plugin properties file #maven.buildNumber.plugin properties file
#Thu Feb 29 11:18:06 EST 2024 #Thu Feb 29 18:57:13 EST 2024
buildNumber=29 buildNumber=30

View File

@ -0,0 +1,9 @@
@page actorsindex Actors
[TOC]
- @subpage texturemask
- @subpage bonerotators
- @subpage staticmorph
- @subpage shadermask
- @subpage animationmask
- @subpage meshmask

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,5 @@
@page meshmask Mesh Mask
TODO
use example: overwriting a mesh without clothing to one with clothing

View File

@ -0,0 +1,5 @@
@page shadermask Shader Mask
TODO
use example: override default cube shader to use a shader that does raymarching

View File

@ -0,0 +1,5 @@
@page staticmorph Actor Static Morph
TODO
use example: scale something that was exported from blender waayyyyyy too large

View File

@ -0,0 +1,3 @@
@page texturemask Actor Texture Mask
TODO

View File

@ -90,7 +90,7 @@ public class FoliageCell {
instancedActor.setPriority((int)grassPosition.distance(playerPosition)); instancedActor.setPriority((int)grassPosition.distance(playerPosition));
//draw //draw
instancedActor.draw(Globals.renderingEngine.getRenderPipelineState()); instancedActor.draw(Globals.renderingEngine.getRenderPipelineState(), new Vector3d(cameraModifiedPosition));
} }
} }
} }

View File

@ -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));
}
}
}

View File

@ -1,5 +1,6 @@
package electrosphere.client.sim; package electrosphere.client.sim;
import electrosphere.client.instancing.InstanceUpdater;
import electrosphere.client.terrain.manager.ClientTerrainManager; import electrosphere.client.terrain.manager.ClientTerrainManager;
import electrosphere.engine.Globals; import electrosphere.engine.Globals;
import electrosphere.entity.EntityUtils; import electrosphere.entity.EntityUtils;
@ -28,6 +29,7 @@ public class ClientFunctions {
ClientTerrainManager.generateTerrainChunkGeometry(); ClientTerrainManager.generateTerrainChunkGeometry();
updateSkyboxPos(); updateSkyboxPos();
Globals.clientSceneWrapper.destroyEntitiesOutsideSimRange(); Globals.clientSceneWrapper.destroyEntitiesOutsideSimRange();
InstanceUpdater.updateInstancedActorPriority();
// updateCellManager(); // updateCellManager();
} }

View File

@ -74,10 +74,6 @@ public class ClientSimulation {
} }
//clear collidable impulse lists //clear collidable impulse lists
Globals.clientSceneWrapper.getCollisionEngine().clearCollidableImpulseLists(); Globals.clientSceneWrapper.getCollisionEngine().clearCollidableImpulseLists();
//delete all client side entities that aren't in visible chunks
if(Globals.clientEntityCullingManager != null){
Globals.clientEntityCullingManager.clearOutOfBoundsEntities();
}
} }
/** /**

View File

@ -23,7 +23,7 @@ import electrosphere.entity.types.terrain.TerrainChunkData;
public class CollisionBodyCreation { public class CollisionBodyCreation {
//Matrix for correcting initial axis of eg cylinders or capsules //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, 1.0000000, 0.0000000, 0.0000000,
0.0000000, 0.0000000, -1.0000000, 0.0000000, 0.0000000, -1.0000000,
0.0000000, 1.0000000, 0.0000000 0.0000000, 1.0000000, 0.0000000
@ -65,7 +65,7 @@ public class CollisionBodyCreation {
public static DBody createCylinderBody(CollisionEngine collisionEngine, double radius, double length){ public static DBody createCylinderBody(CollisionEngine collisionEngine, double radius, double length){
DCylinder geom = collisionEngine.createCylinderGeom(radius,length); DCylinder geom = collisionEngine.createCylinderGeom(radius,length);
DBody returnBody = collisionEngine.createDBody(geom); 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; return returnBody;
} }

View File

@ -671,6 +671,16 @@ public class CollisionEngine {
spaceLock.release(); 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 * Gets the position of the body in a thread-safe way
* @param body The body to get the position of * @param body The body to get the position of

View File

@ -25,6 +25,7 @@ import java.util.Map;
import org.joml.Matrix4d; import org.joml.Matrix4d;
import org.joml.Matrix4f; import org.joml.Matrix4f;
import org.joml.Quaternionf; import org.joml.Quaternionf;
import org.joml.Sphered;
import org.joml.Vector3d; import org.joml.Vector3d;
import org.joml.Vector3f; import org.joml.Vector3f;
import org.joml.Vector4d; import org.joml.Vector4d;
@ -114,6 +115,9 @@ public class Mesh {
Material material; Material material;
//the bounding sphere for this mesh
Sphered boundingSphere;
public static Mesh create_mesh_from_aimesh(AIMesh mesh, ModelPretransforms.MeshMetadata metadata){ public static Mesh create_mesh_from_aimesh(AIMesh mesh, ModelPretransforms.MeshMetadata metadata){
boolean has_bones = false; boolean has_bones = false;
boolean apply_lighting = true; boolean apply_lighting = true;
@ -184,6 +188,7 @@ public class Mesh {
float x = vertex.x(); float x = vertex.x();
float y = vertex.y(); float y = vertex.y();
float z = vertex.z(); float z = vertex.z();
//store dimensions of the model
if(definedDimensions){ if(definedDimensions){
if(x < minX){ minX = x; } if(x < minX){ minX = x; }
if(x > maxX){ maxX = x; } if(x > maxX){ maxX = x; }
@ -197,6 +202,12 @@ public class Mesh {
minY = maxY = y; minY = maxY = y;
minZ = maxZ = z; 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)); Vector4d transformedVertex = vertexPretransform.transform(new Vector4d(x,y,z,1.0));
transformedVertex.w = 1.0; transformedVertex.w = 1.0;
temp[0] = (float)transformedVertex.x; temp[0] = (float)transformedVertex.x;
@ -456,7 +467,7 @@ public class Mesh {
public Mesh(){ public Mesh(){
this.boundingSphere = new Sphered();
} }
@ -745,4 +756,26 @@ public class Mesh {
} }
glBindVertexArray(0); 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;
}
} }

View File

@ -44,6 +44,7 @@ import java.util.LinkedList;
import java.util.Map; import java.util.Map;
import java.util.Stack; import java.util.Stack;
import org.joml.Quaternionf; import org.joml.Quaternionf;
import org.joml.Sphered;
import org.joml.Vector3d; import org.joml.Vector3d;
import org.lwjgl.assimp.AIAnimation; import org.lwjgl.assimp.AIAnimation;
import org.lwjgl.assimp.AIBone; import org.lwjgl.assimp.AIBone;
@ -72,6 +73,15 @@ public class Model {
Map<String,ActorShaderMask> shaderMask = new HashMap<String,ActorShaderMask>(); Map<String,ActorShaderMask> shaderMask = new HashMap<String,ActorShaderMask>();
Map<String,ActorTextureMask> textureMap = null; Map<String,ActorTextureMask> 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){ public static Model createModelFromAiscene(String path, AIScene s){
Model rVal = new Model(); Model rVal = new Model();
// //
@ -100,6 +110,10 @@ public class Model {
Mesh currentMesh = Mesh.create_mesh_from_aimesh(aiMesh, meshMetadata); Mesh currentMesh = Mesh.create_mesh_from_aimesh(aiMesh, meshMetadata);
rVal.meshes.add(currentMesh); rVal.meshes.add(currentMesh);
currentMesh.parent = rVal; currentMesh.parent = rVal;
//update model bounding sphere
if(currentMesh.boundingSphere.r > rVal.boundingSphere.r){
rVal.boundingSphere.r = currentMesh.boundingSphere.r;
}
} }
// //
//register bones //register bones
@ -211,6 +225,7 @@ public class Model {
modelMatrix = new Matrix4d(); modelMatrix = new Matrix4d();
program = null; program = null;
globalInverseTransform = null; globalInverseTransform = null;
this.boundingSphere = new Sphered();
} }
public void free() { public void free() {
@ -532,4 +547,20 @@ public class Model {
mesh.setMaterial(material); 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;
}
} }

View File

@ -1,5 +1,8 @@
package electrosphere.renderer; package electrosphere.renderer;
import org.joml.FrustumIntersection;
import org.joml.Matrix4f;
import electrosphere.renderer.actor.instance.InstanceData; import electrosphere.renderer.actor.instance.InstanceData;
/** /**
@ -53,6 +56,9 @@ public class RenderPipelineState {
//The pointer to the current shader program bound //The pointer to the current shader program bound
int currentShaderPointer; int currentShaderPointer;
//JOML-provided object to perform frustum culling
FrustumIntersection frustumInt = new FrustumIntersection();
public boolean getUseMeshShader(){ public boolean getUseMeshShader(){
return this.useMeshShader; return this.useMeshShader;
} }
@ -141,4 +147,24 @@ public class RenderPipelineState {
this.currentShaderPointer = currentShaderPointer; 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;
}
} }

View File

@ -65,6 +65,7 @@ import org.joml.Matrix4d;
import org.joml.Matrix4f; import org.joml.Matrix4f;
import org.joml.Quaterniond; import org.joml.Quaterniond;
import org.joml.Quaternionf; import org.joml.Quaternionf;
import org.joml.Sphered;
import org.joml.Vector3d; import org.joml.Vector3d;
import org.joml.Vector3f; import org.joml.Vector3f;
import org.lwjgl.BufferUtils; import org.lwjgl.BufferUtils;
@ -182,8 +183,6 @@ public class RenderingEngine {
LightManager lightManager; LightManager lightManager;
static float currentViewPlanarAngle;
ShaderProgram activeProgram; ShaderProgram activeProgram;
static int outputFramebuffer = 0; static int outputFramebuffer = 0;
@ -192,6 +191,7 @@ public class RenderingEngine {
static float aspectRatio = 1.0f; static float aspectRatio = 1.0f;
static float verticalFOV = 90.0f; static float verticalFOV = 90.0f;
//the current state of the rendering pipeline
static RenderPipelineState renderPipelineState = new RenderPipelineState(); static RenderPipelineState renderPipelineState = new RenderPipelineState();
@ -409,32 +409,22 @@ public class RenderingEngine {
return origin.distance(target); return origin.distance(target);
} }
/**
static boolean drawPoint(Vector3f cameraPos, Vector3f position){ * Updates the frustum box of the render pipeline
boolean rVal = true; */
float phi = (float)Math.abs((calculateAngle(cameraPos,position) - currentViewPlanarAngle) % (Math.PI * 2.0f)); void updateFrustumBox(){
if(phi > Math.PI){ renderPipelineState.updateFrustumIntersection(Globals.projectionMatrix, Globals.viewMatrix);
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);
} }
/**
* Main function to draw the screen
*/
public void drawScreen(){ public void drawScreen(){
//calculate render angle for frustum culling //calculate render angle for frustum culling
if(Globals.RENDER_FLAG_RENDER_SCREEN_FRAMEBUFFER_CONTENT){ if(Globals.RENDER_FLAG_RENDER_SCREEN_FRAMEBUFFER_CONTENT){
calculateRenderingAngle(); updateFrustumBox();
} }
// //
@ -581,7 +571,6 @@ public class RenderingEngine {
Vector3d position = EntityUtils.getPosition(currentEntity); Vector3d position = EntityUtils.getPosition(currentEntity);
if( if(
(boolean)currentEntity.getData(EntityDataStrings.DATA_STRING_DRAW) && (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) currentEntity.containsKey(EntityDataStrings.DRAW_CAST_SHADOW)
){ ){
//fetch actor //fetch actor
@ -658,8 +647,7 @@ public class RenderingEngine {
Vector3d position = EntityUtils.getPosition(currentEntity); Vector3d position = EntityUtils.getPosition(currentEntity);
if( if(
(boolean)currentEntity.getData(EntityDataStrings.DATA_STRING_DRAW) && (boolean)currentEntity.getData(EntityDataStrings.DATA_STRING_DRAW) &&
currentEntity.getData(EntityDataStrings.DRAW_SOLID_PASS) != null && currentEntity.getData(EntityDataStrings.DRAW_SOLID_PASS) != null
drawPoint(cameraPos,new Vector3f((float)position.x,(float)position.y,(float)position.z))
){ ){
//fetch actor //fetch actor
Actor currentActor = EntityUtils.getActor(currentEntity); Actor currentActor = EntityUtils.getActor(currentEntity);
@ -679,8 +667,7 @@ public class RenderingEngine {
Vector3d position = EntityUtils.getPosition(currentEntity); Vector3d position = EntityUtils.getPosition(currentEntity);
if( if(
currentEntity.getData(EntityDataStrings.DATA_STRING_DRAW) != null && currentEntity.getData(EntityDataStrings.DATA_STRING_DRAW) != null &&
currentEntity.getData(EntityDataStrings.DRAW_SOLID_PASS) != null && currentEntity.getData(EntityDataStrings.DRAW_SOLID_PASS) != null
drawPoint(cameraPos,new Vector3f((float)position.x,(float)position.y,(float)position.z))
){ ){
//fetch actor //fetch actor
InstancedActor currentActor = InstancedActor.getInstancedActor(currentEntity); 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 Quaternionf((float)rotation.x,(float)rotation.y,(float)rotation.z,(float)rotation.w),
new Vector3f(EntityUtils.getScale(currentEntity)) new Vector3f(EntityUtils.getScale(currentEntity))
); );
// modelTransformMatrix.translate(cameraModifiedPosition);
// modelTransformMatrix.rotate(rotation);
// modelTransformMatrix.scale(new Vector3d(EntityUtils.getScale(currentEntity)));
//set actor value //set actor value
currentActor.setAttribute(modelAttribute, new Matrix4f(modelTransformMatrix)); 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 //draw all instanced models
@ -747,8 +732,7 @@ public class RenderingEngine {
Vector3d position = EntityUtils.getPosition(currentEntity); Vector3d position = EntityUtils.getPosition(currentEntity);
if( if(
(boolean)currentEntity.getData(EntityDataStrings.DATA_STRING_DRAW) && (boolean)currentEntity.getData(EntityDataStrings.DATA_STRING_DRAW) &&
currentEntity.getData(EntityDataStrings.DRAW_TRANSPARENT_PASS) != null && currentEntity.getData(EntityDataStrings.DRAW_TRANSPARENT_PASS) != null
drawPoint(cameraPos,new Vector3f((float)position.x,(float)position.y,(float)position.z))
){ ){
//fetch actor //fetch actor
Actor currentActor = EntityUtils.getActor(currentEntity); Actor currentActor = EntityUtils.getActor(currentEntity);
@ -768,13 +752,27 @@ public class RenderingEngine {
Vector3d position = EntityUtils.getPosition(currentEntity); Vector3d position = EntityUtils.getPosition(currentEntity);
if( if(
currentEntity.getData(EntityDataStrings.DATA_STRING_DRAW) != null && currentEntity.getData(EntityDataStrings.DATA_STRING_DRAW) != null &&
currentEntity.getData(EntityDataStrings.DRAW_TRANSPARENT_PASS) != null && currentEntity.getData(EntityDataStrings.DRAW_TRANSPARENT_PASS) != null
drawPoint(cameraPos,new Vector3f((float)position.x,(float)position.y,(float)position.z))
){ ){
//fetch actor //fetch actor
InstancedActor currentActor = InstancedActor.getInstancedActor(currentEntity); InstancedActor currentActor = InstancedActor.getInstancedActor(currentEntity);
//draw //if the shader attribute for model matrix exists, calculate the model matrix and apply
if(currentActor != null){ 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); currentActor.draw(renderPipelineState);
} }
} }
@ -833,8 +831,7 @@ public class RenderingEngine {
for(Entity currentEntity : Globals.clientScene.getEntitiesWithTag(EntityTags.DRAWABLE)){ for(Entity currentEntity : Globals.clientScene.getEntitiesWithTag(EntityTags.DRAWABLE)){
Vector3d position = EntityUtils.getPosition(currentEntity); Vector3d position = EntityUtils.getPosition(currentEntity);
if( if(
(boolean)currentEntity.getData(EntityDataStrings.DATA_STRING_DRAW) && (boolean)currentEntity.getData(EntityDataStrings.DATA_STRING_DRAW)
drawPoint(cameraPos,new Vector3f((float)position.x,(float)position.y,(float)position.z))
){ ){
//fetch actor //fetch actor
Actor currentActor = EntityUtils.getActor(currentEntity); Actor currentActor = EntityUtils.getActor(currentEntity);
@ -1080,8 +1077,7 @@ public class RenderingEngine {
if( if(
(boolean)currentEntity.getData(EntityDataStrings.DATA_STRING_DRAW) && (boolean)currentEntity.getData(EntityDataStrings.DATA_STRING_DRAW) &&
currentEntity.getData(EntityDataStrings.DRAW_SOLID_PASS) != null && currentEntity.getData(EntityDataStrings.DRAW_SOLID_PASS) != null &&
currentEntity.getData(EntityDataStrings.DRAW_OUTLINE) != null && currentEntity.getData(EntityDataStrings.DRAW_OUTLINE) != null
drawPoint(cameraPos,new Vector3f((float)position.x,(float)position.y,(float)position.z))
){ ){
//fetch actor //fetch actor
Actor currentActor = EntityUtils.getActor(currentEntity); Actor currentActor = EntityUtils.getActor(currentEntity);
@ -1334,7 +1330,6 @@ public class RenderingEngine {
Vector3d position = EntityUtils.getPosition(currentEntity); Vector3d position = EntityUtils.getPosition(currentEntity);
if( if(
(boolean)currentEntity.getData(EntityDataStrings.DATA_STRING_DRAW) && (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) currentEntity.containsKey(EntityDataStrings.DRAW_VOLUMETRIC)
){ ){
//fetch actor //fetch actor
@ -1372,7 +1367,6 @@ public class RenderingEngine {
Vector3d position = EntityUtils.getPosition(currentEntity); Vector3d position = EntityUtils.getPosition(currentEntity);
if( if(
(boolean)currentEntity.getData(EntityDataStrings.DATA_STRING_DRAW) && (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) !currentEntity.containsKey(EntityDataStrings.DRAW_VOLUMETRIC)
){ ){
//fetch actor //fetch actor
@ -1421,7 +1415,6 @@ public class RenderingEngine {
Vector3d position = EntityUtils.getPosition(currentEntity); Vector3d position = EntityUtils.getPosition(currentEntity);
if( if(
(boolean)currentEntity.getData(EntityDataStrings.DATA_STRING_DRAW) && (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) currentEntity.containsKey(EntityDataStrings.DRAW_VOLUMETRIC)
){ ){
//fetch actor //fetch actor

View File

@ -17,23 +17,50 @@ import org.joml.Matrix4d;
import org.joml.Matrix4f; import org.joml.Matrix4f;
import org.joml.Quaterniond; import org.joml.Quaterniond;
import org.joml.Quaternionf; import org.joml.Quaternionf;
import org.joml.Sphered;
import org.joml.Vector3d;
import org.joml.Vector3f; import org.joml.Vector3f;
import org.joml.Vector4d; import org.joml.Vector4d;
import org.joml.Vector4f; import org.joml.Vector4f;
/** /**
* * An actor
* @author amaterasu * 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 { public class Actor {
//the model path of the model backing the actor
String modelPath; 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; String textureOverride;
//scales the time that animations are played at
float animationScalar = 1.0f; float animationScalar = 1.0f;
//The stack of animations being applied to a given actor
PriorityQueue<ActorAnimationMask> animationQueue = new PriorityQueue<ActorAnimationMask>(); PriorityQueue<ActorAnimationMask> animationQueue = new PriorityQueue<ActorAnimationMask>();
//Mask for overwriting meshes in a given actor
ActorMeshMask meshMask = new ActorMeshMask(); ActorMeshMask meshMask = new ActorMeshMask();
//optional overrides for specific shaders
List<ActorShaderMask> shaderMasks = new LinkedList<ActorShaderMask>(); List<ActorShaderMask> shaderMasks = new LinkedList<ActorShaderMask>();
//optional overrides for textures
Map<String,ActorTextureMask> textureMap = null; Map<String,ActorTextureMask> textureMap = null;
//bone rotators
Map<String,ActorBoneRotator> boneRotators = new HashMap<String,ActorBoneRotator>(); Map<String,ActorBoneRotator> boneRotators = new HashMap<String,ActorBoneRotator>();
//static morph for this specific actor
ActorStaticMorph staticMorph; ActorStaticMorph staticMorph;
public Actor(String modelPath){ public Actor(String modelPath){
@ -52,18 +79,6 @@ public class Actor {
for(ActorAnimationMask mask : toRemoveMasks){ for(ActorAnimationMask mask : toRemoveMasks){
animationQueue.remove(mask); 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){ public double getAnimationTime(String animationName){
@ -100,10 +115,6 @@ public class Actor {
} }
} }
// public String getCurrentAnimation(){
// return animation;
// }
public void playAnimation(String animationName, int priority){ public void playAnimation(String animationName, int priority){
// animationTime = 0; // animationTime = 0;
// playingAnimation = true; // playingAnimation = true;
@ -170,10 +181,6 @@ public class Actor {
model.updateNodeTransform(boneRotators,staticMorph); model.updateNodeTransform(boneRotators,staticMorph);
} }
// public boolean isPlayingAnimation(){
// return playingAnimation;
// }
public void setAnimationScalar(float animationScalar) { public void setAnimationScalar(float animationScalar) {
this.animationScalar = 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){ public void draw(RenderPipelineState renderPipelineState){
Model model = Globals.assetManager.fetchModel(modelPath); Model model = Globals.assetManager.fetchModel(modelPath);
boolean hasDrawn = false; boolean hasDrawn = false;
if(model != null){ if(model != null && isWithinFrustumBox(renderPipelineState,model)){
applyAnimationMasks(model); applyAnimationMasks(model);
meshMask.processMeshMaskQueue(); meshMask.processMeshMaskQueue();
model.setMeshMask(meshMask); model.setMeshMask(meshMask);
@ -353,5 +364,17 @@ public class Actor {
return this.staticMorph; 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);
}
} }

View File

@ -2,6 +2,10 @@ package electrosphere.renderer.actor;
import org.joml.Quaternionf; 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 { public class ActorBoneRotator {
Quaternionf rotation = new Quaternionf().identity(); Quaternionf rotation = new Quaternionf().identity();

View File

@ -7,6 +7,10 @@ import org.joml.Matrix4f;
import org.joml.Quaternionf; import org.joml.Quaternionf;
import org.joml.Vector3f; 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 { public class ActorStaticMorph {
Map<String,StaticMorphTransforms> boneTransformMap = new HashMap<String,StaticMorphTransforms>(); Map<String,StaticMorphTransforms> boneTransformMap = new HashMap<String,StaticMorphTransforms>();

View File

@ -3,11 +3,15 @@ package electrosphere.renderer.actor.instance;
import java.nio.DoubleBuffer; import java.nio.DoubleBuffer;
import java.nio.FloatBuffer; import java.nio.FloatBuffer;
import java.nio.IntBuffer; import java.nio.IntBuffer;
import java.util.Comparator;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.NavigableSet;
import java.util.PriorityQueue; import java.util.PriorityQueue;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentSkipListSet;
import org.joml.Matrix4d; import org.joml.Matrix4d;
import org.joml.Matrix4f; import org.joml.Matrix4f;
@ -37,8 +41,8 @@ public class InstanceData {
//the number of draw calls since the last clear operation //the number of draw calls since the last clear operation
int drawCalls = 0; int drawCalls = 0;
//The priority queue of instanced actors to draw //The set of instanced actors to draw
PriorityQueue<InstancedActor> actorQueue = null; List<InstancedActor> actorQueue = null;
//Map of actor to index in the buffers that are emitted //Map of actor to index in the buffers that are emitted
Map<InstancedActor,Integer> actorIndexMap = new HashMap<InstancedActor,Integer>(); Map<InstancedActor,Integer> actorIndexMap = new HashMap<InstancedActor,Integer>();
//Map of index -> actor used for buffer evictions //Map of index -> actor used for buffer evictions
@ -60,7 +64,7 @@ public class InstanceData {
this.capacity = capacity; this.capacity = capacity;
this.vertexShaderPath = vertexPath; this.vertexShaderPath = vertexPath;
this.fragmentShaderPath = fragmentPath; this.fragmentShaderPath = fragmentPath;
actorQueue = new PriorityQueue<InstancedActor>(this.capacity); actorQueue = new LinkedList<InstancedActor>();
} }
/** /**
@ -179,6 +183,7 @@ public class InstanceData {
} break; } break;
} }
} }
actorQueue.sort(Comparator.naturalOrder());
//buffer data //buffer data
for(InstancedActor actor : actorQueue){ for(InstancedActor actor : actorQueue){
//push values to attribute buffers //push values to attribute buffers

View File

@ -6,11 +6,14 @@ import java.util.Map;
import org.joml.Matrix4d; import org.joml.Matrix4d;
import org.joml.Matrix4f; import org.joml.Matrix4f;
import org.joml.Sphered;
import org.joml.Vector3d;
import org.joml.Vector3f; import org.joml.Vector3f;
import electrosphere.engine.Globals; import electrosphere.engine.Globals;
import electrosphere.entity.Entity; import electrosphere.entity.Entity;
import electrosphere.entity.EntityDataStrings; import electrosphere.entity.EntityDataStrings;
import electrosphere.entity.EntityUtils;
import electrosphere.logger.LoggerInterface; import electrosphere.logger.LoggerInterface;
import electrosphere.renderer.Model; import electrosphere.renderer.Model;
import electrosphere.renderer.RenderPipelineState; import electrosphere.renderer.RenderPipelineState;
@ -41,6 +44,23 @@ public class InstancedActor implements Comparable<InstancedActor> {
/** /**
* Draws the instanced actor. Should be called normally in a loop as if this was a regular actor. * 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 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){ public void draw(RenderPipelineState renderPipelineState){
Model model = Globals.assetManager.fetchModel(modelPath); Model model = Globals.assetManager.fetchModel(modelPath);

View File

@ -25,6 +25,7 @@ import electrosphere.renderer.Material;
import electrosphere.renderer.Mesh; import electrosphere.renderer.Mesh;
import electrosphere.renderer.Model; import electrosphere.renderer.Model;
import electrosphere.renderer.ShaderProgram; import electrosphere.renderer.ShaderProgram;
import electrosphere.server.terrain.manager.ServerTerrainChunk;
public class TerrainChunkModelGeneration { public class TerrainChunkModelGeneration {
@ -775,6 +776,13 @@ public class TerrainChunkModelGeneration {
ex.printStackTrace(); 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; m.parent = rVal;
rVal.meshes.add(m); rVal.meshes.add(m);
rVal.setBoundingSphere(m.getBoundingSphere());
return rVal; return rVal;
} }