diff --git a/docs/src/progress/renderertodo.md b/docs/src/progress/renderertodo.md index df8d199e..e337d2e6 100644 --- a/docs/src/progress/renderertodo.md +++ b/docs/src/progress/renderertodo.md @@ -1948,6 +1948,7 @@ Small fixes (05/23/2025) Town layout work Macro broadphase filters prior to per-voxel macro data intersection checks +Block chunks properly stride diff --git a/src/main/java/electrosphere/client/block/cells/BlockDrawCell.java b/src/main/java/electrosphere/client/block/cells/BlockDrawCell.java index 86ec9b92..521b9996 100644 --- a/src/main/java/electrosphere/client/block/cells/BlockDrawCell.java +++ b/src/main/java/electrosphere/client/block/cells/BlockDrawCell.java @@ -30,64 +30,68 @@ public class BlockDrawCell { /** * Number of child cells per parent cell */ - static final int CHILD_CELLS_PER_PARENT = 8; + private static final int CHILD_CELLS_PER_PARENT = 8; - //the position of the draw cell in world coordinates - Vector3i worldPos; + /** + * the position of the draw cell in world coordinates + */ + private Vector3i worldPos; /** * The LOD of the draw cell */ - int lod; + protected int lod; - //the main entity for the cell - List modelEntities = new LinkedList(); + /** + * the main entity for the cell + */ + private List modelEntities = new LinkedList(); /** * The data for generating the visuals */ - BlockChunkData chunkData; + private BlockChunkData chunkData; /** * Tracks whether the draw cell has requested its chunk data or not */ - boolean hasRequested = false; + private boolean hasRequested = false; /** * Tracks whether the draw cell has generated its entity or not */ - boolean hasGenerated = false; + private boolean hasGenerated = false; /** * Tracks whether this draw cell is flagged as homogenous from the server or not */ - boolean homogenous = false; + private boolean homogenous = false; /** * Number of failed generation attempts */ - int failedGenerationAttempts = 0; + private int failedGenerationAttempts = 0; /** * Labels an invalid distance cache */ - static final int INVALID_DIST_CACHE = -1; + private static final int INVALID_DIST_CACHE = -1; /** * The cached minimum distance */ - long cachedMinDistance = -1; + private long cachedMinDistance = -1; /** * Target to notify on generation completion */ - BlockDrawCell notifyTarget = null; + private BlockDrawCell notifyTarget = null; /** * The number of cells that have alerted this one */ - int generationAlertCount = 0; + private int generationAlertCount = 0; /** diff --git a/src/main/java/electrosphere/controls/cursor/CursorState.java b/src/main/java/electrosphere/controls/cursor/CursorState.java index d71b4b44..d0f3bc1f 100644 --- a/src/main/java/electrosphere/controls/cursor/CursorState.java +++ b/src/main/java/electrosphere/controls/cursor/CursorState.java @@ -480,7 +480,7 @@ public class CursorState { public void setSelectedFab(BlockFab fab){ Map solidsMap = Globals.gameConfigCurrent.getBlockData().getSolidsMap(); QueuedModel queuedModel = new QueuedModel(() -> { - return BlockMeshgen.generateBlockModel(BlockMeshgen.rasterize(fab,false,solidsMap)); + return BlockMeshgen.generateBlockModel(BlockMeshgen.rasterize(fab,false,solidsMap,BlockMeshgen.DEFAULT_SCALING_FACTOR)); }); Globals.assetManager.queuedAsset(queuedModel); EntityCreationUtils.makeEntityDrawablePreexistingModel(playerFabCursor, queuedModel.getPromisedPath()); diff --git a/src/main/java/electrosphere/entity/types/terrain/BlockChunkEntity.java b/src/main/java/electrosphere/entity/types/terrain/BlockChunkEntity.java index 69a26b7d..e60b2472 100644 --- a/src/main/java/electrosphere/entity/types/terrain/BlockChunkEntity.java +++ b/src/main/java/electrosphere/entity/types/terrain/BlockChunkEntity.java @@ -71,7 +71,7 @@ public class BlockChunkEntity { generationService.submit(() -> { BlockMeshData data; try { - data = BlockMeshgen.rasterize(chunkData, true, solidsMap); + data = BlockMeshgen.rasterize(chunkData, true, solidsMap,(int)Math.pow(2,levelOfDetail)); if(Globals.clientState.clientScene.containsEntity(solidsEnt) && data.getFaceElements().length > 0){ String modelPath = Globals.assetManager.queuedAsset(new QueuedModel(() -> { return BlockMeshgen.generateBlockModel(data); @@ -122,7 +122,7 @@ public class BlockChunkEntity { generationService.submit(() -> { BlockMeshData data; try { - data = BlockMeshgen.rasterize(chunkData, false, solidsMap); + data = BlockMeshgen.rasterize(chunkData, false, solidsMap, (int)Math.pow(2,levelOfDetail)); if(Globals.clientState.clientScene.containsEntity(transparentEnt) && data.getFaceElements().length > 0){ String modelPath = Globals.assetManager.queuedAsset(new QueuedModel(() -> { return BlockMeshgen.generateBlockModel(data); diff --git a/src/main/java/electrosphere/net/server/protocol/TerrainProtocol.java b/src/main/java/electrosphere/net/server/protocol/TerrainProtocol.java index 03239c3d..a1de91f6 100644 --- a/src/main/java/electrosphere/net/server/protocol/TerrainProtocol.java +++ b/src/main/java/electrosphere/net/server/protocol/TerrainProtocol.java @@ -334,7 +334,7 @@ public class TerrainProtocol implements ServerProtocolTemplate { * @param worldZ the world z * @param stride The stride of the data */ - static void sendBlocksAsyncStrided(ServerConnectionHandler connectionHandler, int worldX, int worldY, int worldZ, int stride){ + private static void sendBlocksAsyncStrided(ServerConnectionHandler connectionHandler, int worldX, int worldY, int worldZ, int stride){ Globals.profiler.beginAggregateCpuSample("TerrainProtocol(server).sendWorldSubChunk"); // System.out.println("Received request for chunk " + message.getworldX() + " " + message.getworldY()); diff --git a/src/main/java/electrosphere/renderer/meshgen/BlockMeshgen.java b/src/main/java/electrosphere/renderer/meshgen/BlockMeshgen.java index 5f417f64..8753c777 100644 --- a/src/main/java/electrosphere/renderer/meshgen/BlockMeshgen.java +++ b/src/main/java/electrosphere/renderer/meshgen/BlockMeshgen.java @@ -66,6 +66,11 @@ public class BlockMeshgen { */ static final int SAMPLER_DATA_SIZE = 1; + /** + * Default scaling factor + */ + public static final int DEFAULT_SCALING_FACTOR = 1; + /** * Checks whether this block should be rasterized or not @@ -394,9 +399,10 @@ public class BlockMeshgen { * @param chunkData The block chunk data * @param solids true to rasterize solid blocks, false to rasterize transparent blocks * @param solidsMap The solids map + * @param scalingFactor Scaling applied to the verts * @return The mesh data */ - public static BlockMeshData rasterize(BlockMeshgenData chunkData, boolean solids, Map solidsMap){ + public static BlockMeshData rasterize(BlockMeshgenData chunkData, boolean solids, Map solidsMap, int scalingFactor){ BlockMeshData rVal = new BlockMeshData(); //calculate quad meshes @@ -447,7 +453,7 @@ public class BlockMeshgen { lastFaceCount = indices.size(); rVal.shapeData.add(blockSingleShape); } - + // //store in flat arrays // @@ -456,9 +462,9 @@ public class BlockMeshgen { rVal.vertices = new float[verts.size() * 3]; for(int i = 0; i < verts.size(); i++){ Vector3f currentVert = verts.get(i); - rVal.vertices[3 * i + 0] = currentVert.x; - rVal.vertices[3 * i + 1] = currentVert.y; - rVal.vertices[3 * i + 2] = currentVert.z; + rVal.vertices[3 * i + 0] = currentVert.x * scalingFactor; + rVal.vertices[3 * i + 1] = currentVert.y * scalingFactor; + rVal.vertices[3 * i + 2] = currentVert.z * scalingFactor; } rVal.vertBuffer = BufferUtils.createFloatBuffer(rVal.vertices.length); rVal.vertBuffer.put(rVal.vertices); @@ -486,8 +492,8 @@ public class BlockMeshgen { rVal.uvs = new float[uvs.size() * 2]; for(int i = 0; i < uvs.size(); i++){ Vector2f currentUV = uvs.get(i); - rVal.uvs[2 * i + 0] = currentUV.x; - rVal.uvs[2 * i + 1] = currentUV.y; + rVal.uvs[2 * i + 0] = currentUV.x * scalingFactor; + rVal.uvs[2 * i + 1] = currentUV.y * scalingFactor; } rVal.uvBuffer = BufferUtils.createFloatBuffer(rVal.uvs.length); rVal.uvBuffer.put(rVal.uvs); @@ -509,7 +515,7 @@ public class BlockMeshgen { * @return The mesh data */ public static BlockMeshData rasterize(BlockMeshgenData chunkData){ - return BlockMeshgen.rasterize(chunkData, true, null); + return BlockMeshgen.rasterize(chunkData, true, null, BlockMeshgen.DEFAULT_SCALING_FACTOR); } /** diff --git a/src/main/java/electrosphere/server/datacell/ServerWorldData.java b/src/main/java/electrosphere/server/datacell/ServerWorldData.java index d02ea60b..fa13238c 100644 --- a/src/main/java/electrosphere/server/datacell/ServerWorldData.java +++ b/src/main/java/electrosphere/server/datacell/ServerWorldData.java @@ -326,6 +326,15 @@ public class ServerWorldData { ); } + + public static void convertRealToChunkSpace(Vector3d position, Vector3i destVec){ + destVec.set( + ServerWorldData.convertRealToChunkSpace(position.x), + ServerWorldData.convertRealToChunkSpace(position.y), + ServerWorldData.convertRealToChunkSpace(position.z) + ); + } + /** * Converts a world space vector to a real space vector * @param position The world space vector @@ -387,6 +396,19 @@ public class ServerWorldData { ); } + /** + * Converts a real coordinate to a local block grid space coordinate + * @param position The real coordinate + * @param destVec The vector to store the result in + */ + public static void convertRealToLocalBlockSpace(Vector3d position, Vector3i destVec){ + destVec.set( + ServerWorldData.convertRealToLocalBlockSpace(position.x), + ServerWorldData.convertRealToLocalBlockSpace(position.y), + ServerWorldData.convertRealToLocalBlockSpace(position.z) + ); + } + /** * Converts a world coordinate to a macro scale coordinate * @param worldPos The world position diff --git a/src/main/java/electrosphere/server/physics/block/manager/ServerBlockChunkGenerationThread.java b/src/main/java/electrosphere/server/physics/block/manager/ServerBlockChunkGenerationThread.java index 9df96a79..5658f420 100644 --- a/src/main/java/electrosphere/server/physics/block/manager/ServerBlockChunkGenerationThread.java +++ b/src/main/java/electrosphere/server/physics/block/manager/ServerBlockChunkGenerationThread.java @@ -21,6 +21,7 @@ import electrosphere.server.macro.spatial.MacroLODObject; import electrosphere.server.macro.spatial.MacroObject; import electrosphere.server.macro.structure.VirtualStructure; import electrosphere.server.physics.block.diskmap.ServerBlockChunkDiskMap; +import electrosphere.server.physics.terrain.manager.ServerChunkCache; /** * A job that fetches a chunk, either by generating it or by reading it from disk @@ -177,7 +178,7 @@ public class ServerBlockChunkGenerationThread implements Runnable { chunk = chunkCache.get(worldX, worldY, worldZ, stride); } else { //pull from disk if it exists - if(chunkDiskMap != null){ + if(chunkDiskMap != null && stride == ServerChunkCache.STRIDE_FULL_RES){ if(chunkDiskMap.containsBlocksAtPosition(worldX, worldY, worldZ)){ chunk = chunkDiskMap.getBlockChunk(worldX, worldY, worldZ); } @@ -188,7 +189,7 @@ public class ServerBlockChunkGenerationThread implements Runnable { chunk.setWorldX(worldX); chunk.setWorldY(worldY); chunk.setWorldZ(worldZ); - ServerBlockChunkGenerationThread.generate(chunk, macroData, worldX, worldY, worldZ); + ServerBlockChunkGenerationThread.generate(chunk, macroData, worldX, worldY, worldZ, stride); } if(chunk != null){ chunkCache.add(worldX, worldY, worldZ, stride, chunk); @@ -204,36 +205,45 @@ public class ServerBlockChunkGenerationThread implements Runnable { * @param worldX THe world x coordinate * @param worldY The world y coordinate * @param worldZ The world z coordinate + * @param stride The stride of the data to generate */ - protected static void generate(BlockChunkData chunk, MacroData macroData, int worldX, int worldY, int worldZ){ + protected static void generate(BlockChunkData chunk, MacroData macroData, int worldX, int worldY, int worldZ, int stride){ if(macroData == null){ chunk.setHomogenousValue(0); return; } //check if this chunk intersects any macro data - AABBd localAABB = new AABBd(ServerWorldData.convertChunkToRealSpace(worldX,worldY,worldZ),ServerWorldData.convertChunkToRealSpace(worldX+1,worldY+1,worldZ+1)); + int strideMultiplier = (int)Math.pow(2,stride); + if(strideMultiplier > 16){ + throw new Error("Invalid stride size!"); + } + AABBd localAABB = new AABBd(ServerWorldData.convertChunkToRealSpace(worldX,worldY,worldZ),ServerWorldData.convertChunkToRealSpace(worldX+strideMultiplier,worldY+strideMultiplier,worldZ+strideMultiplier)); List filtered = macroData.getStructures().stream().filter((VirtualStructure struct) -> {return !struct.isRepairable() && struct.getAABB().testAABB(localAABB);}).collect(Collectors.toList()); if(filtered.size() > 0){ Vector3i chunkPos = new Vector3i(worldX, worldY, worldZ); Vector3i blockPos = new Vector3i(0,0,0); Vector3d chunkRealPos = ServerWorldData.convertChunkToRealSpace(chunkPos); Vector3i localBlockPos = new Vector3i(); + Vector3d currRealPos = new Vector3d(chunkRealPos); //contains at least one structure for(int x = 0; x < BlockChunkData.CHUNK_DATA_WIDTH; x++){ for(int y = 0; y < BlockChunkData.CHUNK_DATA_WIDTH; y++){ for(int z = 0; z < BlockChunkData.CHUNK_DATA_WIDTH; z++){ boolean placedBlock = false; + currRealPos.set(chunkRealPos).add(x * strideMultiplier * BlockChunkData.BLOCK_SIZE_MULTIPLIER,y * strideMultiplier * BlockChunkData.BLOCK_SIZE_MULTIPLIER,z * strideMultiplier * BlockChunkData.BLOCK_SIZE_MULTIPLIER); + ServerWorldData.convertRealToLocalBlockSpace(currRealPos,blockPos); + ServerWorldData.convertRealToChunkSpace(currRealPos,chunkPos); + //try placing a structure block - blockPos.set(x,y,z); - Vector3d currRealPoint = ServerWorldData.convertLocalBlockToRealSpace(chunkPos, blockPos); + blockPos.set(x*strideMultiplier,y*strideMultiplier,z*strideMultiplier); for(VirtualStructure struct : filtered){ - if(struct.getAABB().testPoint(currRealPoint.x, currRealPoint.y, currRealPoint.z)){ + if(struct.getAABB().testPoint(currRealPos.x, currRealPos.y, currRealPos.z)){ AABBd aabb = struct.getAABB(); localBlockPos.set( - (int)((chunkRealPos.x + (x * BlockChunkData.BLOCK_SIZE_MULTIPLIER) - aabb.minX) / BlockChunkData.BLOCK_SIZE_MULTIPLIER), - (int)((chunkRealPos.y + (y * BlockChunkData.BLOCK_SIZE_MULTIPLIER) - aabb.minY) / BlockChunkData.BLOCK_SIZE_MULTIPLIER), - (int)((chunkRealPos.z + (z * BlockChunkData.BLOCK_SIZE_MULTIPLIER) - aabb.minZ) / BlockChunkData.BLOCK_SIZE_MULTIPLIER) + (int)((currRealPos.x - aabb.minX) / BlockChunkData.BLOCK_SIZE_MULTIPLIER), + (int)((currRealPos.y - aabb.minY) / BlockChunkData.BLOCK_SIZE_MULTIPLIER), + (int)((currRealPos.z - aabb.minZ) / BlockChunkData.BLOCK_SIZE_MULTIPLIER) ); //structure file might have dimensions larger than fab, so need to make sure we're inbounds on fab file to draw data from fab file if(localBlockPos.x < struct.getFab().getDimensions().x && localBlockPos.y < struct.getFab().getDimensions().y && localBlockPos.z < struct.getFab().getDimensions().z){