multiple hitboxes per bone + data fixes

This commit is contained in:
austin 2024-08-13 14:09:18 -04:00
parent 06b068643e
commit 0b2086a1d4
4 changed files with 202 additions and 79 deletions

View File

@ -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" : [

View File

@ -69,6 +69,11 @@
"bone": "Blade2",
"radius": 0.04
},
{
"type": "hit_connected",
"bone": "Blade3",
"radius": 0.04
},
{
"type": "hit_connected",
"bone": "Blade3",

View File

@ -551,6 +551,7 @@ Movement tweaks
(08/13/2024)
Hitbox support offsets now
Multiple hitboxes per bone
# TODO

View File

@ -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<DGeom> geoms = new LinkedList<DGeom>();
//the map of bone -> hitbox shape in ode4j
Map<String,DGeom> hitboxGeomMap = new HashMap<String,DGeom>();
//the map of bone -> list of states of individual shapes attached to that bone
Map<String,List<HitboxState>> boneHitboxMap = new HashMap<String,List<HitboxState>>();
//map of hitbox state -> geometry
Map<HitboxState,DGeom> stateGeomMap = new HashMap<HitboxState,DGeom>();
//the map of geometry -> hitbox shape status, useful for finding data about a given hitbox during collision
Map<DGeom,HitboxState> geomStateMap = new HashMap<DGeom,HitboxState>();
@ -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<String> getBones(){
return this.hitboxGeomMap.keySet();
private List<HitboxState> 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<HitboxState> states = new LinkedList<HitboxState>();
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;
}
}
}
/**