block chunks properly stride
All checks were successful
studiorailgun/Renderer/pipeline/head This commit looks good

This commit is contained in:
austin 2025-05-23 18:58:42 -04:00
parent 456036c0d2
commit 086c6da72e
8 changed files with 80 additions and 37 deletions

View File

@ -1948,6 +1948,7 @@ Small fixes
(05/23/2025) (05/23/2025)
Town layout work Town layout work
Macro broadphase filters prior to per-voxel macro data intersection checks Macro broadphase filters prior to per-voxel macro data intersection checks
Block chunks properly stride

View File

@ -30,64 +30,68 @@ public class BlockDrawCell {
/** /**
* Number of child cells per parent cell * 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 * The LOD of the draw cell
*/ */
int lod; protected int lod;
//the main entity for the cell /**
List<Entity> modelEntities = new LinkedList<Entity>(); * the main entity for the cell
*/
private List<Entity> modelEntities = new LinkedList<Entity>();
/** /**
* The data for generating the visuals * The data for generating the visuals
*/ */
BlockChunkData chunkData; private BlockChunkData chunkData;
/** /**
* Tracks whether the draw cell has requested its chunk data or not * 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 * 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 * 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 * Number of failed generation attempts
*/ */
int failedGenerationAttempts = 0; private int failedGenerationAttempts = 0;
/** /**
* Labels an invalid distance cache * Labels an invalid distance cache
*/ */
static final int INVALID_DIST_CACHE = -1; private static final int INVALID_DIST_CACHE = -1;
/** /**
* The cached minimum distance * The cached minimum distance
*/ */
long cachedMinDistance = -1; private long cachedMinDistance = -1;
/** /**
* Target to notify on generation completion * Target to notify on generation completion
*/ */
BlockDrawCell notifyTarget = null; private BlockDrawCell notifyTarget = null;
/** /**
* The number of cells that have alerted this one * The number of cells that have alerted this one
*/ */
int generationAlertCount = 0; private int generationAlertCount = 0;
/** /**

View File

@ -480,7 +480,7 @@ public class CursorState {
public void setSelectedFab(BlockFab fab){ public void setSelectedFab(BlockFab fab){
Map<Integer,Boolean> solidsMap = Globals.gameConfigCurrent.getBlockData().getSolidsMap(); Map<Integer,Boolean> solidsMap = Globals.gameConfigCurrent.getBlockData().getSolidsMap();
QueuedModel queuedModel = new QueuedModel(() -> { 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); Globals.assetManager.queuedAsset(queuedModel);
EntityCreationUtils.makeEntityDrawablePreexistingModel(playerFabCursor, queuedModel.getPromisedPath()); EntityCreationUtils.makeEntityDrawablePreexistingModel(playerFabCursor, queuedModel.getPromisedPath());

View File

@ -71,7 +71,7 @@ public class BlockChunkEntity {
generationService.submit(() -> { generationService.submit(() -> {
BlockMeshData data; BlockMeshData data;
try { 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){ if(Globals.clientState.clientScene.containsEntity(solidsEnt) && data.getFaceElements().length > 0){
String modelPath = Globals.assetManager.queuedAsset(new QueuedModel(() -> { String modelPath = Globals.assetManager.queuedAsset(new QueuedModel(() -> {
return BlockMeshgen.generateBlockModel(data); return BlockMeshgen.generateBlockModel(data);
@ -122,7 +122,7 @@ public class BlockChunkEntity {
generationService.submit(() -> { generationService.submit(() -> {
BlockMeshData data; BlockMeshData data;
try { 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){ if(Globals.clientState.clientScene.containsEntity(transparentEnt) && data.getFaceElements().length > 0){
String modelPath = Globals.assetManager.queuedAsset(new QueuedModel(() -> { String modelPath = Globals.assetManager.queuedAsset(new QueuedModel(() -> {
return BlockMeshgen.generateBlockModel(data); return BlockMeshgen.generateBlockModel(data);

View File

@ -334,7 +334,7 @@ public class TerrainProtocol implements ServerProtocolTemplate<TerrainMessage> {
* @param worldZ the world z * @param worldZ the world z
* @param stride The stride of the data * @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"); Globals.profiler.beginAggregateCpuSample("TerrainProtocol(server).sendWorldSubChunk");
// System.out.println("Received request for chunk " + message.getworldX() + " " + message.getworldY()); // System.out.println("Received request for chunk " + message.getworldX() + " " + message.getworldY());

View File

@ -66,6 +66,11 @@ public class BlockMeshgen {
*/ */
static final int SAMPLER_DATA_SIZE = 1; 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 * Checks whether this block should be rasterized or not
@ -394,9 +399,10 @@ public class BlockMeshgen {
* @param chunkData The block chunk data * @param chunkData The block chunk data
* @param solids true to rasterize solid blocks, false to rasterize transparent blocks * @param solids true to rasterize solid blocks, false to rasterize transparent blocks
* @param solidsMap The solids map * @param solidsMap The solids map
* @param scalingFactor Scaling applied to the verts
* @return The mesh data * @return The mesh data
*/ */
public static BlockMeshData rasterize(BlockMeshgenData chunkData, boolean solids, Map<Integer,Boolean> solidsMap){ public static BlockMeshData rasterize(BlockMeshgenData chunkData, boolean solids, Map<Integer,Boolean> solidsMap, int scalingFactor){
BlockMeshData rVal = new BlockMeshData(); BlockMeshData rVal = new BlockMeshData();
//calculate quad meshes //calculate quad meshes
@ -447,7 +453,7 @@ public class BlockMeshgen {
lastFaceCount = indices.size(); lastFaceCount = indices.size();
rVal.shapeData.add(blockSingleShape); rVal.shapeData.add(blockSingleShape);
} }
// //
//store in flat arrays //store in flat arrays
// //
@ -456,9 +462,9 @@ public class BlockMeshgen {
rVal.vertices = new float[verts.size() * 3]; rVal.vertices = new float[verts.size() * 3];
for(int i = 0; i < verts.size(); i++){ for(int i = 0; i < verts.size(); i++){
Vector3f currentVert = verts.get(i); Vector3f currentVert = verts.get(i);
rVal.vertices[3 * i + 0] = currentVert.x; rVal.vertices[3 * i + 0] = currentVert.x * scalingFactor;
rVal.vertices[3 * i + 1] = currentVert.y; rVal.vertices[3 * i + 1] = currentVert.y * scalingFactor;
rVal.vertices[3 * i + 2] = currentVert.z; rVal.vertices[3 * i + 2] = currentVert.z * scalingFactor;
} }
rVal.vertBuffer = BufferUtils.createFloatBuffer(rVal.vertices.length); rVal.vertBuffer = BufferUtils.createFloatBuffer(rVal.vertices.length);
rVal.vertBuffer.put(rVal.vertices); rVal.vertBuffer.put(rVal.vertices);
@ -486,8 +492,8 @@ public class BlockMeshgen {
rVal.uvs = new float[uvs.size() * 2]; rVal.uvs = new float[uvs.size() * 2];
for(int i = 0; i < uvs.size(); i++){ for(int i = 0; i < uvs.size(); i++){
Vector2f currentUV = uvs.get(i); Vector2f currentUV = uvs.get(i);
rVal.uvs[2 * i + 0] = currentUV.x; rVal.uvs[2 * i + 0] = currentUV.x * scalingFactor;
rVal.uvs[2 * i + 1] = currentUV.y; rVal.uvs[2 * i + 1] = currentUV.y * scalingFactor;
} }
rVal.uvBuffer = BufferUtils.createFloatBuffer(rVal.uvs.length); rVal.uvBuffer = BufferUtils.createFloatBuffer(rVal.uvs.length);
rVal.uvBuffer.put(rVal.uvs); rVal.uvBuffer.put(rVal.uvs);
@ -509,7 +515,7 @@ public class BlockMeshgen {
* @return The mesh data * @return The mesh data
*/ */
public static BlockMeshData rasterize(BlockMeshgenData chunkData){ public static BlockMeshData rasterize(BlockMeshgenData chunkData){
return BlockMeshgen.rasterize(chunkData, true, null); return BlockMeshgen.rasterize(chunkData, true, null, BlockMeshgen.DEFAULT_SCALING_FACTOR);
} }
/** /**

View File

@ -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 * Converts a world space vector to a real space vector
* @param position The world 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 * Converts a world coordinate to a macro scale coordinate
* @param worldPos The world position * @param worldPos The world position

View File

@ -21,6 +21,7 @@ import electrosphere.server.macro.spatial.MacroLODObject;
import electrosphere.server.macro.spatial.MacroObject; import electrosphere.server.macro.spatial.MacroObject;
import electrosphere.server.macro.structure.VirtualStructure; import electrosphere.server.macro.structure.VirtualStructure;
import electrosphere.server.physics.block.diskmap.ServerBlockChunkDiskMap; 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 * 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); chunk = chunkCache.get(worldX, worldY, worldZ, stride);
} else { } else {
//pull from disk if it exists //pull from disk if it exists
if(chunkDiskMap != null){ if(chunkDiskMap != null && stride == ServerChunkCache.STRIDE_FULL_RES){
if(chunkDiskMap.containsBlocksAtPosition(worldX, worldY, worldZ)){ if(chunkDiskMap.containsBlocksAtPosition(worldX, worldY, worldZ)){
chunk = chunkDiskMap.getBlockChunk(worldX, worldY, worldZ); chunk = chunkDiskMap.getBlockChunk(worldX, worldY, worldZ);
} }
@ -188,7 +189,7 @@ public class ServerBlockChunkGenerationThread implements Runnable {
chunk.setWorldX(worldX); chunk.setWorldX(worldX);
chunk.setWorldY(worldY); chunk.setWorldY(worldY);
chunk.setWorldZ(worldZ); chunk.setWorldZ(worldZ);
ServerBlockChunkGenerationThread.generate(chunk, macroData, worldX, worldY, worldZ); ServerBlockChunkGenerationThread.generate(chunk, macroData, worldX, worldY, worldZ, stride);
} }
if(chunk != null){ if(chunk != null){
chunkCache.add(worldX, worldY, worldZ, stride, chunk); chunkCache.add(worldX, worldY, worldZ, stride, chunk);
@ -204,36 +205,45 @@ public class ServerBlockChunkGenerationThread implements Runnable {
* @param worldX THe world x coordinate * @param worldX THe world x coordinate
* @param worldY The world y coordinate * @param worldY The world y coordinate
* @param worldZ The world z 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){ if(macroData == null){
chunk.setHomogenousValue(0); chunk.setHomogenousValue(0);
return; return;
} }
//check if this chunk intersects any macro data //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<VirtualStructure> filtered = macroData.getStructures().stream().filter((VirtualStructure struct) -> {return !struct.isRepairable() && struct.getAABB().testAABB(localAABB);}).collect(Collectors.toList()); List<VirtualStructure> filtered = macroData.getStructures().stream().filter((VirtualStructure struct) -> {return !struct.isRepairable() && struct.getAABB().testAABB(localAABB);}).collect(Collectors.toList());
if(filtered.size() > 0){ if(filtered.size() > 0){
Vector3i chunkPos = new Vector3i(worldX, worldY, worldZ); Vector3i chunkPos = new Vector3i(worldX, worldY, worldZ);
Vector3i blockPos = new Vector3i(0,0,0); Vector3i blockPos = new Vector3i(0,0,0);
Vector3d chunkRealPos = ServerWorldData.convertChunkToRealSpace(chunkPos); Vector3d chunkRealPos = ServerWorldData.convertChunkToRealSpace(chunkPos);
Vector3i localBlockPos = new Vector3i(); Vector3i localBlockPos = new Vector3i();
Vector3d currRealPos = new Vector3d(chunkRealPos);
//contains at least one structure //contains at least one structure
for(int x = 0; x < BlockChunkData.CHUNK_DATA_WIDTH; x++){ for(int x = 0; x < BlockChunkData.CHUNK_DATA_WIDTH; x++){
for(int y = 0; y < BlockChunkData.CHUNK_DATA_WIDTH; y++){ for(int y = 0; y < BlockChunkData.CHUNK_DATA_WIDTH; y++){
for(int z = 0; z < BlockChunkData.CHUNK_DATA_WIDTH; z++){ for(int z = 0; z < BlockChunkData.CHUNK_DATA_WIDTH; z++){
boolean placedBlock = false; 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 //try placing a structure block
blockPos.set(x,y,z); blockPos.set(x*strideMultiplier,y*strideMultiplier,z*strideMultiplier);
Vector3d currRealPoint = ServerWorldData.convertLocalBlockToRealSpace(chunkPos, blockPos);
for(VirtualStructure struct : filtered){ 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(); AABBd aabb = struct.getAABB();
localBlockPos.set( localBlockPos.set(
(int)((chunkRealPos.x + (x * BlockChunkData.BLOCK_SIZE_MULTIPLIER) - aabb.minX) / BlockChunkData.BLOCK_SIZE_MULTIPLIER), (int)((currRealPos.x - aabb.minX) / BlockChunkData.BLOCK_SIZE_MULTIPLIER),
(int)((chunkRealPos.y + (y * BlockChunkData.BLOCK_SIZE_MULTIPLIER) - aabb.minY) / BlockChunkData.BLOCK_SIZE_MULTIPLIER), (int)((currRealPos.y - aabb.minY) / BlockChunkData.BLOCK_SIZE_MULTIPLIER),
(int)((chunkRealPos.z + (z * BlockChunkData.BLOCK_SIZE_MULTIPLIER) - aabb.minZ) / 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 //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){ if(localBlockPos.x < struct.getFab().getDimensions().x && localBlockPos.y < struct.getFab().getDimensions().y && localBlockPos.z < struct.getFab().getDimensions().z){