From 0b2086a1d41ae7e9a824705a5a2ed9ebfbf60343 Mon Sep 17 00:00:00 2001 From: austin Date: Tue, 13 Aug 2024 14:09:18 -0400 Subject: [PATCH] multiple hitboxes per bone + data fixes --- assets/Data/creatures/human.json | 43 ++-- assets/Data/items.json | 5 + docs/src/progress/renderertodo.md | 1 + .../state/hitbox/HitboxCollectionState.java | 232 +++++++++++++----- 4 files changed, 202 insertions(+), 79 deletions(-) diff --git a/assets/Data/creatures/human.json b/assets/Data/creatures/human.json index 46425cb9..fca7a232 100644 --- a/assets/Data/creatures/human.json +++ b/assets/Data/creatures/human.json @@ -15,22 +15,32 @@ }, { "type": "hurt", - "bone": "Leg.L", - "radius": 0.04 - }, - { - "type": "hurt", - "bone": "Leg.R", - "radius": 0.04 - }, - { - "type": "hurt", - "bone": "Shoulder.L", + "bone": "Forearm.L", "radius": 0.06 }, { "type": "hurt", - "bone": "Shoulder.R", + "bone": "Forearm.R", + "radius": 0.06 + }, + { + "type": "hurt", + "bone": "Leg.L", + "radius": 0.06 + }, + { + "type": "hurt", + "bone": "Leg.R", + "radius": 0.06 + }, + { + "type": "hurt", + "bone": "LowerLeg.L", + "radius": 0.06 + }, + { + "type": "hurt", + "bone": "LowerLeg.R", "radius": 0.06 }, { @@ -38,16 +48,11 @@ "bone": "Neck", "radius": 0.06 }, - { - "type": "hurt", - "bone": "Bone", - "radius": 0.08 - }, { "type": "hurt", "bone": "Head", - "radius": 0.06, - "offset": [0.0,3.0,0.0] + "radius": 0.07, + "offset": [0.0,0.13,0.0] } ], "tokens" : [ diff --git a/assets/Data/items.json b/assets/Data/items.json index 41da1f73..9d058978 100644 --- a/assets/Data/items.json +++ b/assets/Data/items.json @@ -69,6 +69,11 @@ "bone": "Blade2", "radius": 0.04 }, + { + "type": "hit_connected", + "bone": "Blade3", + "radius": 0.04 + }, { "type": "hit_connected", "bone": "Blade3", diff --git a/docs/src/progress/renderertodo.md b/docs/src/progress/renderertodo.md index 54f55ebd..7d89c0ef 100644 --- a/docs/src/progress/renderertodo.md +++ b/docs/src/progress/renderertodo.md @@ -551,6 +551,7 @@ Movement tweaks (08/13/2024) Hitbox support offsets now +Multiple hitboxes per bone # TODO diff --git a/src/main/java/electrosphere/entity/state/hitbox/HitboxCollectionState.java b/src/main/java/electrosphere/entity/state/hitbox/HitboxCollectionState.java index 27f29ca5..d48630ce 100644 --- a/src/main/java/electrosphere/entity/state/hitbox/HitboxCollectionState.java +++ b/src/main/java/electrosphere/entity/state/hitbox/HitboxCollectionState.java @@ -1,10 +1,10 @@ package electrosphere.entity.state.hitbox; +import java.util.Arrays; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; -import java.util.Set; import org.joml.Quaterniond; import org.joml.Vector3d; @@ -64,8 +64,10 @@ public class HitboxCollectionState { //the list of all geoms in the collection state List geoms = new LinkedList(); - //the map of bone -> hitbox shape in ode4j - Map hitboxGeomMap = new HashMap(); + //the map of bone -> list of states of individual shapes attached to that bone + Map> boneHitboxMap = new HashMap>(); + //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 Map geomStateMap = new HashMap(); @@ -154,11 +156,16 @@ public class HitboxCollectionState { } 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.hitboxGeomMap.put(hitboxDataRaw.getBone(),geom); + rVal.addHitbox(hitboxDataRaw.getBone(),state); + } else { + LoggerInterface.loggerEngine.WARNING("Trying to attach hitbox to bone where bone cannot be found: " + hitboxDataRaw.getBone()); } - rVal.geoms.add(geom); - rVal.geomStateMap.put(geom,new HitboxState(hitboxDataRaw.getBone(), hitboxDataRaw, type, subType, shapeType, false)); + rVal.registerGeom(geom,state); } //create body with all the shapes @@ -189,28 +196,34 @@ public class HitboxCollectionState { * @return The hitbox state that has been attached to the entity */ public static HitboxCollectionState attachHitboxStateWithCallback(HitboxManager manager, CollisionEngine collisionEngine, Entity entity, HitboxData data, HitboxPositionCallback callback){ - HitboxCollectionState rVal = new HitboxCollectionState(); + throw new UnsupportedOperationException("Not yet implemented!"); + // HitboxCollectionState rVal = new HitboxCollectionState(); - //create the shapes - rVal.hitboxGeomMap.put(data.getBone(),CollisionBodyCreation.createShapeSphere(collisionEngine, data.getRadius(), Collidable.TYPE_OBJECT_BIT)); + // //create the shapes + // DGeom geom = CollisionBodyCreation.createShapeSphere(collisionEngine, data.getRadius(), Collidable.TYPE_OBJECT_BIT); - //create body with all the shapes - DGeom[] geomArray = rVal.hitboxGeomMap.values().toArray(new DGeom[rVal.hitboxGeomMap.values().size()]); - rVal.body = CollisionBodyCreation.createBodyWithShapes(collisionEngine, geomArray); + // //create the state for the individual shape + // HitboxState state = new HitboxState(data.getBone(), data, getType(data), getSubType(data), getShapeType(data), false); + // rVal.addHitbox(data.getBone(), state); + // rVal.registerGeom(geom,state); - //register collidable with collision engine - Collidable collidable = new Collidable(entity, Collidable.TYPE_OBJECT); - collisionEngine.registerCollisionObject(rVal.body, collidable); + // //create body with all the shapes + // DGeom[] geomArray = rVal.geoms.toArray(new DGeom[rVal.geoms.size()]); + // rVal.body = CollisionBodyCreation.createBodyWithShapes(collisionEngine, geomArray); - //attach - entity.putData(EntityDataStrings.HITBOX_DATA, rVal); - rVal.parent = entity; + // //register collidable with collision engine + // Collidable collidable = new Collidable(entity, Collidable.TYPE_OBJECT); + // collisionEngine.registerCollisionObject(rVal.body, collidable); - //register - manager.registerHitbox(rVal); - rVal.manager = manager; + // //attach + // entity.putData(EntityDataStrings.HITBOX_DATA, rVal); + // rVal.parent = entity; - return rVal; + // //register + // manager.registerHitbox(rVal); + // rVal.manager = manager; + + // return rVal; } /** @@ -228,22 +241,24 @@ public class HitboxCollectionState { */ public void updateHitboxPositions(CollisionEngine collisionEngine){ if(parent != null && !isServer && EntityUtils.getActor(parent) != null){ - if(!this.hitboxGeomMap.isEmpty()){ + if(!this.geoms.isEmpty()){ Vector3d entityPosition = EntityUtils.getPosition(parent); this.body.setPosition(PhysicsUtils.jomlVecToOdeVec(entityPosition)); - for(String boneName : this.hitboxGeomMap.keySet()){ + for(String boneName : this.boneHitboxMap.keySet()){ Vector3f bonePosition = EntityUtils.getActor(parent).getBonePosition(boneName); - DGeom geom = this.hitboxGeomMap.get(boneName); - 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.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; + } } } } else if(positionCallback != null){ @@ -254,22 +269,24 @@ public class HitboxCollectionState { PhysicsEntityUtils.setGeometryPosition(collisionEngine, geom, worldPosition, rotation); } } else if(parent != null && isServer && EntityUtils.getPoseActor(parent) != null){ - if(!this.hitboxGeomMap.isEmpty()){ + if(!this.geoms.isEmpty()){ Vector3d entityPosition = EntityUtils.getPosition(parent); this.body.setPosition(PhysicsUtils.jomlVecToOdeVec(entityPosition)); - for(String boneName : this.hitboxGeomMap.keySet()){ + for(String boneName : this.boneHitboxMap.keySet()){ Vector3f bonePosition = EntityUtils.getPoseActor(parent).getBonePosition(boneName); - DGeom geom = this.hitboxGeomMap.get(boneName); - 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.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; + } } } } else if(positionCallback != null){ @@ -313,7 +330,7 @@ public class HitboxCollectionState { * @param bonePosition the position of the bone */ private void updateSphereShapePosition(CollisionEngine collisionEngine, String boneName, HitboxState hitboxState, Vector3f bonePosition){ - DGeom geom = this.hitboxGeomMap.get(boneName); + DGeom geom = this.stateGeomMap.get(hitboxState); //get offset's transform Vector3d offsetPosition = DataFormatUtil.getDoubleListAsVector(hitboxState.getHitboxData().getOffset()); @@ -344,7 +361,7 @@ public class HitboxCollectionState { private void updateCapsuleShapePosition(CollisionEngine collisionEngine, String boneName, HitboxState hitboxState, Vector3f bonePosition){ //get data about the hitbox - DGeom geom = this.hitboxGeomMap.get(boneName); + DGeom geom = this.stateGeomMap.get(hitboxState); Vector3d previousWorldPos = hitboxState.getPreviousWorldPos(); double length = hitboxState.getHitboxData().getRadius(); @@ -418,7 +435,7 @@ public class HitboxCollectionState { CollisionBodyCreation.attachGeomToBody(collisionEngine,body,geom); } //update maps and other variables for next frame - this.hitboxGeomMap.put(boneName,geom); + this.stateGeomMap.put(hitboxState,geom); this.geomStateMap.put(geom,hitboxState); this.geoms.add(geom); hitboxState.setPreviousWorldPos(worldPosition); @@ -514,20 +531,115 @@ public class HitboxCollectionState { } /** - * Gets the set of bone names in the state data - * @return The set of bone names in the state data + * Gets the hitboxes associated with a bone + * @param bone The bone + * @return The list of hitboxes if at least one is present, null otherwise */ - public Set getBones(){ - return this.hitboxGeomMap.keySet(); + private List getHitboxes(String bone){ + return this.boneHitboxMap.get(bone); } /** - * Gets geometry on a single hitbox based on its bone name - * @param boneName the bone name - * @return the hitbox geometry + * Adds a hitbox to a bone + * @param bone The bone + * @param state The hitbox */ - public DGeom getGeometry(String boneName){ - return this.hitboxGeomMap.get(boneName); + private void addHitbox(String bone, HitboxState state){ + if(this.boneHitboxMap.containsKey(bone)){ + this.boneHitboxMap.get(bone).add(state); + } else { + List states = new LinkedList(); + states.add(state); + this.boneHitboxMap.put(bone,states); + } + } + + /** + * Registers a geometry + * @param geom The geom + * @param state The state associated with the geom + */ + private void registerGeom(DGeom geom, HitboxState state){ + this.geoms.add(geom); + this.geomStateMap.put(geom,state); + this.stateGeomMap.put(state,geom); + } + + /** + * Gets the hitbox type of a given hitbox data + * @param data The data + * @return The type of hitbox + */ + private static HitboxType getType(HitboxData data){ + switch(data.getType()){ + case HitboxData.HITBOX_TYPE_HIT: { + return HitboxType.HIT; + } + case HitboxData.HITBOX_TYPE_HURT: { + return HitboxType.HURT; + } + case HitboxData.HITBOX_TYPE_HIT_CONNECTED: { + return HitboxType.HIT; + } + case HitboxData.HITBOX_TYPE_HURT_CONNECTED: { + return HitboxType.HURT; + } + case HitboxData.HITBOX_TYPE_STATIC_CAPSULE: { + return HitboxType.HURT; + } + default: { + LoggerInterface.loggerEngine.ERROR(new IllegalArgumentException("Trying to parse undefined hitbox type " + data.getType())); + return HitboxType.HIT; + } + } + } + + /** + * Gets the shape type of a hitbox data object + * @param data The data + * @return The shape type + */ + private static HitboxShapeType getShapeType(HitboxData data){ + switch(data.getType()){ + case HitboxData.HITBOX_TYPE_HIT: + case HitboxData.HITBOX_TYPE_HURT: { + return HitboxShapeType.SPHERE; + } + case HitboxData.HITBOX_TYPE_HIT_CONNECTED: + case HitboxData.HITBOX_TYPE_HURT_CONNECTED: { + return HitboxShapeType.CAPSULE; + } + case HitboxData.HITBOX_TYPE_STATIC_CAPSULE: { + return HitboxShapeType.STATIC_CAPSULE; + } + default: { + LoggerInterface.loggerEngine.ERROR(new IllegalArgumentException("Trying to parse undefined hitbox shape type " + data.getType())); + return HitboxShapeType.SPHERE; + } + } + } + + /** + * Gets the hitbox subtype + * @param data The data + * @return The subtype + */ + private static HitboxSubtype getSubType(HitboxData data){ + switch(data.getSubType()){ + case HitboxData.HITBOX_SUBTYPE_SWEET: { + return HitboxSubtype.SWEET; + } + case HitboxData.HITBOX_SUBTYPE_REUGLAR: { + return HitboxSubtype.REGULAR; + } + case HitboxData.HITBOX_SUBTYPE_SOUR: { + return HitboxSubtype.SOUR; + } + default: { + LoggerInterface.loggerEngine.ERROR(new IllegalArgumentException("Trying to parse undefined hitbox subtype " + data.getSubType())); + return HitboxSubtype.REGULAR; + } + } } /**