From fcd2f1e0a743f63dc217e118740c23096a8eda7c Mon Sep 17 00:00:00 2001 From: austin Date: Fri, 28 Mar 2025 11:32:09 -0400 Subject: [PATCH] fix block mesh ray cast bug --- docs/src/progress/renderertodo.md | 1 + .../client/block/ClientBlockManager.java | 5 + .../collision/CollisionBodyCreation.java | 30 ++++ .../collision/PhysicsEntityUtils.java | 33 +++++ .../collidable/MultiShapeTriGeomData.java | 16 +++ .../types/terrain/BlockChunkEntity.java | 4 +- .../net/client/protocol/TerrainProtocol.java | 1 + .../renderer/meshgen/BlockMeshgen.java | 129 +++++++++++++++++- 8 files changed, 211 insertions(+), 8 deletions(-) create mode 100644 src/main/java/electrosphere/entity/state/collidable/MultiShapeTriGeomData.java diff --git a/docs/src/progress/renderertodo.md b/docs/src/progress/renderertodo.md index 1538b85d..0451837c 100644 --- a/docs/src/progress/renderertodo.md +++ b/docs/src/progress/renderertodo.md @@ -1353,6 +1353,7 @@ Fix TextureInstancedActor packing data texture incorrectly (column major instead (03/28/2025) Grass height variance with control from ui + file +Fix block mesh ray casting bug due to trimesh overlap # TODO diff --git a/src/main/java/electrosphere/client/block/ClientBlockManager.java b/src/main/java/electrosphere/client/block/ClientBlockManager.java index 0791ca83..96a246b0 100644 --- a/src/main/java/electrosphere/client/block/ClientBlockManager.java +++ b/src/main/java/electrosphere/client/block/ClientBlockManager.java @@ -23,7 +23,12 @@ import electrosphere.renderer.meshgen.BlockMeshgen.BlockMeshData; import electrosphere.server.terrain.manager.ServerTerrainChunk; import electrosphere.server.terrain.manager.ServerTerrainManager; + +/** + * Handles networking for block-related messages on client + */ public class ClientBlockManager { + //queues messages from server List messageQueue = new LinkedList(); diff --git a/src/main/java/electrosphere/collision/CollisionBodyCreation.java b/src/main/java/electrosphere/collision/CollisionBodyCreation.java index df6e7da2..867b2bed 100644 --- a/src/main/java/electrosphere/collision/CollisionBodyCreation.java +++ b/src/main/java/electrosphere/collision/CollisionBodyCreation.java @@ -19,6 +19,7 @@ import org.ode4j.ode.DMass; import org.ode4j.ode.DSphere; import org.ode4j.ode.DTriMesh; +import electrosphere.entity.state.collidable.MultiShapeTriGeomData; import electrosphere.entity.state.collidable.TriGeomData; /** @@ -270,6 +271,35 @@ public class CollisionBodyCreation { return body; } + /** + * Creates an ode DBody from a mesh data set containing multiple shapes + * @param data The mesh data data + * @return The DBody + */ + public static DBody generateBodyFromMultiShapeMeshData(CollisionEngine collisionEngine, MultiShapeTriGeomData data, long categoryBits){ + DBody body = null; + + DGeom[] geoms = new DGeom[data.getData().size()]; + + //create trimeshes + int i = 0; + for(TriGeomData shapeData : data.getData()){ + if(shapeData.getFaceElements().length > 0){ + DTriMesh triMesh = collisionEngine.createTrimeshGeom(shapeData.getVertices(),shapeData.getFaceElements(),categoryBits); + geoms[i] = triMesh; + } + i++; + } + + //create body from shapes + body = collisionEngine.createDBody(geoms); + collisionEngine.setKinematic(body); + collisionEngine.setGravityMode(body, false); + + + return body; + } + /** * Generates a body from an AIScene * @param scene The AIScene to generate a rigid body off of diff --git a/src/main/java/electrosphere/collision/PhysicsEntityUtils.java b/src/main/java/electrosphere/collision/PhysicsEntityUtils.java index fac926f9..4f36198b 100644 --- a/src/main/java/electrosphere/collision/PhysicsEntityUtils.java +++ b/src/main/java/electrosphere/collision/PhysicsEntityUtils.java @@ -17,6 +17,7 @@ import electrosphere.entity.EntityTags; import electrosphere.entity.EntityUtils; import electrosphere.entity.ServerEntityUtils; import electrosphere.entity.state.collidable.ClientCollidableTree; +import electrosphere.entity.state.collidable.MultiShapeTriGeomData; import electrosphere.entity.state.collidable.ServerCollidableTree; import electrosphere.entity.state.collidable.TriGeomData; import electrosphere.entity.state.physicssync.ClientPhysicsSyncTree; @@ -504,6 +505,21 @@ public class PhysicsEntityUtils { terrain.putData(EntityDataStrings.PHYSICS_COLLIDABLE, collidable); } + /** + * [CLIENT ONLY] Given an entity and a multi-shape trimesh description, create physics for the shapes and attach it to the entity + * @param terrain The entity + * @param data The trimesh description + * @return The rigid body created (note, attachment has already been performed) + */ + public static void clientAttachMultiShapeTriGeomRigidBody(Entity terrain, MultiShapeTriGeomData data){ + DBody terrainBody = CollisionBodyCreation.generateBodyFromMultiShapeMeshData(Globals.clientSceneWrapper.getCollisionEngine(), data, Collidable.TYPE_STATIC_BIT); + CollisionBodyCreation.setAutoDisable(Globals.clientSceneWrapper.getCollisionEngine(), terrainBody, true, LINEAR_THRESHOLD, ANGULAR_THRESHOLD, STEP_THRESHOLD); + Collidable collidable = new Collidable(terrain,Collidable.TYPE_STATIC, false); + Globals.clientSceneWrapper.getCollisionEngine().registerCollisionObject(terrainBody, collidable); + PhysicsEntityUtils.setDBody(terrain,terrainBody); + terrain.putData(EntityDataStrings.PHYSICS_COLLIDABLE, collidable); + } + /** * [SERVER ONLY] Given an entity and a terrain chunk description, create physics for the chunk and attach it to the entity @@ -522,6 +538,23 @@ public class PhysicsEntityUtils { return terrainBody; } + /** + * [SERVER ONLY] Given an entity and a multi-shape trimesh description, create physics for the shapes and attach it to the entity + * @param terrain The entity + * @param data The trimesh description + * @return The rigid body created (note, attachment has already been performed) + */ + public static DBody serverAttachMultiShapeTriGeomRigidBody(Entity terrain, MultiShapeTriGeomData data){ + Realm realm = Globals.realmManager.getEntityRealm(terrain); + DBody terrainBody = CollisionBodyCreation.generateBodyFromMultiShapeMeshData(realm.getCollisionEngine(),data,Collidable.TYPE_STATIC_BIT); + CollisionBodyCreation.setAutoDisable(realm.getCollisionEngine(), terrainBody, true, LINEAR_THRESHOLD, ANGULAR_THRESHOLD, STEP_THRESHOLD); + + realm.getCollisionEngine().registerCollisionObject(terrainBody, new Collidable(terrain,Collidable.TYPE_STATIC, false)); + PhysicsEntityUtils.setDBody(terrain,terrainBody); + + return terrainBody; + } + /** * Repositions all active physics-scoped entities on a given realm * @param collisionEngine The realm's collision engine diff --git a/src/main/java/electrosphere/entity/state/collidable/MultiShapeTriGeomData.java b/src/main/java/electrosphere/entity/state/collidable/MultiShapeTriGeomData.java new file mode 100644 index 00000000..4b335cf4 --- /dev/null +++ b/src/main/java/electrosphere/entity/state/collidable/MultiShapeTriGeomData.java @@ -0,0 +1,16 @@ +package electrosphere.entity.state.collidable; + +import java.util.Collection; + +/** + * A tri geom data set that contains multiple shapes + */ +public interface MultiShapeTriGeomData { + + /** + * Gets the data for the tri geom shapes + * @return The data + */ + public Collection getData(); + +} diff --git a/src/main/java/electrosphere/entity/types/terrain/BlockChunkEntity.java b/src/main/java/electrosphere/entity/types/terrain/BlockChunkEntity.java index 74088751..d50eb995 100644 --- a/src/main/java/electrosphere/entity/types/terrain/BlockChunkEntity.java +++ b/src/main/java/electrosphere/entity/types/terrain/BlockChunkEntity.java @@ -67,7 +67,7 @@ public class BlockChunkEntity { })); EntityCreationUtils.makeEntityDrawablePreexistingModel(rVal, modelPath); if(levelOfDetail == BlockChunkData.LOD_FULL_RES){ - PhysicsEntityUtils.clientAttachTriGeomRigidBody(rVal, data); + PhysicsEntityUtils.clientAttachMultiShapeTriGeomRigidBody(rVal, data); CollisionObjUtils.clientPositionCharacter(rVal, new Vector3d(EntityUtils.getPosition(rVal)), new Quaterniond()); } else { EntityCreationUtils.bypassShadowPass(rVal); @@ -112,7 +112,7 @@ public class BlockChunkEntity { */ public static void serverCreateBlockChunkEntity(Entity entity, BlockMeshData blockChunkData){ if(blockChunkData.getVertices().length > 0){ - PhysicsEntityUtils.serverAttachTriGeomRigidBody(entity, blockChunkData); + PhysicsEntityUtils.serverAttachMultiShapeTriGeomRigidBody(entity, blockChunkData); Realm realm = Globals.realmManager.getEntityRealm(entity); DBody terrainBody = PhysicsEntityUtils.getDBody(entity); Vector3d entityPos = EntityUtils.getPosition(entity); diff --git a/src/main/java/electrosphere/net/client/protocol/TerrainProtocol.java b/src/main/java/electrosphere/net/client/protocol/TerrainProtocol.java index 4671c389..c0298cc7 100644 --- a/src/main/java/electrosphere/net/client/protocol/TerrainProtocol.java +++ b/src/main/java/electrosphere/net/client/protocol/TerrainProtocol.java @@ -208,6 +208,7 @@ public class TerrainProtocol implements ClientProtocolTemplate { if(Globals.clientBlockManager.containsChunkDataAtWorldPoint(worldPos.x, worldPos.y, worldPos.z, ChunkData.NO_STRIDE)){ BlockChunkData data = Globals.clientBlockManager.getChunkDataAtWorldPoint(worldPos.x, worldPos.y, worldPos.z, ChunkData.NO_STRIDE); if(data != null){ + System.out.println("Set " + message.getvoxelX() + " " + message.getvoxelY() + " " + message.getvoxelZ() + " to " + message.getblockType()); data.setType( message.getvoxelX(), message.getvoxelY(), diff --git a/src/main/java/electrosphere/renderer/meshgen/BlockMeshgen.java b/src/main/java/electrosphere/renderer/meshgen/BlockMeshgen.java index b8a1b0c2..070c95b8 100644 --- a/src/main/java/electrosphere/renderer/meshgen/BlockMeshgen.java +++ b/src/main/java/electrosphere/renderer/meshgen/BlockMeshgen.java @@ -2,6 +2,7 @@ package electrosphere.renderer.meshgen; import java.nio.FloatBuffer; import java.nio.IntBuffer; +import java.util.Collection; import java.util.Collections; import java.util.LinkedList; import java.util.List; @@ -13,6 +14,7 @@ import org.lwjgl.opengl.GL40; import electrosphere.client.block.BlockChunkData; import electrosphere.engine.Globals; +import electrosphere.entity.state.collidable.MultiShapeTriGeomData; import electrosphere.entity.state.collidable.TriGeomData; import electrosphere.renderer.model.Material; import electrosphere.renderer.model.Mesh; @@ -311,6 +313,9 @@ public class BlockMeshgen { //sort Collections.sort(quadMeshes); + int lastVertexCount = 0; + int lastFaceCount = 0; + //generate volumes QuadMesh quad1 = null; QuadMesh quad2 = null; @@ -325,6 +330,11 @@ public class BlockMeshgen { } else { BlockMeshgen.meshifyBox(verts,normals,uvs,indices,samplers,quad1,zEnd,quad1.type,verts.size()); quad1 = quad2; + + BlockSingleShape blockSingleShape = BlockMeshgen.copyDataToShape(verts,indices,lastVertexCount,lastFaceCount); + lastVertexCount = verts.size(); + lastFaceCount = indices.size(); + rVal.shapeData.add(blockSingleShape); break; } } @@ -332,6 +342,10 @@ public class BlockMeshgen { } if(quad1 != null){ BlockMeshgen.meshifyBox(verts,normals,uvs,indices,samplers,quad1,zEnd,quad1.type,verts.size()); + BlockSingleShape blockSingleShape = BlockMeshgen.copyDataToShape(verts,indices,lastVertexCount,lastFaceCount); + lastVertexCount = verts.size(); + lastFaceCount = indices.size(); + rVal.shapeData.add(blockSingleShape); } // @@ -389,6 +403,28 @@ public class BlockMeshgen { return rVal; } + /** + * Copies vertex and index data from the combined array into a single shape + * @param verts The list of vertices + * @param indices The list of indices + * @param lastVertCount The last vertex position that was copied + * @param lastFaceCount The last index position that was copied + * @return The data containing a single shape's worth of geometry data + */ + private static BlockSingleShape copyDataToShape(List verts, List indices, int lastVertCount, int lastFaceCount){ + BlockSingleShape blockSingleShape = new BlockSingleShape((verts.size() - lastVertCount),(indices.size() - lastFaceCount)); + for(int i = lastVertCount; i < verts.size(); i++){ + Vector3f vert = verts.get(i); + blockSingleShape.vertices[(i - lastVertCount) * 3 + 0] = vert.x; + blockSingleShape.vertices[(i - lastVertCount) * 3 + 1] = vert.y; + blockSingleShape.vertices[(i - lastVertCount) * 3 + 2] = vert.z; + } + for(int i = lastFaceCount; i < indices.size(); i++){ + blockSingleShape.faceElements[i - lastFaceCount] = indices.get(i) - lastVertCount; + } + return blockSingleShape; + } + /** * Generates a mesh based on a block mesh data object * @param data The block mesh data object @@ -492,36 +528,117 @@ public class BlockMeshgen { return rVal; } + /** + * Contains the geom data for a single shape in the block mesh + */ + public static class BlockSingleShape implements TriGeomData { + + /** + * The verts + */ + float[] vertices; + + /** + * The faces + */ + int[] faceElements; + + /** + * Constructor + * @param vertCount The number of verts + * @param faceCount The number of faces + */ + public BlockSingleShape(int vertCount, int faceCount){ + vertices = new float[vertCount * 3]; + faceElements = new int[faceCount]; + } + + @Override + public float[] getVertices() { + return vertices; + } + + @Override + public int[] getFaceElements() { + return faceElements; + } + + } /** * The final rasterization data that is emitted */ - public static class BlockMeshData implements TriGeomData { - //the verts + public static class BlockMeshData implements TriGeomData, MultiShapeTriGeomData { + + /** + * Vertex data in array form + */ float[] vertices; - //normals + + /** + * Normal data in array form + */ float[] normals; - //faces + + /** + * Face data in array form + */ int[] faceElements; - //UVs + + /** + * UV data in array form + */ float[] uvs; - //The samplers for each quad + + /** + * Sampler data in array form + */ int[] samplers; + /** + * Data broken out by each shape + */ + List shapeData = new LinkedList(); + + /** + * Buffer of vertex data + */ FloatBuffer vertBuffer; + + /** + * Buffer of normal data + */ FloatBuffer normalBuffer; + + /** + * Buffer of face data + */ IntBuffer faceBuffer; + + /** + * Buffer of UV data + */ FloatBuffer uvBuffer; + + /** + * Buffer of sampler data + */ IntBuffer samplerBuffer; @Override public float[] getVertices() { return vertices; } + @Override public int[] getFaceElements() { return faceElements; } + + @Override + public Collection getData() { + return shapeData; + } } /**