diff --git a/docs/src/progress/renderertodo.md b/docs/src/progress/renderertodo.md index 37881d97..251aff42 100644 --- a/docs/src/progress/renderertodo.md +++ b/docs/src/progress/renderertodo.md @@ -1436,6 +1436,7 @@ Unarmed combat GriddedDataCellTrackingData created Reduce allocations in GriddedDataCellManager methods that loop cells Implement max distance for queueing for simulation +Client hitbox body destruction based on distance from player (for performance) diff --git a/src/main/java/electrosphere/collision/CollisionEngine.java b/src/main/java/electrosphere/collision/CollisionEngine.java index ffbd0113..8936a50e 100644 --- a/src/main/java/electrosphere/collision/CollisionEngine.java +++ b/src/main/java/electrosphere/collision/CollisionEngine.java @@ -776,6 +776,50 @@ public class CollisionEngine { ServerPhysicsSyncTree.detachTree(e, ServerPhysicsSyncTree.getTree(e)); } } + + /** + * Destroys a body + collidable pair + * @param body The body + * @param collidable The collidable + */ + protected void destroyPhysicsPair(DBody body, Collidable collidable){ + spaceLock.lock(); + this.deregisterCollisionObject(body, collidable); + this.destroyDBody(body); + spaceLock.unlock(); + } + + /** + * Disables a body + * @param body The body + */ + protected void disable(DBody body){ + spaceLock.lock(); + body.disable(); + spaceLock.unlock(); + } + + /** + * Enables a body + * @param body The body + */ + protected void enable(DBody body){ + spaceLock.lock(); + body.enable(); + spaceLock.unlock(); + } + + /** + * Checks if a body is enabled + * @param body The body + * @return true if it is enabled, false otherwise + */ + protected boolean isEnabled(DBody body){ + spaceLock.lock(); + boolean rVal = body.isEnabled(); + spaceLock.unlock(); + return rVal; + } /** * Creates a trimesh from a given set of vertices and indices diff --git a/src/main/java/electrosphere/collision/PhysicsUtils.java b/src/main/java/electrosphere/collision/PhysicsUtils.java index 7aeb8793..27b1ca4f 100644 --- a/src/main/java/electrosphere/collision/PhysicsUtils.java +++ b/src/main/java/electrosphere/collision/PhysicsUtils.java @@ -8,6 +8,7 @@ import org.ode4j.math.DQuaternion; import org.ode4j.math.DQuaternionC; import org.ode4j.ode.DBody; +import electrosphere.collision.collidable.Collidable; import electrosphere.game.data.collidable.CollidableTemplate; /** @@ -120,5 +121,42 @@ public class PhysicsUtils { collisionEngine.destroyDBody(body); } + /** + * Destroys a body + collidable pair + * @param body The body + * @param collidable The collidable + */ + public static void destroyPhysicsPair(CollisionEngine collisionEngine, DBody body, Collidable collidable){ + collisionEngine.destroyPhysicsPair(body, collidable); + } + + /** + * Disables a body + * @param collisionEngine The collision engine + * @param body The body + */ + public static void disableBody(CollisionEngine collisionEngine, DBody body){ + collisionEngine.disable(body); + } + + /** + * Enables a body + * @param collisionEngine The collision engine + * @param body The body + */ + public static void enableBody(CollisionEngine collisionEngine, DBody body){ + collisionEngine.enable(body); + } + + /** + * Checks if a body is enabled + * @param collisionEngine The collision engine + * @param body The body + * @return true if the body is enabled, false otherwise + */ + public static boolean isBodyEnabled(CollisionEngine collisionEngine, DBody body){ + return collisionEngine.isEnabled(body); + } + } diff --git a/src/main/java/electrosphere/collision/hitbox/HitboxManager.java b/src/main/java/electrosphere/collision/hitbox/HitboxManager.java index 3737bfb5..dc01cd27 100644 --- a/src/main/java/electrosphere/collision/hitbox/HitboxManager.java +++ b/src/main/java/electrosphere/collision/hitbox/HitboxManager.java @@ -30,7 +30,9 @@ public class HitboxManager { */ ReentrantLock lock = new ReentrantLock(); - //an id incrementer for hitboxes + /** + * An incrementer for hitbox IDs + */ long idIncrementer = 0; /** diff --git a/src/main/java/electrosphere/entity/state/collidable/ClientCollidableTree.java b/src/main/java/electrosphere/entity/state/collidable/ClientCollidableTree.java index 4a260459..6fb19f90 100644 --- a/src/main/java/electrosphere/entity/state/collidable/ClientCollidableTree.java +++ b/src/main/java/electrosphere/entity/state/collidable/ClientCollidableTree.java @@ -17,28 +17,37 @@ import electrosphere.entity.btree.BehaviorTree; */ public class ClientCollidableTree implements BehaviorTree { + /** + * The parent entity of this tree + */ Entity parent; - //the ode body for this collidable tree + + /** + * The body for this tree + */ DBody body; + + /** + * The collidable for this tree + */ Collidable collidable; - boolean applyImpulses = true; - + /** + * Constructor + * @param e The entity + * @param collidable The collidable + * @param body The body + */ public ClientCollidableTree(Entity e, Collidable collidable, DBody body){ parent = e; this.collidable = collidable; this.body = body; } - public ClientCollidableTree(Entity e, Collidable collidable, DBody body, boolean applyImpulses){ - parent = e; - this.collidable = collidable; - this.body = body; - this.applyImpulses = applyImpulses; - } - - static int incrementer = 0; - + + /** + * Simulates the component + */ public void simulate(float deltaTime){ Vector3d position = EntityUtils.getPosition(parent); Quaterniond rotation = EntityUtils.getRotation(parent); @@ -68,10 +77,20 @@ public class ClientCollidableTree implements BehaviorTree { this.collidable = collidable; } + /** + * Checks if the entity has a collidable tree + * @param e The entity + * @return true if the entity contains a collidable tree, false otherwise + */ public static boolean hasClientCollidableTree(Entity e){ return e.containsKey(EntityDataStrings.CLIENT_COLLIDABLE_TREE); } + /** + * Gets the collidable tree on the entity + * @param e The entity + * @return The tree + */ public static ClientCollidableTree getClientCollidableTree(Entity e){ return (ClientCollidableTree)e.getData(EntityDataStrings.CLIENT_COLLIDABLE_TREE); } diff --git a/src/main/java/electrosphere/entity/state/hitbox/HitboxCollectionState.java b/src/main/java/electrosphere/entity/state/hitbox/HitboxCollectionState.java index 30148c9f..c106604c 100644 --- a/src/main/java/electrosphere/entity/state/hitbox/HitboxCollectionState.java +++ b/src/main/java/electrosphere/entity/state/hitbox/HitboxCollectionState.java @@ -54,22 +54,49 @@ public class HitboxCollectionState { SOUR, //less damage } - //the parent entity of the hitbox state + /** + * Distance after which hitboxes are disabled on the client + */ + public static final double CLIENT_DISABLE_DISTANCE = 40; + + /** + * Distance within which hitboxes are enabled on the client + */ + public static final double CLIENT_ENABLE_DISTANCE = 35; + + /** + * The parent entity of the hitbox state + */ Entity parent; - //the body that contains all the hitbox shapes + /** + * The body that contains all the hitbox shapes + */ DBody body; - //The collidable associated with the body + /** + * The collidable associated with the body + */ Collidable collidable; - //the list of all geoms in the collection state + /** + * The list of all geoms in the collection state + */ List geoms = new LinkedList(); - //the map of bone -> list of states of individual shapes attached to that bone + + /** + * The map of bone -> list of states of individual shapes attached to that bone + */ Map> boneHitboxMap = new HashMap>(); - //map of hitbox state -> geometry + + /** + * Map of hitbox state -> geometry + */ Map stateGeomMap = new HashMap(); - //the map of geometry -> hitbox shape status, useful for finding data about a given hitbox during collision + + /** + * The map of geometry -> hitbox shape status, useful for finding data about a given hitbox during collision + */ Map geomStateMap = new HashMap(); /** @@ -82,19 +109,34 @@ public class HitboxCollectionState { */ List nonBoneHitboxes = new LinkedList(); - //callback to provide a position for the hitbox each frame + /** + * Callback to provide a position for the hitbox each frame + */ HitboxPositionCallback positionCallback; - //controls whether the hitbox state is active or not + /** + * The raw data to pattern hitboxes off of + */ + List rawData; + + /** + * Controls whether the hitbox state is active or not + */ boolean active = true; - //controls whether active hitboxes should be overwritten with block boxes + /** + * Controls whether active hitboxes should be overwritten with block boxes + */ boolean blockOverride = false; - //the associated manager + /** + * The associated manager + */ HitboxManager manager; - //controls whether this hitbox collection thinks its on the server or client + /** + * Controls whether this hitbox collection thinks its on the server or client + */ boolean isServer = true; @@ -107,94 +149,15 @@ public class HitboxCollectionState { */ public static HitboxCollectionState attachHitboxState(HitboxManager manager, boolean isServer, Entity entity, List hitboxListRaw){ HitboxCollectionState rVal = new HitboxCollectionState(); - + rVal.rawData = hitboxListRaw; + rVal.manager = manager; rVal.isServer = isServer; - //create the shapes - for(HitboxData hitboxDataRaw : hitboxListRaw){ - DGeom geom = null; - HitboxType type = HitboxType.HIT; - HitboxShapeType shapeType = HitboxShapeType.SPHERE; - // - //Get the type as an enum - // - switch(hitboxDataRaw.getType()){ - case HitboxData.HITBOX_TYPE_HIT: { - type = HitboxType.HIT; - shapeType = HitboxShapeType.SPHERE; - geom = CollisionBodyCreation.createShapeSphere(manager.getCollisionEngine(), hitboxDataRaw.getRadius(), Collidable.TYPE_OBJECT_BIT); - } break; - case HitboxData.HITBOX_TYPE_HURT: { - type = HitboxType.HURT; - shapeType = HitboxShapeType.SPHERE; - geom = CollisionBodyCreation.createShapeSphere(manager.getCollisionEngine(), hitboxDataRaw.getRadius(), Collidable.TYPE_OBJECT_BIT); - } break; - case HitboxData.HITBOX_TYPE_HIT_CONNECTED: { - type = HitboxType.HIT; - shapeType = HitboxShapeType.CAPSULE; - geom = CollisionBodyCreation.createShapeSphere(manager.getCollisionEngine(), hitboxDataRaw.getRadius(), Collidable.TYPE_OBJECT_BIT); - } break; - case HitboxData.HITBOX_TYPE_HURT_CONNECTED: { - type = HitboxType.HURT; - shapeType = HitboxShapeType.CAPSULE; - geom = CollisionBodyCreation.createShapeSphere(manager.getCollisionEngine(), hitboxDataRaw.getRadius(), Collidable.TYPE_OBJECT_BIT); - } break; - case HitboxData.HITBOX_TYPE_STATIC_CAPSULE: { - type = HitboxType.HURT; - shapeType = HitboxShapeType.STATIC_CAPSULE; - geom = CollisionBodyCreation.createCapsuleShape(manager.getCollisionEngine(), hitboxDataRaw.getRadius(), hitboxDataRaw.getLength(), Collidable.TYPE_OBJECT_BIT); - } break; - } - // - //Get the subtype as an enum - // - HitboxSubtype subType; - String subTypeRaw = hitboxDataRaw.getSubType(); - if(subTypeRaw == null){ - subTypeRaw = HitboxData.HITBOX_SUBTYPE_REUGLAR; - } - switch(subTypeRaw){ - case HitboxData.HITBOX_SUBTYPE_SWEET: { - subType = HitboxSubtype.SWEET; - } break; - case HitboxData.HITBOX_SUBTYPE_REUGLAR: { - subType = HitboxSubtype.REGULAR; - } break; - case HitboxData.HITBOX_SUBTYPE_SOUR: { - subType = HitboxSubtype.SOUR; - } break; - default: { - subType = HitboxSubtype.REGULAR; - } break; - } - - //create the state for the individual shape - HitboxState state = new HitboxState(hitboxDataRaw.getBone(), hitboxDataRaw, type, subType, shapeType, false); - - //add to structures - if(hitboxDataRaw.getBone() != null){ - rVal.addHitbox(hitboxDataRaw.getBone(),state); - } else { - rVal.addHitbox(state); - } - rVal.registerGeom(geom,state); - } - - //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); - manager.getCollisionEngine().registerCollisionObject(rVal.body, collidable); - //attach entity.putData(EntityDataStrings.HITBOX_DATA, rVal); rVal.parent = entity; - //register + rVal.createBody(); manager.registerHitbox(rVal); - rVal.manager = manager; return rVal; } @@ -238,6 +201,110 @@ public class HitboxCollectionState { // return rVal; } + /** + * Creates the body + */ + private void createBody(){ + CollisionEngine collisionEngine = this.manager.getCollisionEngine(); + //create the shapes + for(HitboxData hitboxDataRaw : this.rawData){ + DGeom geom = null; + HitboxType type = HitboxType.HIT; + HitboxShapeType shapeType = HitboxShapeType.SPHERE; + // + //Get the type as an enum + // + switch(hitboxDataRaw.getType()){ + case HitboxData.HITBOX_TYPE_HIT: { + type = HitboxType.HIT; + shapeType = HitboxShapeType.SPHERE; + geom = CollisionBodyCreation.createShapeSphere(collisionEngine, hitboxDataRaw.getRadius(), Collidable.TYPE_OBJECT_BIT); + } break; + case HitboxData.HITBOX_TYPE_HURT: { + type = HitboxType.HURT; + shapeType = HitboxShapeType.SPHERE; + geom = CollisionBodyCreation.createShapeSphere(collisionEngine, hitboxDataRaw.getRadius(), Collidable.TYPE_OBJECT_BIT); + } break; + case HitboxData.HITBOX_TYPE_HIT_CONNECTED: { + type = HitboxType.HIT; + shapeType = HitboxShapeType.CAPSULE; + geom = CollisionBodyCreation.createShapeSphere(collisionEngine, hitboxDataRaw.getRadius(), Collidable.TYPE_OBJECT_BIT); + } break; + case HitboxData.HITBOX_TYPE_HURT_CONNECTED: { + type = HitboxType.HURT; + shapeType = HitboxShapeType.CAPSULE; + geom = CollisionBodyCreation.createShapeSphere(collisionEngine, hitboxDataRaw.getRadius(), Collidable.TYPE_OBJECT_BIT); + } break; + case HitboxData.HITBOX_TYPE_STATIC_CAPSULE: { + type = HitboxType.HURT; + shapeType = HitboxShapeType.STATIC_CAPSULE; + geom = CollisionBodyCreation.createCapsuleShape(collisionEngine, hitboxDataRaw.getRadius(), hitboxDataRaw.getLength(), Collidable.TYPE_OBJECT_BIT); + } break; + } + // + //Get the subtype as an enum + // + HitboxSubtype subType; + String subTypeRaw = hitboxDataRaw.getSubType(); + if(subTypeRaw == null){ + subTypeRaw = HitboxData.HITBOX_SUBTYPE_REUGLAR; + } + switch(subTypeRaw){ + case HitboxData.HITBOX_SUBTYPE_SWEET: { + subType = HitboxSubtype.SWEET; + } break; + case HitboxData.HITBOX_SUBTYPE_REUGLAR: { + subType = HitboxSubtype.REGULAR; + } break; + case HitboxData.HITBOX_SUBTYPE_SOUR: { + subType = HitboxSubtype.SOUR; + } break; + default: { + subType = HitboxSubtype.REGULAR; + } break; + } + + //create the state for the individual shape + HitboxState state = new HitboxState(hitboxDataRaw.getBone(), hitboxDataRaw, type, subType, shapeType, false); + + //add to structures + if(hitboxDataRaw.getBone() != null){ + this.addHitbox(hitboxDataRaw.getBone(),state); + } else { + this.addHitbox(state); + } + this.registerGeom(geom,state); + } + + //create body with all the shapes + DGeom[] geomArray = this.geoms.toArray(new DGeom[this.geoms.size()]); + this.body = CollisionBodyCreation.createBodyWithShapes(collisionEngine, geomArray); + CollisionBodyCreation.setKinematic(collisionEngine, this.body); + + //register collidable with collision engine + this.collidable = new Collidable(this.parent, Collidable.TYPE_OBJECT, true); + collisionEngine.registerCollisionObject(this.body, this.collidable); + } + + /** + * Destroys the body + */ + private void destroyBody(){ + //destroy body + if(this.body != null){ + PhysicsUtils.destroyPhysicsPair(this.manager.getCollisionEngine(), this.body, this.collidable); + //clear tracking structures + this.geoms.clear(); + this.boneHitboxMap.clear(); + this.stateGeomMap.clear(); + this.geomStateMap.clear(); + this.allStates.clear(); + this.nonBoneHitboxes.clear(); + this.body = null; + this.collidable = null; + } + } + /** * Clears the collision status of all shapes */ @@ -255,38 +322,49 @@ public class HitboxCollectionState { if(parent != null && !isServer && EntityUtils.getActor(parent) != null){ if(!this.geoms.isEmpty()){ Vector3d entityPosition = EntityUtils.getPosition(parent); - PhysicsUtils.setRigidBodyTransform(collisionEngine, entityPosition, new Quaterniond(), body); - for(String boneName : this.boneHitboxMap.keySet()){ - if(EntityUtils.getActor(parent).containsBone(boneName)){ - Vector3d bonePosition = EntityUtils.getActor(parent).getBonePosition(boneName); - for(HitboxState state : this.boneHitboxMap.get(boneName)){ - DGeom geom = this.stateGeomMap.get(state); - HitboxState shapeStatus = this.geomStateMap.get(geom); - switch(shapeStatus.shapeType){ - case SPHERE: { - this.updateSphereShapePosition(collisionEngine,boneName,shapeStatus,bonePosition); - } break; - case CAPSULE: { - this.updateCapsuleShapePosition(collisionEngine,boneName,shapeStatus,bonePosition); - } break; - case STATIC_CAPSULE: { - } break; + Vector3d playerPos = EntityUtils.getPosition(Globals.playerEntity); + double distanceToPlayer = entityPosition.distance(playerPos); + if(distanceToPlayer > CLIENT_DISABLE_DISTANCE){ + if(this.body != null){ + this.destroyBody(); + } + } else if(distanceToPlayer < CLIENT_ENABLE_DISTANCE) { + if(this.body == null){ + this.createBody(); + } + PhysicsUtils.setRigidBodyTransform(collisionEngine, entityPosition, new Quaterniond(), body); + for(String boneName : this.boneHitboxMap.keySet()){ + if(EntityUtils.getActor(parent).containsBone(boneName)){ + Vector3d bonePosition = EntityUtils.getActor(parent).getBonePosition(boneName); + for(HitboxState state : this.boneHitboxMap.get(boneName)){ + DGeom geom = this.stateGeomMap.get(state); + HitboxState shapeStatus = this.geomStateMap.get(geom); + switch(shapeStatus.shapeType){ + case SPHERE: { + this.updateSphereShapePosition(collisionEngine,boneName,shapeStatus,bonePosition); + } break; + case CAPSULE: { + this.updateCapsuleShapePosition(collisionEngine,boneName,shapeStatus,bonePosition); + } break; + case STATIC_CAPSULE: { + } break; + } } } } - } - for(HitboxState state : this.nonBoneHitboxes){ - DGeom geom = this.stateGeomMap.get(state); - HitboxState shapeStatus = this.geomStateMap.get(geom); - switch(shapeStatus.shapeType){ - case SPHERE: { - this.updateSphereShapePosition(collisionEngine,null,shapeStatus,null); - } break; - case CAPSULE: { - this.updateCapsuleShapePosition(collisionEngine,null,shapeStatus,null); - } break; - case STATIC_CAPSULE: { - } break; + for(HitboxState state : this.nonBoneHitboxes){ + DGeom geom = this.stateGeomMap.get(state); + HitboxState shapeStatus = this.geomStateMap.get(geom); + switch(shapeStatus.shapeType){ + case SPHERE: { + this.updateSphereShapePosition(collisionEngine,null,shapeStatus,null); + } break; + case CAPSULE: { + this.updateCapsuleShapePosition(collisionEngine,null,shapeStatus,null); + } break; + case STATIC_CAPSULE: { + } break; + } } } } @@ -506,6 +584,9 @@ public class HitboxCollectionState { * @return the hitbox state if it exists */ public static HitboxCollectionState getHitboxState(Entity entity){ + if(!entity.containsKey(EntityDataStrings.HITBOX_DATA)){ + return null; + } return (HitboxCollectionState)entity.getData(EntityDataStrings.HITBOX_DATA); } @@ -549,7 +630,7 @@ public class HitboxCollectionState { } else { engine = Globals.clientSceneWrapper.getHitboxManager().getCollisionEngine(); } - if(engine != null){ + if(engine != null && body != null){ PhysicsUtils.destroyBody(engine, body); } } diff --git a/src/main/java/electrosphere/server/datacell/gridded/GriddedDataCellManager.java b/src/main/java/electrosphere/server/datacell/gridded/GriddedDataCellManager.java index f5347cde..347bff58 100644 --- a/src/main/java/electrosphere/server/datacell/gridded/GriddedDataCellManager.java +++ b/src/main/java/electrosphere/server/datacell/gridded/GriddedDataCellManager.java @@ -361,7 +361,6 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager public boolean updatePlayerPositions(){ Globals.profiler.beginCpuSample("GriddedDataCellManager.updatePlayerPositions - Reset chunk distances"); loadedCellsLock.lock(); - Globals.profiler.beginCpuSample("GriddedDataCellManager.updatePlayerPositions - Remove from old cells"); for(ServerDataCell cell : this.groundDataCells.values()){ GriddedDataCellTrackingData trackingData = this.cellTrackingMap.get(cell); trackingData.setClosestPlayer(GriddedDataCellTrackingData.REALLY_LARGE_DISTANCE);