From f9d8ad0adb8ce45131fb57ef8d511af73b8ad2a0 Mon Sep 17 00:00:00 2001 From: austin Date: Wed, 4 Jun 2025 17:37:05 -0400 Subject: [PATCH] client collidable LOD work --- docs/src/progress/renderertodo.md | 1 + .../collision/PhysicsEntityUtils.java | 226 ++++++++++-------- .../entity/state/lod/ClientLODComponent.java | 8 + .../physicssync/ClientPhysicsSyncTree.java | 9 +- .../physicssync/ServerPhysicsSyncTree.java | 37 ++- 5 files changed, 177 insertions(+), 104 deletions(-) diff --git a/docs/src/progress/renderertodo.md b/docs/src/progress/renderertodo.md index 03859916..c19881fc 100644 --- a/docs/src/progress/renderertodo.md +++ b/docs/src/progress/renderertodo.md @@ -2110,6 +2110,7 @@ Fix physics performance issues (06/04/2025) ServerGroundMovementTree actually moves collidable-based entities +Client uses non-rigid-body collidables for farther away entities (via client LOD tree) diff --git a/src/main/java/electrosphere/collision/PhysicsEntityUtils.java b/src/main/java/electrosphere/collision/PhysicsEntityUtils.java index aa65689d..550008ef 100644 --- a/src/main/java/electrosphere/collision/PhysicsEntityUtils.java +++ b/src/main/java/electrosphere/collision/PhysicsEntityUtils.java @@ -67,7 +67,6 @@ public class PhysicsEntityUtils { public static void clientAttachCollidableTemplate(Entity rVal, CollidableTemplate physicsTemplate){ Collidable collidable; double mass = 1.0f; - CollisionEngine engine = Globals.clientState.clientSceneWrapper.getCollisionEngine(); if(physicsTemplate.getMass() != null){ mass = physicsTemplate.getMass(); } @@ -77,96 +76,7 @@ public class PhysicsEntityUtils { } CollisionEngine.lockOde(); if(physicsTemplate.getKinematic()){ - DGeom geom = null; - switch(physicsTemplate.getType()){ - case CollidableTemplate.COLLIDABLE_TYPE_CYLINDER: { - - // - //create dbody - geom = CollisionBodyCreation.createCylinderShape( - engine, - physicsTemplate.getDimension1(), - physicsTemplate.getDimension2(), - categoryBit - ); - - // - //create collidable and link to structures - collidable = new Collidable(rVal, Collidable.TYPE_CREATURE, true); - PhysicsEntityUtils.setDGeom(rVal, geom); - - // - //store values - Matrix4d offsetTransform = new Matrix4d().translationRotate( - physicsTemplate.getOffsetX(), physicsTemplate.getOffsetY(), physicsTemplate.getOffsetZ(), //translate - physicsTemplate.getRotX(), physicsTemplate.getRotY(), physicsTemplate.getRotZ(), physicsTemplate.getRotW() //rotate - ); - rVal.putData(EntityDataStrings.PHYSICS_COLLISION_BODY_TRANSFORM, offsetTransform); - rVal.putData(EntityDataStrings.PHYSICS_MODEL_TEMPLATE, physicsTemplate); - rVal.putData(EntityDataStrings.PHYSICS_COLLIDABLE, collidable); - - Globals.clientState.clientScene.registerEntityToTag(rVal, EntityTags.COLLIDABLE); - engine.registerCollisionObject(geom, collidable, EntityUtils.getPosition(rVal)); - } break; - case CollidableTemplate.COLLIDABLE_TYPE_CUBE: { - // - //create dbody - geom = CollisionBodyCreation.createCubeShape( - Globals.clientState.clientSceneWrapper.getCollisionEngine(), - new Vector3d(physicsTemplate.getDimension1(),physicsTemplate.getDimension2(),physicsTemplate.getDimension3()), - categoryBit - ); - - // - //create collidable and link to structures - collidable = new Collidable(rVal, Collidable.TYPE_CREATURE, true); - PhysicsEntityUtils.setDGeom(rVal,geom); - - // - //store values - Matrix4d offsetTransform = new Matrix4d().translationRotateScale( - physicsTemplate.getOffsetX(), physicsTemplate.getOffsetY(), physicsTemplate.getOffsetZ(), //translate - physicsTemplate.getRotX(), physicsTemplate.getRotY(), physicsTemplate.getRotZ(), physicsTemplate.getRotW(), //rotate - 1, 1, 1 //scale - ); - rVal.putData(EntityDataStrings.PHYSICS_COLLISION_BODY_TRANSFORM, offsetTransform); - rVal.putData(EntityDataStrings.PHYSICS_MODEL_TEMPLATE, physicsTemplate); - rVal.putData(EntityDataStrings.PHYSICS_COLLIDABLE, collidable); - - Globals.clientState.clientScene.registerEntityToTag(rVal, EntityTags.COLLIDABLE); - engine.registerCollisionObject(geom, collidable, EntityUtils.getPosition(rVal)); - } break; - case CollidableTemplate.COLLIDABLE_TYPE_CAPSULE: { - // - //create dbody - geom = CollisionBodyCreation.createCapsuleShape( - Globals.clientState.clientSceneWrapper.getCollisionEngine(), - physicsTemplate.getDimension1(), - physicsTemplate.getDimension2(), - categoryBit - ); - - // - //create collidable and link to structures - collidable = new Collidable(rVal, Collidable.TYPE_CREATURE, true); - PhysicsEntityUtils.setDGeom(rVal,geom); - - // - //store values - Matrix4d offsetTransform = new Matrix4d().translationRotateScale( - physicsTemplate.getOffsetX(), physicsTemplate.getOffsetY(), physicsTemplate.getOffsetZ(), //translate - physicsTemplate.getRotX(), physicsTemplate.getRotY(), physicsTemplate.getRotZ(), physicsTemplate.getRotW(), //rotate - 1, 1, 1 //scale - ); - rVal.putData(EntityDataStrings.PHYSICS_COLLISION_BODY_TRANSFORM, offsetTransform); - rVal.putData(EntityDataStrings.PHYSICS_MODEL_TEMPLATE, physicsTemplate); - rVal.putData(EntityDataStrings.PHYSICS_COLLIDABLE, collidable); - rVal.putData(EntityDataStrings.PHYSICS_MASS, mass); - - Globals.clientState.clientScene.registerEntityToTag(rVal, EntityTags.COLLIDABLE); - engine.registerCollisionObject(geom, collidable, EntityUtils.getPosition(rVal)); - } break; - } + PhysicsEntityUtils.clientAttachGeom(rVal, physicsTemplate, EntityUtils.getPosition(rVal)); } else { DBody rigidBody = null; switch(physicsTemplate.getType()){ @@ -366,16 +276,128 @@ public class PhysicsEntityUtils { } break; } //if we successfully attached the body, add a sync tree - if(rigidBody != null){ - ClientPhysicsSyncTree.attachTree(rVal); - if(ClientGravityTree.hasClientGravityTree(rVal)){ - ClientGravityTree.getClientGravityTree(rVal).updatePhysicsPair(PhysicsEntityUtils.getCollidable(rVal),PhysicsEntityUtils.getDBody(rVal)); - } + ClientPhysicsSyncTree.attachTree(rVal); + if(ClientGravityTree.hasClientGravityTree(rVal)){ + ClientGravityTree.getClientGravityTree(rVal).updatePhysicsPair(PhysicsEntityUtils.getCollidable(rVal),PhysicsEntityUtils.getDBody(rVal)); } } CollisionEngine.unlockOde(); } + /** + * [SERVER ONLY] Attaches a collidable template to a given entity + * @param rVal The entity + * @param physicsTemplate The collidable template + * @return The geometry object + */ + public static DGeom clientAttachGeom(Entity rVal, CollidableTemplate physicsTemplate, Vector3d position){ + DGeom geom = null; + Collidable collidable; + double mass = 1.0f; + long categoryBit = Collidable.TYPE_CREATURE_BIT; + if(physicsTemplate.getKinematic()){ + categoryBit = Collidable.TYPE_STATIC_BIT; + } + CollisionEngine engine = Globals.clientState.clientSceneWrapper.getCollisionEngine(); + CollisionEngine.lockOde(); + switch(physicsTemplate.getType()){ + case CollidableTemplate.COLLIDABLE_TYPE_CYLINDER: { + + // + //create dbody + geom = CollisionBodyCreation.createCylinderShape( + engine, + physicsTemplate.getDimension1(), + physicsTemplate.getDimension2(), + categoryBit + ); + + // + //create collidable and link to structures + collidable = new Collidable(rVal, Collidable.TYPE_CREATURE, true); + PhysicsEntityUtils.setDGeom(rVal, geom); + + // + //store values + Matrix4d offsetTransform = new Matrix4d().translationRotate( + physicsTemplate.getOffsetX(), physicsTemplate.getOffsetY(), physicsTemplate.getOffsetZ(), //translate + physicsTemplate.getRotX(), physicsTemplate.getRotY(), physicsTemplate.getRotZ(), physicsTemplate.getRotW() //rotate + ); + rVal.putData(EntityDataStrings.PHYSICS_COLLISION_BODY_TRANSFORM, offsetTransform); + rVal.putData(EntityDataStrings.PHYSICS_MODEL_TEMPLATE, physicsTemplate); + rVal.putData(EntityDataStrings.PHYSICS_COLLIDABLE, collidable); + + Globals.clientState.clientScene.registerEntityToTag(rVal, EntityTags.COLLIDABLE); + engine.registerCollisionObject(geom, collidable, EntityUtils.getPosition(rVal)); + } break; + case CollidableTemplate.COLLIDABLE_TYPE_CUBE: { + // + //create dbody + geom = CollisionBodyCreation.createCubeShape( + Globals.clientState.clientSceneWrapper.getCollisionEngine(), + new Vector3d(physicsTemplate.getDimension1(),physicsTemplate.getDimension2(),physicsTemplate.getDimension3()), + categoryBit + ); + + // + //create collidable and link to structures + collidable = new Collidable(rVal, Collidable.TYPE_CREATURE, true); + PhysicsEntityUtils.setDGeom(rVal,geom); + + // + //store values + Matrix4d offsetTransform = new Matrix4d().translationRotateScale( + physicsTemplate.getOffsetX(), physicsTemplate.getOffsetY(), physicsTemplate.getOffsetZ(), //translate + physicsTemplate.getRotX(), physicsTemplate.getRotY(), physicsTemplate.getRotZ(), physicsTemplate.getRotW(), //rotate + 1, 1, 1 //scale + ); + rVal.putData(EntityDataStrings.PHYSICS_COLLISION_BODY_TRANSFORM, offsetTransform); + rVal.putData(EntityDataStrings.PHYSICS_MODEL_TEMPLATE, physicsTemplate); + rVal.putData(EntityDataStrings.PHYSICS_COLLIDABLE, collidable); + + Globals.clientState.clientScene.registerEntityToTag(rVal, EntityTags.COLLIDABLE); + engine.registerCollisionObject(geom, collidable, EntityUtils.getPosition(rVal)); + } break; + case CollidableTemplate.COLLIDABLE_TYPE_CAPSULE: { + // + //create dbody + geom = CollisionBodyCreation.createCapsuleShape( + Globals.clientState.clientSceneWrapper.getCollisionEngine(), + physicsTemplate.getDimension1(), + physicsTemplate.getDimension2(), + categoryBit + ); + + // + //create collidable and link to structures + collidable = new Collidable(rVal, Collidable.TYPE_CREATURE, true); + PhysicsEntityUtils.setDGeom(rVal,geom); + + // + //store values + Matrix4d offsetTransform = new Matrix4d().translationRotateScale( + physicsTemplate.getOffsetX(), physicsTemplate.getOffsetY(), physicsTemplate.getOffsetZ(), //translate + physicsTemplate.getRotX(), physicsTemplate.getRotY(), physicsTemplate.getRotZ(), physicsTemplate.getRotW(), //rotate + 1, 1, 1 //scale + ); + rVal.putData(EntityDataStrings.PHYSICS_COLLISION_BODY_TRANSFORM, offsetTransform); + rVal.putData(EntityDataStrings.PHYSICS_MODEL_TEMPLATE, physicsTemplate); + rVal.putData(EntityDataStrings.PHYSICS_COLLIDABLE, collidable); + rVal.putData(EntityDataStrings.PHYSICS_MASS, mass); + + Globals.clientState.clientScene.registerEntityToTag(rVal, EntityTags.COLLIDABLE); + engine.registerCollisionObject(geom, collidable, EntityUtils.getPosition(rVal)); + } break; + } + //if we successfully attached the body, add a sync tree + ClientPhysicsSyncTree.attachTree(rVal); + if(ClientGravityTree.hasClientGravityTree(rVal)){ + ClientGravityTree.getClientGravityTree(rVal).updatePhysicsPair(PhysicsEntityUtils.getCollidable(rVal),PhysicsEntityUtils.getDBody(rVal)); + } + CollisionEngine.unlockOde(); + return geom; + } + /** * [SERVER ONLY] Attaches a collidable template to a given entity @@ -599,11 +621,9 @@ public class PhysicsEntityUtils { } break; } //if we successfully attached the body, add a sync tree - if(rigidBody != null){ - ServerPhysicsSyncTree.attachTree(rVal); - if(ServerGravityTree.hasServerGravityTree(rVal)){ - ServerGravityTree.getServerGravityTree(rVal).updatePhysicsPair(PhysicsEntityUtils.getCollidable(rVal),PhysicsEntityUtils.getDBody(rVal)); - } + ServerPhysicsSyncTree.attachTree(rVal); + if(ServerGravityTree.hasServerGravityTree(rVal)){ + ServerGravityTree.getServerGravityTree(rVal).updatePhysicsPair(PhysicsEntityUtils.getCollidable(rVal),PhysicsEntityUtils.getDBody(rVal)); } } CollisionEngine.unlockOde(); @@ -726,6 +746,10 @@ public class PhysicsEntityUtils { realm.getCollisionEngine().registerCollisionObject(geom, collidable, position); } break; } + ServerPhysicsSyncTree.attachTree(rVal); + if(ServerGravityTree.hasServerGravityTree(rVal)){ + ServerGravityTree.getServerGravityTree(rVal).updatePhysicsPair(PhysicsEntityUtils.getCollidable(rVal),PhysicsEntityUtils.getDBody(rVal)); + } CollisionEngine.unlockOde(); return geom; } diff --git a/src/main/java/electrosphere/entity/state/lod/ClientLODComponent.java b/src/main/java/electrosphere/entity/state/lod/ClientLODComponent.java index c219d4ba..8c6dfd57 100644 --- a/src/main/java/electrosphere/entity/state/lod/ClientLODComponent.java +++ b/src/main/java/electrosphere/entity/state/lod/ClientLODComponent.java @@ -13,6 +13,7 @@ import electrosphere.entity.Entity; import electrosphere.net.synchronization.enums.BehaviorTreeIdEnums; import electrosphere.entity.btree.BehaviorTree; import electrosphere.entity.types.common.CommonEntityUtils; +import electrosphere.entity.types.creature.CreatureUtils; import electrosphere.net.synchronization.annotation.SyncedField; import electrosphere.net.synchronization.annotation.SynchronizedBehaviorTree; @@ -76,6 +77,13 @@ public class ClientLODComponent implements BehaviorTree { PhysicsEntityUtils.getCollidable(this.parent) ); } + CommonEntityType type = CommonEntityUtils.getCommonData(this.parent); + if(type.getCollidable() != null){ + if(CreatureUtils.hasControllerPlayerId(parent)){ + throw new Error("Should not be attaching a geometry to a player entity!"); + } + PhysicsEntityUtils.clientAttachGeom(parent, type.getCollidable(), EntityUtils.getPosition(parent)); + } } } } diff --git a/src/main/java/electrosphere/entity/state/physicssync/ClientPhysicsSyncTree.java b/src/main/java/electrosphere/entity/state/physicssync/ClientPhysicsSyncTree.java index 31f25c4a..8ef8d186 100644 --- a/src/main/java/electrosphere/entity/state/physicssync/ClientPhysicsSyncTree.java +++ b/src/main/java/electrosphere/entity/state/physicssync/ClientPhysicsSyncTree.java @@ -3,6 +3,7 @@ package electrosphere.entity.state.physicssync; import org.joml.Quaterniond; import org.joml.Vector3d; import org.ode4j.ode.DBody; +import org.ode4j.ode.DGeom; import electrosphere.client.entity.camera.CameraEntityUtils; import electrosphere.client.terrain.foliage.FoliageCellManager; @@ -59,6 +60,7 @@ public class ClientPhysicsSyncTree implements BehaviorTree { Vector3d angularForce = new Vector3d(); boolean enabled = latestMessage.getbodyEnabled(); DBody body = PhysicsEntityUtils.getDBody(parent); + DGeom geom = PhysicsEntityUtils.getDGeom(parent); // //bust distance caches if this is the player's entity and we've traveled a long distance suddenly @@ -77,7 +79,12 @@ public class ClientPhysicsSyncTree implements BehaviorTree { //Synchronize data EntityUtils.setPosition(parent, position); EntityUtils.getRotation(parent).set(rotationFromServer); - PhysicsUtils.synchronizeData(Globals.clientState.clientSceneWrapper.getCollisionEngine(), body, position, rotationFromServer, linearVelocity, angularVelocity, linearForce, angularForce, enabled); + if(body != null){ + PhysicsUtils.synchronizeData(Globals.clientState.clientSceneWrapper.getCollisionEngine(), body, position, rotationFromServer, linearVelocity, angularVelocity, linearForce, angularForce, enabled); + } + if(geom != null){ + PhysicsUtils.setGeomTransform(Globals.clientState.clientSceneWrapper.getCollisionEngine(), position, rotationFromServer, geom); + } // //update facing vector if relevant diff --git a/src/main/java/electrosphere/entity/state/physicssync/ServerPhysicsSyncTree.java b/src/main/java/electrosphere/entity/state/physicssync/ServerPhysicsSyncTree.java index 0bf1a02a..5512eadd 100644 --- a/src/main/java/electrosphere/entity/state/physicssync/ServerPhysicsSyncTree.java +++ b/src/main/java/electrosphere/entity/state/physicssync/ServerPhysicsSyncTree.java @@ -3,6 +3,7 @@ package electrosphere.entity.state.physicssync; import org.joml.Quaterniond; import org.joml.Vector3d; import org.ode4j.ode.DBody; +import org.ode4j.ode.DGeom; import electrosphere.collision.PhysicsEntityUtils; import electrosphere.collision.PhysicsUtils; @@ -44,8 +45,8 @@ public class ServerPhysicsSyncTree implements BehaviorTree { Vector3d position = EntityUtils.getPosition(parent); Quaterniond rotation = EntityUtils.getRotation(parent); DBody body = PhysicsEntityUtils.getDBody(parent); - if(body == null){ - } else { + DGeom geom = PhysicsEntityUtils.getDGeom(parent); + if(body != null){ //velocities Vector3d linearVel = PhysicsUtils.odeVecToJomlVec(body.getLinearVel()); Vector3d angularVel = PhysicsUtils.odeVecToJomlVec(body.getAngularVel()); @@ -83,6 +84,38 @@ public class ServerPhysicsSyncTree implements BehaviorTree { ); } } + if(geom != null){ + if(position.distance(lastSentPosition) > UPDATE_THRESHOLD || 1.0 - rotation.dot(lastSentRotation) > UPDATE_THRESHOLD){ + lastSentPosition.set(position); + lastSentRotation.set(rotation); + DataCellSearchUtils.getEntityDataCell(parent).broadcastNetworkMessage( + EntityMessage.constructsyncPhysicsMessage( + parent.getId(), + Globals.engineState.timekeeper.getNumberOfSimFramesElapsed(), + position.x, + position.y, + position.z, + rotation.x, + rotation.y, + rotation.z, + rotation.w, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + false + ) + ); + } + } } /**