optimizations
All checks were successful
studiorailgun/Renderer/pipeline/head This commit looks good

This commit is contained in:
austin 2025-03-28 23:19:09 -04:00
parent 50a816adc0
commit deea49ba64
13 changed files with 186 additions and 151 deletions

View File

@ -1365,6 +1365,15 @@ Stop sleeping during high frame time
Actor bone spatial data caching for performance
Volumetric pipeline optimization
Collidable tree simplification
Near collision callback streamlining
Reduced mid-range chunk LOD
MicroSimulation performance checks
Small hitbox collection optimization
Remove excess collision call on client
Move profiler logging
Disable kinematic colliders on creation
MainContentPipeline reduce allocations
Move AI simulation to server level from chunk level

View File

@ -57,7 +57,6 @@ public class ClientSimulation {
if(Globals.RUN_PHYSICS){
Globals.clientSceneWrapper.getCollisionEngine().simulatePhysics((float)Globals.timekeeper.getSimFrameTime());
Globals.clientSceneWrapper.getCollisionEngine().updateDynamicObjectTransforms();
Globals.clientSceneWrapper.getChemistryEngine().collide();
}
//update actor animations

View File

@ -40,17 +40,17 @@ public class ClientDrawCellManager {
/**
* The distance for half resolution
*/
public static final double HALF_RES_DIST = 16;
public static final double HALF_RES_DIST = 12;
/**
* The distance for quarter resolution
*/
public static final double QUARTER_RES_DIST = 20;
public static final double QUARTER_RES_DIST = 16;
/**
* The distance for eighth resolution
*/
public static final double EIGHTH_RES_DIST = 32;
public static final double EIGHTH_RES_DIST = 24;
/**
* The distance for sixteenth resolution

View File

@ -315,6 +315,11 @@ public class CollisionEngine {
private void nearCallback(Object data, DGeom o1, DGeom o2){
// if (o1->body && o2->body) return;
//if the collision is static-on-static, skip
if(o1.getCategoryBits() == Collidable.TYPE_STATIC_BIT && o2.getCategoryBits() == Collidable.TYPE_STATIC_BIT){
return;
}
// exit without doing anything if the two bodies are connected by a joint
DBody b1 = o1.getBody();
DBody b2 = o2.getBody();
@ -322,143 +327,136 @@ public class CollisionEngine {
return;
}
//if collision is between static and disabled, skip
if(o1.getCategoryBits() == Collidable.TYPE_STATIC_BIT && !b2.isEnabled()){
return;
}
if(o2.getCategoryBits() == Collidable.TYPE_STATIC_BIT && !b1.isEnabled()){
return;
}
Collidable c1 = bodyPointerMap.get(b1);
Collidable c2 = bodyPointerMap.get(b2);
if(c1 == null || c2 == null){
return;
}
if(
!(
bodyPointerMap.get(b1).getType() == Collidable.TYPE_STATIC &&
(
bodyPointerMap.get(b2).getType() == Collidable.TYPE_STATIC ||
!b2.isEnabled()
)
) &&
!(
bodyPointerMap.get(b2).getType() == Collidable.TYPE_STATIC &&
(
bodyPointerMap.get(b1).getType() == Collidable.TYPE_STATIC ||
!b1.isEnabled()
)
)
){
try {
//creates a buffer to store potential collisions
DContactBuffer contacts = new DContactBuffer(MAX_CONTACTS); // up to MAX_CONTACTS contacts per box-box
SurfaceParams surfaceParams = c1.getSurfaceParams();
for (int i=0; i<MAX_CONTACTS; i++) {
DContact contact = contacts.get(i);
contact.surface.mode = surfaceParams.getMode();
contact.surface.mu = surfaceParams.getMu();
if(surfaceParams.getRho() != null){
contact.surface.rho = surfaceParams.getRho();
}
if(surfaceParams.getRho2() != null){
contact.surface.rho2 = surfaceParams.getRho2();
}
if(surfaceParams.getRhoN() != null){
contact.surface.rhoN = surfaceParams.getRhoN();
}
if(surfaceParams.getBounce() != null){
contact.surface.bounce = surfaceParams.getBounce();
}
if(surfaceParams.getBounceVel() != null){
contact.surface.bounce_vel = surfaceParams.getBounceVel();
}
Globals.profiler.beginAggregateCpuSample("CollisionEngine.nearCallback - try collisions");
try {
//creates a buffer to store potential collisions
DContactBuffer contacts = new DContactBuffer(MAX_CONTACTS); // up to MAX_CONTACTS contacts per box-box
SurfaceParams surfaceParams = c1.getSurfaceParams();
for (int i=0; i<MAX_CONTACTS; i++) {
DContact contact = contacts.get(i);
contact.surface.mode = surfaceParams.getMode();
contact.surface.mu = surfaceParams.getMu();
if(surfaceParams.getRho() != null){
contact.surface.rho = surfaceParams.getRho();
}
//calculate collisions
int numc = OdeHelper.collide(o1,o2,MAX_CONTACTS,contacts.getGeomBuffer());
//create DContacts based on each collision that occurs
if (numc != 0) {
for (int i=0; i<numc; i++) {
DContact contact = contacts.get(i);
//special code for ray casting
if (o1 instanceof DRay || o2 instanceof DRay){
DVector3 end = new DVector3();
end.eqSum( contact.geom.pos, contact.geom.normal, contact.geom.depth );
continue;
}
// //
// //apply shallow slope correction
// if(
// //is terrain
// (c1.getType() == Collidable.TYPE_TERRAIN || c2.getType() == Collidable.TYPE_TERRAIN) &&
// (!c1.getType().equals(c2.getType())) &&
// contact.geom.normal.length() > 0
// &&
// //force is pointing basically upwards
// PhysicsUtils.odeVecToJomlVec(contact.geom.normal).dot(MathUtils.getUpVector()) > 0.7
// ){
// System.out.println(PhysicsUtils.odeVecToJomlVec(contact.geom.normal).dot(MathUtils.getUpVector()));
// if(//force is pointing basically upwards
// PhysicsUtils.odeVecToJomlVec(contact.geom.normal).dot(MathUtils.getUpVector()) > 0.7){
// contact.geom.normal.set(PhysicsUtils.jomlVecToOdeVec(MathUtils.getUpVector()));
// }
// }
//
//add contact to contact group
DJoint c = OdeHelper.createContactJoint(world,contactgroup,contact);
c.attach(b1,b2);
// Use the default collision resolution
if(collisionResolutionCallback == null) {
resolveCollision(
contact.geom,
bodyPointerMap.get(o1.getBody()),
bodyPointerMap.get(o2.getBody()),
PhysicsUtils.odeVecToJomlVec(contact.geom.normal).mul(-1.0),
PhysicsUtils.odeVecToJomlVec(contact.fdir1).mul(-1.0),
PhysicsUtils.odeVecToJomlVec(contact.geom.pos),
(float)contact.geom.depth
);
resolveCollision(
contact.geom,
bodyPointerMap.get(o2.getBody()),
bodyPointerMap.get(o1.getBody()),
PhysicsUtils.odeVecToJomlVec(contact.geom.normal),
PhysicsUtils.odeVecToJomlVec(contact.fdir1),
PhysicsUtils.odeVecToJomlVec(contact.geom.pos),
(float)contact.geom.depth
);
} else {
//use custom collision resolution
collisionResolutionCallback.resolve(
contact.geom,
o1,
o2,
bodyPointerMap.get(o1.getBody()),
bodyPointerMap.get(o2.getBody()),
PhysicsUtils.odeVecToJomlVec(contact.geom.normal).mul(-1.0),
PhysicsUtils.odeVecToJomlVec(contact.fdir1).mul(-1.0),
PhysicsUtils.odeVecToJomlVec(contact.geom.pos),
(float)contact.geom.depth
);
collisionResolutionCallback.resolve(
contact.geom,
o2,
o1,
bodyPointerMap.get(o2.getBody()),
bodyPointerMap.get(o1.getBody()),
PhysicsUtils.odeVecToJomlVec(contact.geom.normal),
PhysicsUtils.odeVecToJomlVec(contact.fdir1),
PhysicsUtils.odeVecToJomlVec(contact.geom.pos),
(float)contact.geom.depth
);
}
}
if(surfaceParams.getRho2() != null){
contact.surface.rho2 = surfaceParams.getRho2();
}
if(surfaceParams.getRhoN() != null){
contact.surface.rhoN = surfaceParams.getRhoN();
}
if(surfaceParams.getBounce() != null){
contact.surface.bounce = surfaceParams.getBounce();
}
if(surfaceParams.getBounceVel() != null){
contact.surface.bounce_vel = surfaceParams.getBounceVel();
}
} catch(ArrayIndexOutOfBoundsException ex){
//I've found that ode4j occasionally throws an exception on the OdeHelper.collide function.
//I don't know why it has out of bounds elements, but it's happening.
//Catching the exception here allows the engine to keep running at least.
LoggerInterface.loggerEngine.ERROR("ode4j error", ex);
}
//calculate collisions
int numc = OdeHelper.collide(o1,o2,MAX_CONTACTS,contacts.getGeomBuffer());
//create DContacts based on each collision that occurs
if (numc != 0) {
for (int i=0; i<numc; i++) {
DContact contact = contacts.get(i);
//special code for ray casting
if (o1 instanceof DRay || o2 instanceof DRay){
DVector3 end = new DVector3();
end.eqSum( contact.geom.pos, contact.geom.normal, contact.geom.depth );
continue;
}
// //
// //apply shallow slope correction
// if(
// //is terrain
// (c1.getType() == Collidable.TYPE_TERRAIN || c2.getType() == Collidable.TYPE_TERRAIN) &&
// (!c1.getType().equals(c2.getType())) &&
// contact.geom.normal.length() > 0
// &&
// //force is pointing basically upwards
// PhysicsUtils.odeVecToJomlVec(contact.geom.normal).dot(MathUtils.getUpVector()) > 0.7
// ){
// System.out.println(PhysicsUtils.odeVecToJomlVec(contact.geom.normal).dot(MathUtils.getUpVector()));
// if(//force is pointing basically upwards
// PhysicsUtils.odeVecToJomlVec(contact.geom.normal).dot(MathUtils.getUpVector()) > 0.7){
// contact.geom.normal.set(PhysicsUtils.jomlVecToOdeVec(MathUtils.getUpVector()));
// }
// }
//
//add contact to contact group
DJoint c = OdeHelper.createContactJoint(world,contactgroup,contact);
c.attach(b1,b2);
// Use the default collision resolution
if(collisionResolutionCallback == null) {
CollisionEngine.resolveCollision(
contact.geom,
bodyPointerMap.get(o1.getBody()),
bodyPointerMap.get(o2.getBody()),
PhysicsUtils.odeVecToJomlVec(contact.geom.normal).mul(-1.0),
PhysicsUtils.odeVecToJomlVec(contact.fdir1).mul(-1.0),
PhysicsUtils.odeVecToJomlVec(contact.geom.pos),
(float)contact.geom.depth
);
CollisionEngine.resolveCollision(
contact.geom,
bodyPointerMap.get(o2.getBody()),
bodyPointerMap.get(o1.getBody()),
PhysicsUtils.odeVecToJomlVec(contact.geom.normal),
PhysicsUtils.odeVecToJomlVec(contact.fdir1),
PhysicsUtils.odeVecToJomlVec(contact.geom.pos),
(float)contact.geom.depth
);
} else {
//use custom collision resolution
collisionResolutionCallback.resolve(
contact.geom,
o1,
o2,
bodyPointerMap.get(o1.getBody()),
bodyPointerMap.get(o2.getBody()),
PhysicsUtils.odeVecToJomlVec(contact.geom.normal).mul(-1.0),
PhysicsUtils.odeVecToJomlVec(contact.fdir1).mul(-1.0),
PhysicsUtils.odeVecToJomlVec(contact.geom.pos),
(float)contact.geom.depth
);
collisionResolutionCallback.resolve(
contact.geom,
o2,
o1,
bodyPointerMap.get(o2.getBody()),
bodyPointerMap.get(o1.getBody()),
PhysicsUtils.odeVecToJomlVec(contact.geom.normal),
PhysicsUtils.odeVecToJomlVec(contact.fdir1),
PhysicsUtils.odeVecToJomlVec(contact.geom.pos),
(float)contact.geom.depth
);
}
}
}
} catch(ArrayIndexOutOfBoundsException ex){
//I've found that ode4j occasionally throws an exception on the OdeHelper.collide function.
//I don't know why it has out of bounds elements, but it's happening.
//Catching the exception here allows the engine to keep running at least.
LoggerInterface.loggerEngine.ERROR("ode4j error", ex);
}
Globals.profiler.endCpuSample();
}
/**

View File

@ -123,6 +123,7 @@ public class PhysicsEntityUtils {
}
if(physicsTemplate.getKinematic()){
Globals.clientSceneWrapper.getCollisionEngine().setKinematic(rigidBody);
rigidBody.disable();
}
rVal.putData(EntityDataStrings.PHYSICS_COLLISION_BODY_TRANSFORM, offsetTransform);
rVal.putData(EntityDataStrings.PHYSICS_MODEL_TEMPLATE, physicsTemplate);
@ -186,6 +187,7 @@ public class PhysicsEntityUtils {
}
if(physicsTemplate.getKinematic()){
Globals.clientSceneWrapper.getCollisionEngine().setKinematic(rigidBody);
rigidBody.disable();
}
rVal.putData(EntityDataStrings.PHYSICS_COLLISION_BODY_TRANSFORM, offsetTransform);
rVal.putData(EntityDataStrings.PHYSICS_MODEL_TEMPLATE, physicsTemplate);
@ -251,6 +253,7 @@ public class PhysicsEntityUtils {
}
if(physicsTemplate.getKinematic()){
Globals.clientSceneWrapper.getCollisionEngine().setKinematic(rigidBody);
rigidBody.disable();
}
rVal.putData(EntityDataStrings.PHYSICS_COLLISION_BODY_TRANSFORM, offsetTransform);
rVal.putData(EntityDataStrings.PHYSICS_MODEL_TEMPLATE, physicsTemplate);
@ -343,6 +346,7 @@ public class PhysicsEntityUtils {
}
if(physicsTemplate.getKinematic()){
Globals.clientSceneWrapper.getCollisionEngine().setKinematic(rigidBody);
rigidBody.disable();
}
rVal.putData(EntityDataStrings.PHYSICS_COLLISION_BODY_TRANSFORM, offsetTransform);
rVal.putData(EntityDataStrings.PHYSICS_MODEL_TEMPLATE, physicsTemplate);
@ -406,6 +410,7 @@ public class PhysicsEntityUtils {
}
if(physicsTemplate.getKinematic()){
Globals.clientSceneWrapper.getCollisionEngine().setKinematic(rigidBody);
rigidBody.disable();
}
rVal.putData(EntityDataStrings.PHYSICS_COLLISION_BODY_TRANSFORM, offsetTransform);
rVal.putData(EntityDataStrings.PHYSICS_MODEL_TEMPLATE, physicsTemplate);
@ -471,6 +476,7 @@ public class PhysicsEntityUtils {
}
if(physicsTemplate.getKinematic()){
Globals.clientSceneWrapper.getCollisionEngine().setKinematic(rigidBody);
rigidBody.disable();
}
rVal.putData(EntityDataStrings.PHYSICS_COLLISION_BODY_TRANSFORM, offsetTransform);
rVal.putData(EntityDataStrings.PHYSICS_MODEL_TEMPLATE, physicsTemplate);

View File

@ -181,6 +181,7 @@ public class HitboxCollectionState {
//create body with all the shapes
DGeom[] geomArray = rVal.geoms.toArray(new DGeom[rVal.geoms.size()]);
rVal.body = CollisionBodyCreation.createBodyWithShapes(manager.getCollisionEngine(), geomArray);
CollisionBodyCreation.setKinematic(manager.getCollisionEngine(), rVal.body);
//register collidable with collision engine
Collidable collidable = new Collidable(entity, Collidable.TYPE_OBJECT, true);

View File

@ -452,8 +452,9 @@ public class Actor {
* @param renderPipelineState The render pipeline state to draw within
*/
public void draw(RenderPipelineState renderPipelineState, OpenGLState openGLState){
Globals.profiler.beginAggregateCpuSample("Actor.draw");
Model model = Globals.assetManager.fetchModel(modelPath);
if(model != null && isWithinFrustumBox(renderPipelineState,model,frustumCull)){
if(model != null && Actor.isWithinFrustumBox(renderPipelineState,model,frustumCull)){
this.applyAnimationMasks(model);
meshMask.processMeshMaskQueue();
model.setMeshMask(meshMask);
@ -483,6 +484,7 @@ public class Actor {
model.getShaderMask().clear();
model.setTextureMask(null);
}
Globals.profiler.endCpuSample();
}
public void drawUI(){
@ -734,9 +736,12 @@ public class Actor {
if(!frustumCull){
return true;
}
Globals.profiler.beginAggregateCpuSample("Actor.isWithinFrustumBox");
Sphered sphere = model.getBoundingSphere();
Vector3d modelPosition = model.getModelMatrix().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);
boolean check = renderPipelineState.getFrustumIntersection().testSphere((float)(sphere.x + modelPosition.x), (float)(sphere.y + modelPosition.y), (float)(sphere.z + modelPosition.z), (float)sphere.r);
Globals.profiler.endCpuSample();
return check;
}

View File

@ -347,6 +347,7 @@ public class Mesh {
* @param renderPipelineState The state of the render pipeline
*/
public void complexDraw(RenderPipelineState renderPipelineState, OpenGLState openGLState){
Globals.profiler.beginAggregateCpuSample("Mesh.complexDraw");
//bind vao off the rip
GL40.glBindVertexArray(vertexArrayObject);
@ -526,6 +527,7 @@ public class Mesh {
}
GL40.glBindVertexArray(0);
Globals.renderingEngine.checkError();
Globals.profiler.endCpuSample();
}
/**

View File

@ -215,6 +215,7 @@ public class Model {
* @param renderPipelineState the render pipeline state
*/
public void draw(RenderPipelineState renderPipelineState, OpenGLState openGLState){
Globals.profiler.beginAggregateCpuSample("Model.draw");
Iterator<Mesh> mesh_Iterator = meshes.iterator();
while(mesh_Iterator.hasNext()){
Mesh currentMesh = mesh_Iterator.next();
@ -249,6 +250,7 @@ public class Model {
toDraw.setShader(original);
}
}
Globals.profiler.endCpuSample();
}
/**

View File

@ -1,5 +1,7 @@
package electrosphere.renderer.pipelines;
import java.util.Set;
import org.joml.Matrix4d;
import org.joml.Quaterniond;
import org.joml.Vector3d;
@ -70,18 +72,21 @@ public class MainContentPipeline implements RenderPipeline {
// D R A W A L L E N T I T I E S
//
Globals.profiler.beginCpuSample("MainContentPipeline.render - Solids non-instanced");
for(Entity currentEntity : Globals.clientScene.getEntitiesWithTag(EntityTags.DRAWABLE)){
Set<Entity> solidsNonInstanced = Globals.clientScene.getEntitiesWithTag(EntityTags.DRAWABLE);
Vector3d positionVec = new Vector3d();
Vector3d cameraCenterVec = CameraEntityUtils.getCameraCenter(Globals.playerCamera);
for(Entity currentEntity : solidsNonInstanced){
Vector3d position = EntityUtils.getPosition(currentEntity);
if(shouldDrawSolidPass(currentEntity)){
if(MainContentPipeline.shouldDrawSolidPass(currentEntity)){
//fetch actor
Actor currentActor = EntityUtils.getActor(currentEntity);
//calculate camera-modified vector3d
Vector3d cameraModifiedPosition = new Vector3d(position).sub(CameraEntityUtils.getCameraCenter(Globals.playerCamera));
Vector3d cameraModifiedPosition = positionVec.set(position).sub(cameraCenterVec);
//calculate and apply model transform
modelTransformMatrix.identity();
modelTransformMatrix.translate(cameraModifiedPosition);
modelTransformMatrix.rotate(EntityUtils.getRotation(currentEntity));
modelTransformMatrix.scale(new Vector3d(EntityUtils.getScale(currentEntity)));
modelTransformMatrix.scale(positionVec.set(EntityUtils.getScale(currentEntity)));
currentActor.applySpatialData(modelTransformMatrix,position);
//draw
currentActor.draw(renderPipelineState,openGLState);
@ -94,7 +99,7 @@ public class MainContentPipeline implements RenderPipeline {
Globals.profiler.beginCpuSample("MainContentPipeline.render - Solids instanced");
for(Entity currentEntity : Globals.clientScene.getEntitiesWithTag(EntityTags.DRAW_INSTANCED)){
Vector3d position = EntityUtils.getPosition(currentEntity);
if(shouldDrawSolidPass(currentEntity)){
if(MainContentPipeline.shouldDrawSolidPass(currentEntity)){
//fetch actor
InstancedActor currentActor = InstancedActor.getInstancedActor(currentEntity);
//if the shader attribute for model matrix exists, calculate the model matrix and apply
@ -158,7 +163,7 @@ public class MainContentPipeline implements RenderPipeline {
Globals.profiler.beginCpuSample("MainContentPipeline.render - Transparents non-instanced");
for(Entity currentEntity : Globals.clientScene.getEntitiesWithTag(EntityTags.DRAWABLE)){
Vector3d position = EntityUtils.getPosition(currentEntity);
if(shouldDrawTransparentPass(currentEntity)){
if(MainContentPipeline.shouldDrawTransparentPass(currentEntity)){
//fetch actor
Actor currentActor = EntityUtils.getActor(currentEntity);
//calculate camera-modified vector3d
@ -177,7 +182,7 @@ public class MainContentPipeline implements RenderPipeline {
Globals.profiler.beginCpuSample("MainContentPipeline.render - Transparents instanced");
for(Entity currentEntity : Globals.clientScene.getEntitiesWithTag(EntityTags.DRAW_INSTANCED)){
Vector3d position = EntityUtils.getPosition(currentEntity);
if(shouldDrawTransparentPass(currentEntity)){
if(MainContentPipeline.shouldDrawTransparentPass(currentEntity)){
//fetch actor
InstancedActor currentActor = InstancedActor.getInstancedActor(currentEntity);
//if the shader attribute for model matrix exists, calculate the model matrix and apply
@ -236,7 +241,7 @@ public class MainContentPipeline implements RenderPipeline {
entity.getData(EntityDataStrings.DRAW_SOLID_PASS) != null
) &&
(
!entityBlacklist(entity)
!MainContentPipeline.entityBlacklist(entity)
)
;
}
@ -253,7 +258,7 @@ public class MainContentPipeline implements RenderPipeline {
entity.getData(EntityDataStrings.DRAW_TRANSPARENT_PASS) != null
) &&
(
!entityBlacklist(entity)
!MainContentPipeline.entityBlacklist(entity)
)
;
}
@ -270,15 +275,15 @@ public class MainContentPipeline implements RenderPipeline {
//don't draw third person view if camera is first person
(
!Globals.controlHandler.cameraIsThirdPerson() &&
entity == Globals.playerEntity
entity == Globals.playerEntity &&
!Globals.controlHandler.cameraIsThirdPerson()
) ||
//don't draw items if they're attached to viewmodel
(
Globals.firstPersonEntity != null &&
!Globals.controlHandler.cameraIsThirdPerson() &&
AttachUtils.hasParent(entity) &&
Globals.firstPersonEntity != null &&
AttachUtils.getParent(entity) == Globals.firstPersonEntity
)
;

View File

@ -33,6 +33,10 @@ public class MainServerFunctions {
}
Globals.profiler.endCpuSample();
//
//Update AI
Globals.aiManager.simulate();
//
//Micro simulation (ie simulating each scene on the server)
Globals.profiler.beginCpuSample("MainServerFunctions.simulate - Server micro simulation");

View File

@ -593,8 +593,10 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
public void simulate(){
Globals.profiler.beginCpuSample("GriddedDataCellManager.simulate");
loadedCellsLock.lock();
for(ServerDataCell cell : this.groundDataCells.values()){
if(Globals.microSimulation != null && Globals.microSimulation.isReady()){
Collection<ServerDataCell> groundCells = this.groundDataCells.values();
boolean runMicroSim = Globals.microSimulation != null && Globals.microSimulation.isReady();
for(ServerDataCell cell : groundCells){
if(runMicroSim){
Globals.microSimulation.simulate(cell);
}

View File

@ -36,8 +36,6 @@ public class MicroSimulation {
public void simulate(ServerDataCell dataCell){
Globals.profiler.beginCpuSample("MicroSimulation.simulate");
if(dataCell.isReady()){
//simulate ai
Globals.aiManager.simulate();
//update actor animations
Set<Entity> poseableEntities = dataCell.getScene().getEntitiesWithTag(EntityTags.POSEABLE);
if(poseableEntities != null){
@ -55,13 +53,17 @@ public class MicroSimulation {
ItemUtils.updateItemPoseActorAnimation(item);
}
//simulate behavior trees
dataCell.getScene().simulateBehaviorTrees((float)Globals.timekeeper.getSimFrameTime());
if(dataCell.getScene().getBehaviorTrees().size() > 0){
dataCell.getScene().simulateBehaviorTrees((float)Globals.timekeeper.getSimFrameTime());
}
//update attached entity positions
//!!This must come after simulating behavior trees!!
//if it does not come after queueing animations, the attach positions might not represent the animation of the parent
AttachUtils.serverUpdateAttachedEntityPositions(dataCell);
//sum collidable impulses
for(Entity collidable : dataCell.getScene().getEntitiesWithTag(EntityTags.COLLIDABLE)){
Set<Entity> collidables = dataCell.getScene().getEntitiesWithTag(EntityTags.COLLIDABLE);
for(Entity collidable : collidables){
ServerCollidableTree.getServerCollidableTree(collidable).simulate((float)Globals.timekeeper.getSimFrameTime());
}
}