diff --git a/docs/src/progress/renderertodo.md b/docs/src/progress/renderertodo.md index 0934f28f..d7ed047b 100644 --- a/docs/src/progress/renderertodo.md +++ b/docs/src/progress/renderertodo.md @@ -1150,6 +1150,10 @@ Add server manager for block chunk data & management Add endpoint to request strided block data Add client side handling of block endpoint Add server-driven block rasterizer with LOD +Convert PhysicsEntityUtils to use generic interface to load tri geom rigid bodies + +(11/24/2024) +Fix winding order on block meshes # TODO diff --git a/src/main/java/electrosphere/client/block/BlockChunkCache.java b/src/main/java/electrosphere/client/block/BlockChunkCache.java index 10c1318b..8c60323f 100644 --- a/src/main/java/electrosphere/client/block/BlockChunkCache.java +++ b/src/main/java/electrosphere/client/block/BlockChunkCache.java @@ -7,7 +7,7 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.Semaphore; +import java.util.concurrent.locks.ReentrantLock; import io.github.studiorailgun.HashUtils; @@ -64,16 +64,16 @@ public class BlockChunkCache { /** * The lock for thread safety */ - Semaphore lock = new Semaphore(1); + ReentrantLock lock = new ReentrantLock(); /** * Gets the collection of server block chunks that are cached * @return The collection of chunks */ public Collection getContents(){ - lock.acquireUninterruptibly(); + lock.lock(); Collection rVal = Collections.unmodifiableCollection(cacheMapFullRes.values()); - lock.release(); + lock.unlock(); return rVal; } @@ -81,13 +81,13 @@ public class BlockChunkCache { * Evicts all chunks in the cache */ public void clear(){ - lock.acquireUninterruptibly(); + lock.lock(); cacheMapFullRes.clear(); cacheMapHalfRes.clear(); cacheMapQuarterRes.clear(); cacheMapEighthRes.clear(); cacheMapSixteenthRes.clear(); - lock.release(); + lock.unlock(); } /** @@ -101,12 +101,12 @@ public class BlockChunkCache { public BlockChunkData get(int worldX, int worldY, int worldZ, int stride){ BlockChunkData rVal = null; Long key = this.getKey(worldX, worldY, worldZ); - lock.acquireUninterruptibly(); + lock.lock(); queryRecencyQueue.remove(key); queryRecencyQueue.add(0, key); Map cache = this.getCache(stride); rVal = cache.get(key); - lock.release(); + lock.unlock(); return rVal; } @@ -120,7 +120,7 @@ public class BlockChunkCache { */ public void add(int worldX, int worldY, int worldZ, int stride, BlockChunkData chunk){ Long key = this.getKey(worldX, worldY, worldZ); - lock.acquireUninterruptibly(); + lock.lock(); queryRecencyQueue.add(0, key); Map cache = this.getCache(stride); cache.put(key, chunk); @@ -132,7 +132,7 @@ public class BlockChunkCache { cacheMapEighthRes.remove(oldKey); cacheMapSixteenthRes.remove(oldKey); } - lock.release(); + lock.unlock(); } /** @@ -145,10 +145,10 @@ public class BlockChunkCache { */ public boolean containsChunk(int worldX, int worldY, int worldZ, int stride){ Long key = this.getKey(worldX,worldY,worldZ); - lock.acquireUninterruptibly(); + lock.lock(); Map cache = this.getCache(stride); boolean rVal = cache.containsKey(key); - lock.release(); + lock.unlock(); return rVal; } @@ -172,9 +172,9 @@ public class BlockChunkCache { */ public boolean chunkIsQueued(int worldX, int worldY, int worldZ){ Long key = this.getKey(worldX,worldY,worldZ); - lock.acquireUninterruptibly(); + lock.lock(); boolean rVal = this.queuedChunkMap.containsKey(key); - lock.release(); + lock.unlock(); return rVal; } @@ -186,9 +186,9 @@ public class BlockChunkCache { */ public void queueChunk(int worldX, int worldY, int worldZ){ Long key = this.getKey(worldX,worldY,worldZ); - lock.acquireUninterruptibly(); + lock.lock(); this.queuedChunkMap.put(key,true); - lock.release(); + lock.unlock(); } /** @@ -200,9 +200,9 @@ public class BlockChunkCache { */ public void unqueueChunk(int worldX, int worldY, int worldZ, int stride){ Long key = this.getKey(worldX,worldY,worldZ); - lock.acquireUninterruptibly(); + lock.lock(); this.queuedChunkMap.remove(key); - lock.release(); + lock.unlock(); } /** diff --git a/src/main/java/electrosphere/collision/CollisionBodyCreation.java b/src/main/java/electrosphere/collision/CollisionBodyCreation.java index 9270a9c1..3db5413a 100644 --- a/src/main/java/electrosphere/collision/CollisionBodyCreation.java +++ b/src/main/java/electrosphere/collision/CollisionBodyCreation.java @@ -19,7 +19,7 @@ import org.ode4j.ode.DMass; import org.ode4j.ode.DSphere; import org.ode4j.ode.DTriMesh; -import electrosphere.entity.types.terrain.TerrainChunkData; +import electrosphere.entity.state.collidable.TriGeomData; /** * Utilities for creating types of rigid bodies @@ -256,7 +256,7 @@ public class CollisionBodyCreation { * @param data The terrain data * @return The DBody */ - public static DBody generateBodyFromTerrainData(CollisionEngine collisionEngine, TerrainChunkData data, long categoryBits){ + public static DBody generateBodyFromTerrainData(CollisionEngine collisionEngine, TriGeomData data, long categoryBits){ DBody body = null; //create trimesh diff --git a/src/main/java/electrosphere/collision/PhysicsEntityUtils.java b/src/main/java/electrosphere/collision/PhysicsEntityUtils.java index 99c7c2f6..572a3c4d 100644 --- a/src/main/java/electrosphere/collision/PhysicsEntityUtils.java +++ b/src/main/java/electrosphere/collision/PhysicsEntityUtils.java @@ -18,9 +18,9 @@ import electrosphere.entity.EntityUtils; import electrosphere.entity.ServerEntityUtils; import electrosphere.entity.state.collidable.ClientCollidableTree; import electrosphere.entity.state.collidable.ServerCollidableTree; +import electrosphere.entity.state.collidable.TriGeomData; import electrosphere.entity.state.physicssync.ClientPhysicsSyncTree; import electrosphere.entity.state.physicssync.ServerPhysicsSyncTree; -import electrosphere.entity.types.terrain.TerrainChunkData; import electrosphere.game.data.collidable.CollidableTemplate; import electrosphere.game.server.world.ServerWorldData; import electrosphere.server.datacell.Realm; @@ -487,7 +487,7 @@ public class PhysicsEntityUtils { * @param data The terrain description * @return The rigid body created (note, attachment has already been performed) */ - public static void clientAttachTerrainChunkRigidBody(Entity terrain, TerrainChunkData data){ + public static void clientAttachTriGeomRigidBody(Entity terrain, TriGeomData data){ DBody terrainBody = CollisionBodyCreation.generateBodyFromTerrainData(Globals.clientSceneWrapper.getCollisionEngine(), data, Collidable.TYPE_STATIC_BIT); Collidable collidable = new Collidable(terrain,Collidable.TYPE_TERRAIN, false); Globals.clientSceneWrapper.getCollisionEngine().registerCollisionObject(terrainBody, collidable); @@ -502,7 +502,7 @@ public class PhysicsEntityUtils { * @param data The terrain description * @return The rigid body created (note, attachment has already been performed) */ - public static DBody serverAttachTerrainChunkRigidBody(Entity terrain, TerrainChunkData data){ + public static DBody serverAttachTriGeomRigidBody(Entity terrain, TriGeomData data){ Realm terrainRealm = Globals.realmManager.getEntityRealm(terrain); DBody terrainBody = CollisionBodyCreation.generateBodyFromTerrainData(terrainRealm.getCollisionEngine(),data,Collidable.TYPE_STATIC_BIT); diff --git a/src/main/java/electrosphere/entity/state/collidable/TriGeomData.java b/src/main/java/electrosphere/entity/state/collidable/TriGeomData.java new file mode 100644 index 00000000..498b5a75 --- /dev/null +++ b/src/main/java/electrosphere/entity/state/collidable/TriGeomData.java @@ -0,0 +1,20 @@ +package electrosphere.entity.state.collidable; + +/** + * A data object that can be used to generate a collidable with arbitrary geometry + */ +public interface TriGeomData { + + /** + * Gets the vertex data + * @return the vertex data + */ + public float[] getVertices(); + + /** + * Gets the face element data + * @return the face element data + */ + public int[] getFaceElements(); + +} diff --git a/src/main/java/electrosphere/entity/types/terrain/BlockChunkEntity.java b/src/main/java/electrosphere/entity/types/terrain/BlockChunkEntity.java index 11ee589e..e372d12a 100644 --- a/src/main/java/electrosphere/entity/types/terrain/BlockChunkEntity.java +++ b/src/main/java/electrosphere/entity/types/terrain/BlockChunkEntity.java @@ -9,6 +9,7 @@ import org.joml.Vector3d; import electrosphere.client.block.BlockChunkData; import electrosphere.client.block.cells.BlockDrawCell; import electrosphere.client.block.cells.BlockTextureAtlas; +import electrosphere.collision.PhysicsEntityUtils; import electrosphere.engine.Globals; import electrosphere.engine.assetmanager.queue.QueuedModel; import electrosphere.entity.ClientEntityUtils; @@ -16,10 +17,12 @@ import electrosphere.entity.Entity; import electrosphere.entity.EntityCreationUtils; import electrosphere.entity.EntityDataStrings; import electrosphere.entity.EntityUtils; +import electrosphere.entity.ServerEntityUtils; import electrosphere.entity.types.collision.CollisionObjUtils; import electrosphere.logger.LoggerInterface; import electrosphere.renderer.meshgen.BlockMeshgen; import electrosphere.renderer.meshgen.BlockMeshgen.BlockMeshData; +import electrosphere.server.datacell.Realm; /** * Generates block chunk entities @@ -63,7 +66,7 @@ public class BlockChunkEntity { })); EntityCreationUtils.makeEntityDrawablePreexistingModel(rVal, modelPath); if(levelOfDetail == BlockChunkData.LOD_FULL_RES){ - // PhysicsEntityUtils.clientAttachTerrainChunkRigidBody(rVal, data); + PhysicsEntityUtils.clientAttachTriGeomRigidBody(rVal, data); CollisionObjUtils.clientPositionCharacter(rVal, new Vector3d(EntityUtils.getPosition(rVal)), new Quaterniond()); } else { EntityCreationUtils.bypassShadowPass(rVal); @@ -99,6 +102,34 @@ public class BlockChunkEntity { return rVal; } + /** + * Creates a block chunk entity on the server + * @param realm The realm + * @param position The position of the chunk + * @param weights The weights for the block chunk + * @param values The values of each voxel in the chunk + * @return The block entity + */ + public static Entity serverCreateBlockChunkEntity(Realm realm, Vector3d position, BlockMeshData blockChunkData){ + + Entity rVal = EntityCreationUtils.createServerEntity(realm, position); + if(blockChunkData.getVertices().length > 0){ + PhysicsEntityUtils.serverAttachTriGeomRigidBody(rVal, blockChunkData); + rVal.putData(EntityDataStrings.TERRAIN_IS_TERRAIN, true); + } + + + //position entity + //this needs to be called at the end of this function. + //Burried underneath this is function call to initialize a server side entity. + //The server initialization logic checks what type of entity this is, if this function is called prior to its type being stored + //the server will not be able to synchronize it properly. + ServerEntityUtils.initiallyPositionEntity(realm,rVal,position); + + + return rVal; + } + /** * Halts all running generation threads */ diff --git a/src/main/java/electrosphere/entity/types/terrain/TerrainChunk.java b/src/main/java/electrosphere/entity/types/terrain/TerrainChunk.java index fd63442e..ebac2a84 100644 --- a/src/main/java/electrosphere/entity/types/terrain/TerrainChunk.java +++ b/src/main/java/electrosphere/entity/types/terrain/TerrainChunk.java @@ -6,6 +6,7 @@ import java.util.concurrent.Executors; import org.joml.Quaterniond; import org.joml.Vector3d; +import electrosphere.client.block.BlockChunkData; import electrosphere.client.terrain.cells.ClientDrawCellManager; import electrosphere.client.terrain.cells.DrawCell; import electrosphere.client.terrain.cells.VoxelTextureAtlas; @@ -64,8 +65,8 @@ public class TerrainChunk { if(Globals.clientScene.containsEntity(rVal)){ String modelPath = ClientTerrainManager.queueTerrainGridGeneration(data, atlas, notifyTarget, toDelete); EntityCreationUtils.makeEntityDrawablePreexistingModel(rVal, modelPath); - if(levelOfDetail == ClientDrawCellManager.FULL_RES_LOD && data.faceElements.length > 0){ - PhysicsEntityUtils.clientAttachTerrainChunkRigidBody(rVal, data); + if(levelOfDetail == BlockChunkData.LOD_FULL_RES && data.faceElements.length > 0){ + PhysicsEntityUtils.clientAttachTriGeomRigidBody(rVal, data); CollisionObjUtils.clientPositionCharacter(rVal, new Vector3d(EntityUtils.getPosition(rVal)), new Quaterniond()); } else { EntityCreationUtils.bypassShadowPass(rVal); @@ -116,7 +117,7 @@ public class TerrainChunk { Entity rVal = EntityCreationUtils.createServerEntity(realm, position); if(data.vertices.length > 0){ - PhysicsEntityUtils.serverAttachTerrainChunkRigidBody(rVal, data); + PhysicsEntityUtils.serverAttachTriGeomRigidBody(rVal, data); rVal.putData(EntityDataStrings.TERRAIN_IS_TERRAIN, true); // ServerEntityUtils.initiallyPositionEntity(realm, rVal, position); // physicsObject = PhysicsUtils.attachTerrainRigidBody(physicsEntity,heightmap,true); diff --git a/src/main/java/electrosphere/entity/types/terrain/TerrainChunkData.java b/src/main/java/electrosphere/entity/types/terrain/TerrainChunkData.java index cbbff820..2ee8c393 100644 --- a/src/main/java/electrosphere/entity/types/terrain/TerrainChunkData.java +++ b/src/main/java/electrosphere/entity/types/terrain/TerrainChunkData.java @@ -5,10 +5,12 @@ import java.nio.IntBuffer; import org.lwjgl.BufferUtils; +import electrosphere.entity.state.collidable.TriGeomData; + /** * The data required to generate a texture */ -public class TerrainChunkData { +public class TerrainChunkData implements TriGeomData { //the verts float[] vertices; @@ -81,10 +83,7 @@ public class TerrainChunkData { ratioBuffer.put(this.textureRatioVectors); } - /** - * Gets the vertex data - * @return the vertex data - */ + @Override public float[] getVertices(){ return vertices; } @@ -97,10 +96,7 @@ public class TerrainChunkData { return normals; } - /** - * Gets the face element data - * @return the face element data - */ + @Override public int[] getFaceElements(){ return faceElements; } diff --git a/src/main/java/electrosphere/renderer/meshgen/BlockMeshgen.java b/src/main/java/electrosphere/renderer/meshgen/BlockMeshgen.java index d70d4b2c..fcd49d1e 100644 --- a/src/main/java/electrosphere/renderer/meshgen/BlockMeshgen.java +++ b/src/main/java/electrosphere/renderer/meshgen/BlockMeshgen.java @@ -13,6 +13,7 @@ import org.lwjgl.opengl.GL40; import electrosphere.client.block.BlockChunkData; import electrosphere.engine.Globals; +import electrosphere.entity.state.collidable.TriGeomData; import electrosphere.renderer.model.Mesh; import electrosphere.renderer.model.Model; @@ -120,8 +121,8 @@ public class BlockMeshgen { indices.add(2); indices.add(3); indices.add(0); - indices.add(1); indices.add(3); + indices.add(1); //normals normals.add(new Vector3f(0,0,-1)); normals.add(new Vector3f(0,0,-1)); @@ -144,8 +145,8 @@ public class BlockMeshgen { verts.add(new Vector3f(quad.x, quad.y + quad.h, quad.z + depth).mul(BlockChunkData.BLOCK_SIZE_MULTIPLIER)); //indices indices.add(4); - indices.add(6); indices.add(7); + indices.add(6); indices.add(4); indices.add(5); indices.add(7); @@ -173,8 +174,8 @@ public class BlockMeshgen { indices.add( 8); indices.add(10); indices.add(11); - indices.add( 8); indices.add( 9); + indices.add( 8); indices.add(11); //normals normals.add(new Vector3f(0,-1,0)); @@ -198,8 +199,8 @@ public class BlockMeshgen { verts.add(new Vector3f(quad.x + quad.w, quad.y + quad.h, quad.z + depth).mul(BlockChunkData.BLOCK_SIZE_MULTIPLIER)); //indices indices.add(12); - indices.add(14); indices.add(15); + indices.add(14); indices.add(12); indices.add(13); indices.add(15); @@ -229,8 +230,8 @@ public class BlockMeshgen { indices.add(18); indices.add(19); indices.add(16); - indices.add(17); indices.add(19); + indices.add(17); //normals normals.add(new Vector3f(1,0,0)); normals.add(new Vector3f(1,0,0)); @@ -254,8 +255,8 @@ public class BlockMeshgen { verts.add(new Vector3f(quad.x + quad.w, quad.y + quad.h, quad.z + depth).mul(BlockChunkData.BLOCK_SIZE_MULTIPLIER)); //indices indices.add(20); - indices.add(22); indices.add(23); + indices.add(22); indices.add(20); indices.add(21); indices.add(23); @@ -466,7 +467,7 @@ public class BlockMeshgen { /** * The final rasterization data that is emitted */ - public static class BlockMeshData { + public static class BlockMeshData implements TriGeomData { //the verts float[] vertices; //normals @@ -480,6 +481,15 @@ public class BlockMeshgen { FloatBuffer normalBuffer; IntBuffer faceBuffer; FloatBuffer uvBuffer; + + @Override + public float[] getVertices() { + return vertices; + } + @Override + public int[] getFaceElements() { + return faceElements; + } } /** diff --git a/src/main/java/electrosphere/server/block/manager/ServerBlockManager.java b/src/main/java/electrosphere/server/block/manager/ServerBlockManager.java index 71b7ca7e..084e1551 100644 --- a/src/main/java/electrosphere/server/block/manager/ServerBlockManager.java +++ b/src/main/java/electrosphere/server/block/manager/ServerBlockManager.java @@ -121,6 +121,16 @@ public class ServerBlockManager { returnedChunk.setWorldY(worldY); returnedChunk.setWorldZ(worldZ); returnedChunk.setHomogenousValue(0); + if(worldX == 0 && worldY == 0 && worldZ == 0){ + returnedChunk.setHomogenousValue(BlockChunkData.NOT_HOMOGENOUS); + for(int x = 3; x < 16; x++){ + for(int y = 8; y < 16; y++){ + for(int z = 3; z < 16; z++){ + returnedChunk.setType(x, y, z, 1); + } + } + } + } } this.chunkCache.add(worldX, worldY, worldZ, BlockChunkData.LOD_FULL_RES, returnedChunk); } diff --git a/src/main/java/electrosphere/server/datacell/physics/PhysicsDataCell.java b/src/main/java/electrosphere/server/datacell/physics/PhysicsDataCell.java index 0bcdb034..509d64b9 100644 --- a/src/main/java/electrosphere/server/datacell/physics/PhysicsDataCell.java +++ b/src/main/java/electrosphere/server/datacell/physics/PhysicsDataCell.java @@ -5,7 +5,10 @@ import electrosphere.engine.Globals; import electrosphere.entity.Entity; import electrosphere.entity.EntityDataStrings; import electrosphere.entity.ServerEntityUtils; +import electrosphere.entity.types.terrain.BlockChunkEntity; import electrosphere.entity.types.terrain.TerrainChunk; +import electrosphere.renderer.meshgen.BlockMeshgen; +import electrosphere.renderer.meshgen.BlockMeshgen.BlockMeshData; import electrosphere.server.datacell.Realm; import electrosphere.server.terrain.manager.ServerTerrainChunk; import electrosphere.server.terrain.manager.ServerTerrainManager; @@ -22,6 +25,7 @@ public class PhysicsDataCell { Vector3i worldPos; Entity physicsEntity; + Entity blockPhysicsEntity; DBody physicsObject; @@ -59,6 +63,8 @@ public class PhysicsDataCell { public void retireCell(){ ServerEntityUtils.destroyEntity(physicsEntity); this.physicsEntity = null; + ServerEntityUtils.destroyEntity(blockPhysicsEntity); + this.blockPhysicsEntity = null; } /** @@ -81,6 +87,16 @@ public class PhysicsDataCell { physicsEntity = TerrainChunk.serverCreateTerrainChunkEntity(realm, realPos, weights, types); physicsEntity.putData(EntityDataStrings.TERRAIN_IS_TERRAIN, true); } + if(blockPhysicsEntity == null){ + Vector3d realPos = new Vector3d( + worldPos.x * ServerTerrainChunk.CHUNK_PLACEMENT_OFFSET, + worldPos.y * ServerTerrainChunk.CHUNK_PLACEMENT_OFFSET, + worldPos.z * ServerTerrainChunk.CHUNK_PLACEMENT_OFFSET + ); + BlockMeshData meshData = BlockMeshgen.rasterize(realm.getServerWorldData().getServerBlockManager().getChunk(worldPos.x, worldPos.y, worldPos.z)); + blockPhysicsEntity = BlockChunkEntity.serverCreateBlockChunkEntity(realm, realPos, meshData); + blockPhysicsEntity.putData(EntityDataStrings.TERRAIN_IS_TERRAIN, true); + } // //then actually perform the attach // physicsObject = PhysicsUtils.attachTerrainRigidBody(physicsEntity,heightmap,true); // Realm realm = Globals.realmManager.getEntityRealm(physicsEntity); @@ -93,6 +109,7 @@ public class PhysicsDataCell { public void destroyPhysics(){ Realm realm = Globals.realmManager.getEntityRealm(physicsEntity); realm.getCollisionEngine().destroyPhysics(physicsEntity); + realm.getCollisionEngine().destroyPhysics(blockPhysicsEntity); } /**