Renderer/src/main/java/electrosphere/collision/PhysicsEntityUtils.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);
}
}