293 lines
15 KiB
Java
293 lines
15 KiB
Java
package electrosphere.collision;
|
|
|
|
import org.joml.Matrix4d;
|
|
import org.joml.Matrix4f;
|
|
import org.joml.Vector3d;
|
|
import org.joml.Vector3f;
|
|
import org.ode4j.ode.DBody;
|
|
import org.ode4j.ode.DTriMesh;
|
|
|
|
import electrosphere.collision.collidable.Collidable;
|
|
import electrosphere.engine.Globals;
|
|
import electrosphere.entity.Entity;
|
|
import electrosphere.entity.EntityDataStrings;
|
|
import electrosphere.entity.EntityTags;
|
|
import electrosphere.entity.EntityUtils;
|
|
import electrosphere.entity.state.collidable.ClientCollidableTree;
|
|
import electrosphere.entity.state.collidable.ServerCollidableTree;
|
|
import electrosphere.entity.types.terrain.TerrainChunkData;
|
|
import electrosphere.game.data.collidable.CollidableTemplate;
|
|
import electrosphere.server.datacell.Realm;
|
|
import electrosphere.server.datacell.utils.ServerEntityTagUtils;
|
|
|
|
/**
|
|
* Utilities for performing physics-related operations on entities (particularly attaching physics to them in the first place)
|
|
*/
|
|
public class PhysicsEntityUtils {
|
|
|
|
/**
|
|
* [CLIENT ONLY] Attaches a collidable template to a given entity
|
|
* @param rVal The entity
|
|
* @param physicsTemplate The collidable template
|
|
*/
|
|
public static void clientAttachCollidableTemplate(Entity rVal, CollidableTemplate physicsTemplate){
|
|
DBody rigidBody;
|
|
Collidable collidable;
|
|
float mass = 1.0f;
|
|
Matrix4f inertiaTensor;
|
|
Vector3f scale;
|
|
switch(physicsTemplate.getType()){
|
|
case "CYLINDER": {
|
|
rigidBody = CollisionBodyCreation.createCylinderBody(
|
|
Globals.clientSceneWrapper.getCollisionEngine(),
|
|
physicsTemplate.getDimension1(),
|
|
physicsTemplate.getDimension2()
|
|
);
|
|
collidable = new Collidable(rVal, Collidable.TYPE_CREATURE);
|
|
ClientCollidableTree tree = new ClientCollidableTree(rVal,collidable,rigidBody);
|
|
rVal.putData(EntityDataStrings.PHYSICS_COLLISION_BODY, rigidBody);
|
|
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_OFFSET, new Vector3f(physicsTemplate.getOffsetX(),physicsTemplate.getOffsetY(),physicsTemplate.getOffsetZ()));
|
|
rVal.putData(EntityDataStrings.PHYSICS_COLLISION_BODY_TRANSFORM, offsetTransform);
|
|
rVal.putData(EntityDataStrings.PHYSICS_MODEL_TEMPLATE, physicsTemplate);
|
|
rVal.putData(EntityDataStrings.PHYSICS_COLLIDABLE, collidable);
|
|
rVal.putData(EntityDataStrings.CLIENT_COLLIDABLE_TREE, tree);
|
|
|
|
scale = new Vector3f(physicsTemplate.getDimension1(),physicsTemplate.getDimension2(),physicsTemplate.getDimension3());
|
|
rVal.putData(EntityDataStrings.PHYSICS_MASS, mass);
|
|
//inertia tensor
|
|
//https://scienceworld.wolfram.com/physics/MomentofInertiaCylinder.html
|
|
inertiaTensor = new Matrix4f();
|
|
inertiaTensor.m00(mass * scale.y * scale.y / 12.0f + mass * scale.x * scale.x / 4.0f);
|
|
inertiaTensor.m11(mass * scale.y * scale.y / 12.0f + mass * scale.x * scale.x / 4.0f);
|
|
inertiaTensor.m22(mass * scale.x * scale.x / 2.0f);
|
|
rVal.putData(EntityDataStrings.PHYSICS_INVERSE_INERTIA_TENSOR, inertiaTensor.invert());
|
|
|
|
Globals.clientSceneWrapper.getCollisionEngine().registerCollisionObject(rigidBody, collidable);
|
|
Globals.clientScene.registerEntityToTag(rVal, EntityTags.COLLIDABLE);
|
|
} break;
|
|
case "CUBE": {
|
|
rigidBody = CollisionBodyCreation.createCubeBody(
|
|
Globals.clientSceneWrapper.getCollisionEngine(),
|
|
new Vector3d(physicsTemplate.getDimension1(),physicsTemplate.getDimension2(),physicsTemplate.getDimension3())
|
|
);
|
|
collidable = new Collidable(rVal, Collidable.TYPE_CREATURE);
|
|
ClientCollidableTree tree = new ClientCollidableTree(rVal,collidable,rigidBody);
|
|
rVal.putData(EntityDataStrings.PHYSICS_COLLISION_BODY, rigidBody);
|
|
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_OFFSET, new Vector3f(physicsTemplate.getOffsetX(),physicsTemplate.getOffsetY(),physicsTemplate.getOffsetZ()));
|
|
rVal.putData(EntityDataStrings.PHYSICS_COLLISION_BODY_TRANSFORM, offsetTransform);
|
|
rVal.putData(EntityDataStrings.PHYSICS_MODEL_TEMPLATE, physicsTemplate);
|
|
rVal.putData(EntityDataStrings.PHYSICS_COLLIDABLE, collidable);
|
|
rVal.putData(EntityDataStrings.CLIENT_COLLIDABLE_TREE, tree);
|
|
|
|
scale = new Vector3f(physicsTemplate.getDimension1(),physicsTemplate.getDimension2(),physicsTemplate.getDimension3());
|
|
rVal.putData(EntityDataStrings.PHYSICS_MASS, mass);
|
|
//inertia tensor
|
|
//https://hepweb.ucsd.edu/ph110b/110b_notes/node26.html
|
|
inertiaTensor = new Matrix4f().identity().scale(mass * scale.x * scale.x / 6.0f).m33(1);
|
|
rVal.putData(EntityDataStrings.PHYSICS_INVERSE_INERTIA_TENSOR, inertiaTensor.invert());
|
|
|
|
Globals.clientSceneWrapper.getCollisionEngine().registerCollisionObject(rigidBody, collidable);
|
|
Globals.clientScene.registerEntityToTag(rVal, EntityTags.COLLIDABLE);
|
|
} break;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* [SERVER ONLY] Attaches a collidable template to a given entity
|
|
* @param realm The realm the entity is inside of
|
|
* @param rVal The entity
|
|
* @param physicsTemplate The collidable template
|
|
*/
|
|
public static void serverAttachCollidableTemplate(Realm realm, Entity rVal, CollidableTemplate physicsTemplate){
|
|
DBody rigidBody;
|
|
Collidable collidable;
|
|
float mass = 1.0f;
|
|
Matrix4f inertiaTensor;
|
|
Vector3f scale;
|
|
switch(physicsTemplate.getType()){
|
|
case "CYLINDER": {
|
|
rigidBody = CollisionBodyCreation.createCylinderBody(
|
|
realm.getCollisionEngine(),
|
|
physicsTemplate.getDimension1(),
|
|
physicsTemplate.getDimension2()
|
|
);
|
|
collidable = new Collidable(rVal, Collidable.TYPE_CREATURE);
|
|
ServerCollidableTree tree = new ServerCollidableTree(rVal,collidable,rigidBody);
|
|
rVal.putData(EntityDataStrings.PHYSICS_COLLISION_BODY, rigidBody);
|
|
rVal.putData(EntityDataStrings.PHYSICS_COLLISION_BODY_OFFSET, new Vector3f(physicsTemplate.getOffsetX(),physicsTemplate.getOffsetY(),physicsTemplate.getOffsetZ()));
|
|
rVal.putData(EntityDataStrings.PHYSICS_MODEL_TEMPLATE, physicsTemplate);
|
|
rVal.putData(EntityDataStrings.PHYSICS_COLLIDABLE, collidable);
|
|
rVal.putData(EntityDataStrings.SERVER_COLLIDABLE_TREE, tree);
|
|
|
|
scale = new Vector3f(physicsTemplate.getDimension1(),physicsTemplate.getDimension2(),physicsTemplate.getDimension3());
|
|
rVal.putData(EntityDataStrings.PHYSICS_MASS, mass);
|
|
//inertia tensor
|
|
//https://scienceworld.wolfram.com/physics/MomentofInertiaCylinder.html
|
|
inertiaTensor = new Matrix4f();
|
|
inertiaTensor.m00(mass * scale.y * scale.y / 12.0f + mass * scale.x * scale.x / 4.0f);
|
|
inertiaTensor.m11(mass * scale.y * scale.y / 12.0f + mass * scale.x * scale.x / 4.0f);
|
|
inertiaTensor.m22(mass * scale.x * scale.x / 2.0f);
|
|
rVal.putData(EntityDataStrings.PHYSICS_INVERSE_INERTIA_TENSOR, inertiaTensor.invert());
|
|
|
|
realm.getCollisionEngine().registerCollisionObject(rigidBody, collidable);
|
|
ServerEntityTagUtils.attachTagToEntity(rVal, EntityTags.COLLIDABLE);
|
|
} break;
|
|
case "CUBE": {
|
|
rigidBody = CollisionBodyCreation.createCubeBody(realm.getCollisionEngine(),new Vector3d(physicsTemplate.getDimension1(),physicsTemplate.getDimension2(),physicsTemplate.getDimension3()));
|
|
collidable = new Collidable(rVal, Collidable.TYPE_CREATURE);
|
|
ServerCollidableTree tree = new ServerCollidableTree(rVal,collidable,rigidBody);
|
|
rVal.putData(EntityDataStrings.PHYSICS_COLLISION_BODY, rigidBody);
|
|
rVal.putData(EntityDataStrings.PHYSICS_COLLISION_BODY_OFFSET, new Vector3f(physicsTemplate.getOffsetX(),physicsTemplate.getOffsetY(),physicsTemplate.getOffsetZ()));
|
|
rVal.putData(EntityDataStrings.PHYSICS_MODEL_TEMPLATE, physicsTemplate);
|
|
rVal.putData(EntityDataStrings.PHYSICS_COLLIDABLE, collidable);
|
|
rVal.putData(EntityDataStrings.SERVER_COLLIDABLE_TREE, tree);
|
|
|
|
scale = new Vector3f(physicsTemplate.getDimension1(),physicsTemplate.getDimension2(),physicsTemplate.getDimension3());
|
|
rVal.putData(EntityDataStrings.PHYSICS_MASS, mass);
|
|
//inertia tensor
|
|
//https://hepweb.ucsd.edu/ph110b/110b_notes/node26.html
|
|
inertiaTensor = new Matrix4f().identity().scale(mass * scale.x * scale.x / 6.0f).m33(1);
|
|
rVal.putData(EntityDataStrings.PHYSICS_INVERSE_INERTIA_TENSOR, inertiaTensor.invert());
|
|
|
|
realm.getCollisionEngine().registerCollisionObject(rigidBody, collidable);
|
|
ServerEntityTagUtils.attachTagToEntity(rVal, EntityTags.COLLIDABLE);
|
|
} break;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
* [CLIENT ONLY] Attaches a heightmap dbody to an entity
|
|
* @param terrain The terrain entity
|
|
* @param heightfield The heightfield values
|
|
* @return The DBody created
|
|
*/
|
|
public static DBody clientAttachTerrainRigidBody(Entity terrain, CollisionEngine collisionEngine, float[][] heightfield){
|
|
Vector3d position = EntityUtils.getPosition(terrain);
|
|
int arrayLength = heightfield.length;
|
|
int arrayWidth = heightfield[0].length;
|
|
float collisionMargin = 0.08f;
|
|
|
|
/*
|
|
Traditional buffer code not working for some reason
|
|
the approach of
|
|
https://stackoverflow.com/questions/40855945/lwjgl-mesh-to-jbullet-collider
|
|
works much better
|
|
IDK why
|
|
*/
|
|
|
|
int numberTriangles = (arrayLength - 1) * (arrayWidth - 1) * 2;
|
|
int triangleStride = 0;
|
|
|
|
int numberVertices = arrayLength * arrayWidth;
|
|
int vertexStride = 0;
|
|
|
|
float[] vertices = new float[numberVertices * 3];
|
|
int vertexInserterPos = 0;
|
|
int[] indices = new int[numberTriangles * 3];
|
|
int indexInserterPos = 0;
|
|
|
|
for(int x = 0; x < arrayLength; x++){
|
|
for(int y = 0; y < arrayWidth; y++){
|
|
vertices[vertexInserterPos] = x;
|
|
vertexInserterPos++;
|
|
vertices[vertexInserterPos] = heightfield[x][y] - collisionMargin;
|
|
vertexInserterPos++;
|
|
vertices[vertexInserterPos] = y;
|
|
vertexInserterPos++;
|
|
if(x < arrayLength - 1 && y < arrayWidth - 1){
|
|
//if we should also add a triangle index
|
|
/*
|
|
as copied from ModelUtil's terrain mesh generation function
|
|
faces.put((x / stride + 0) * actualHeight + (y / stride + 0));
|
|
faces.put((x / stride + 0) * actualHeight + (y / stride + 1));
|
|
faces.put((x / stride + 1) * actualHeight + (y / stride + 0));
|
|
faces.put((x / stride + 1) * actualHeight + (y / stride + 0));
|
|
faces.put((x / stride + 0) * actualHeight + (y / stride + 1));
|
|
faces.put((x / stride + 1) * actualHeight + (y / stride + 1));
|
|
*/
|
|
indices[indexInserterPos] = (x + 0) * arrayWidth + (y + 0);
|
|
indexInserterPos++;
|
|
indices[indexInserterPos] = (x + 0) * arrayWidth + (y + 1);
|
|
indexInserterPos++;
|
|
indices[indexInserterPos] = (x + 1) * arrayWidth + (y + 0);
|
|
indexInserterPos++;
|
|
indices[indexInserterPos] = (x + 1) * arrayWidth + (y + 0);
|
|
indexInserterPos++;
|
|
indices[indexInserterPos] = (x + 0) * arrayWidth + (y + 1);
|
|
indexInserterPos++;
|
|
indices[indexInserterPos] = (x + 1) * arrayWidth + (y + 1);
|
|
indexInserterPos++;
|
|
}
|
|
}
|
|
}
|
|
|
|
DTriMesh triMesh = collisionEngine.createTrimeshGeom(vertices,indices);
|
|
DBody body = collisionEngine.createDBody(triMesh);
|
|
|
|
Globals.clientSceneWrapper.getCollisionEngine().registerCollisionObject(body, new Collidable(terrain,Collidable.TYPE_TERRAIN));
|
|
terrain.putData(EntityDataStrings.PHYSICS_COLLISION_BODY, body);
|
|
|
|
return body;
|
|
}
|
|
|
|
|
|
/**
|
|
* [CLIENT ONLY] Given an entity and a terrain chunk description, create physics for the chunk and attach it to the entity
|
|
* @param terrain The entity
|
|
* @param data The terrain description
|
|
* @return The rigid body created (note, attachment has already been performed)
|
|
*/
|
|
public static DBody clientAttachTerrainChunkRigidBody(Entity terrain, TerrainChunkData data){
|
|
Vector3d position = EntityUtils.getPosition(terrain);
|
|
DBody terrainBody = CollisionBodyCreation.generateBodyFromTerrainData(Globals.clientSceneWrapper.getCollisionEngine(), data);
|
|
|
|
|
|
Globals.clientSceneWrapper.getCollisionEngine().registerCollisionObject(terrainBody, new Collidable(terrain,Collidable.TYPE_TERRAIN));
|
|
terrain.putData(EntityDataStrings.PHYSICS_COLLISION_BODY, terrainBody);
|
|
|
|
return terrainBody;
|
|
}
|
|
|
|
|
|
/**
|
|
* [SERVER ONLY] Given an entity and a terrain chunk description, create physics for the chunk and attach it to the entity
|
|
* @param terrain The entity
|
|
* @param data The terrain description
|
|
* @return The rigid body created (note, attachment has already been performed)
|
|
*/
|
|
public static DBody serverAttachTerrainChunkRigidBody(Entity terrain, TerrainChunkData data){
|
|
Vector3d position = EntityUtils.getPosition(terrain);
|
|
Realm terrainRealm = Globals.realmManager.getEntityRealm(terrain);
|
|
DBody terrainBody = CollisionBodyCreation.generateBodyFromTerrainData(terrainRealm.getCollisionEngine(),data);
|
|
|
|
terrainRealm.getCollisionEngine().registerCollisionObject(terrainBody, new Collidable(terrain,Collidable.TYPE_TERRAIN));
|
|
terrain.putData(EntityDataStrings.PHYSICS_COLLISION_BODY, terrainBody);
|
|
|
|
return terrainBody;
|
|
}
|
|
|
|
/**
|
|
* Gets the transform from parent entity position to rigid body
|
|
* @param entity The entity
|
|
* @return The transform
|
|
*/
|
|
public static Matrix4d getEntityCollidableTransform(Entity entity){
|
|
return (Matrix4d) entity.getData(EntityDataStrings.PHYSICS_COLLISION_BODY_TRANSFORM);
|
|
}
|
|
|
|
}
|