Skip to content

Commit

Permalink
Use fixed-size bounding boxes for chunk sections
Browse files Browse the repository at this point in the history
Trying to fit the bounds more accurately only eliminates
a very small amount of geometry (<0.1% in most cases, <0.3% at most).

Since this code adds complexity and overhead during
command buffer building, the trade-off of having it really
doesn't make sense.
  • Loading branch information
jellysquid3 committed Jul 23, 2023
1 parent a04ce05 commit 6bf02af
Show file tree
Hide file tree
Showing 10 changed files with 138 additions and 143 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,29 +11,46 @@
public class ChunkGraphicsState {
private final GlBufferSegment vertexSegment;

private final VertexRange model;
private final VertexRange[] parts;

private final int flags;

public ChunkGraphicsState(GlBufferSegment vertexSegment, ChunkMeshData data) {
Validate.notNull(vertexSegment);

this.vertexSegment = vertexSegment;

this.model = new VertexRange(0, data.getVertexCount());
this.parts = new VertexRange[ModelQuadFacing.COUNT];

int flags = 0;

for (Map.Entry<ModelQuadFacing, VertexRange> entry : data.getParts().entrySet()) {
this.parts[entry.getKey().ordinal()] = entry.getValue();
flags |= 1 << entry.getKey().ordinal();
}

this.flags = flags;
}

public void delete() {
this.vertexSegment.delete();
}

public VertexRange getModelPart(ModelQuadFacing facing) {
return this.parts[facing.ordinal()];
public VertexRange getModelPart(int facing) {
return this.parts[facing];
}

public GlBufferSegment getVertexSegment() {
return this.vertexSegment;
}

public int getFlags() {
return this.flags;
}

public VertexRange getModel() {
return this.model;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
import me.jellysquid.mods.sodium.client.model.quad.properties.ModelQuadFacing;
import me.jellysquid.mods.sodium.client.render.chunk.region.RenderRegionManager;
import me.jellysquid.mods.sodium.client.render.chunk.vertex.format.ChunkVertexType;
import me.jellysquid.mods.sodium.client.render.chunk.data.ChunkRenderBounds;
import me.jellysquid.mods.sodium.client.render.chunk.vertex.format.ChunkMeshAttribute;
import me.jellysquid.mods.sodium.client.render.chunk.region.RenderRegion;
import me.jellysquid.mods.sodium.client.render.chunk.shader.ChunkShaderBindingPoints;
Expand Down Expand Up @@ -96,48 +95,89 @@ private MultiDrawBatch prepareDrawBatch(RenderRegion.RenderRegionStorage storage
int baseVertex = state.getVertexSegment()
.getOffset();

this.addDrawCalls(camera, section, state, baseVertex);
this.collectDrawCommands(camera, section, state, baseVertex);
}

return commandBuffer;
}

private void addDrawCalls(ChunkCameraContext camera, RenderSection section, ChunkGraphicsState state, int baseVertex) {
var commandBufferBuilder = this.commandBufferBuilder;
private void collectDrawCommands(ChunkCameraContext camera, RenderSection section, ChunkGraphicsState state, int baseVertex) {
if (this.isBlockFaceCullingEnabled) {
this.addFilteredDrawCommands(camera, section, state, baseVertex);
} else {
this.addUnfilteredDrawCommands(state, baseVertex);
}
}

addDrawCall(commandBufferBuilder, state.getModelPart(ModelQuadFacing.UNASSIGNED), baseVertex);
private void addFilteredDrawCommands(ChunkCameraContext camera, RenderSection section, ChunkGraphicsState state, int baseVertex) {
int faces = this.getFrontFacingPlanes(camera, section) & state.getFlags();

if (this.isBlockFaceCullingEnabled) {
ChunkRenderBounds bounds = section.getBounds();
if (faces == 0) {
return;
}

if (camera.posY > bounds.minY) {
addDrawCall(commandBufferBuilder, state.getModelPart(ModelQuadFacing.UP), baseVertex);
for (int facing = 0; facing < ModelQuadFacing.COUNT; facing++) {
if ((faces & (1 << facing)) == 0) {
continue;
}

if (camera.posY < bounds.maxY) {
addDrawCall(commandBufferBuilder, state.getModelPart(ModelQuadFacing.DOWN), baseVertex);
}
addDrawCommand(this.commandBufferBuilder, state.getModelPart(facing), baseVertex);
}
}

if (camera.posX > bounds.minX) {
addDrawCall(commandBufferBuilder, state.getModelPart(ModelQuadFacing.EAST), baseVertex);
}
private void addUnfilteredDrawCommands(ChunkGraphicsState state, int baseVertex) {
var model = state.getModel();

if (camera.posX < bounds.maxX) {
addDrawCall(commandBufferBuilder, state.getModelPart(ModelQuadFacing.WEST), baseVertex);
}
if (model != null) {
addDrawCommand(this.commandBufferBuilder, model, baseVertex);
}
}

if (camera.posZ > bounds.minZ) {
addDrawCall(commandBufferBuilder, state.getModelPart(ModelQuadFacing.SOUTH), baseVertex);
}
/**
* The number of blocks to extend the render bounds of a chunk section. Since block models can emit geometry
* which is outside the section's bounds, we need some margin.
*/
private static final int RENDER_BOUNDS_MARGIN = 4;

if (camera.posZ < bounds.maxZ) {
addDrawCall(commandBufferBuilder, state.getModelPart(ModelQuadFacing.NORTH), baseVertex);
}
} else {
for (ModelQuadFacing facing : ModelQuadFacing.DIRECTIONS) {
addDrawCall(commandBufferBuilder, state.getModelPart(facing), baseVertex);
}
private static final int MODEL_UNASSIGNED = ModelQuadFacing.UNASSIGNED.ordinal();
private static final int MODEL_POS_X = ModelQuadFacing.EAST.ordinal();
private static final int MODEL_NEG_X = ModelQuadFacing.WEST.ordinal();
private static final int MODEL_POS_Y = ModelQuadFacing.UP.ordinal();
private static final int MODEL_NEG_Y = ModelQuadFacing.DOWN.ordinal();
private static final int MODEL_POS_Z = ModelQuadFacing.SOUTH.ordinal();
private static final int MODEL_NEG_Z = ModelQuadFacing.NORTH.ordinal();

private int getFrontFacingPlanes(ChunkCameraContext camera, RenderSection section) {
int flags = 0;

// Always added, as we can't determine whether these faces are visible
flags |= 1 << MODEL_UNASSIGNED;

if (camera.blockX > (section.getMinX() - RENDER_BOUNDS_MARGIN)) {
flags |= 1 << MODEL_POS_X;
}

if (camera.blockX < (section.getMaxX() + RENDER_BOUNDS_MARGIN)) {
flags |= 1 << MODEL_NEG_X;
}

if (camera.blockY > (section.getMinY() - RENDER_BOUNDS_MARGIN)) {
flags |= 1 << MODEL_POS_Y;
}

if (camera.blockY < (section.getMaxY() + RENDER_BOUNDS_MARGIN)) {
flags |= 1 << MODEL_NEG_Y;
}

if (camera.blockZ > (section.getMinZ() - RENDER_BOUNDS_MARGIN)) {
flags |= 1 << MODEL_POS_Z;
}

if (camera.blockZ < (section.getMaxZ() + RENDER_BOUNDS_MARGIN)) {
flags |= 1 << MODEL_NEG_Z;
}

return flags;
}

private GlTessellation createTessellationForRegion(CommandList commandList, RenderRegion region, TerrainRenderPass pass, SharedQuadIndexBuffer indexBuffer) {
Expand Down Expand Up @@ -167,10 +207,8 @@ private void setModelMatrixUniforms(ChunkShaderInterface shader, RenderRegion re
shader.setRegionOffset(x, y, z);
}

private static void addDrawCall(MultiDrawBatch batch, VertexRange part, int baseVertex) {
if (part != null) {
batch.add(0L, (part.vertexCount() >> 2) * 6, baseVertex + part.vertexStart());
}
private static void addDrawCommand(MultiDrawBatch batch, VertexRange part, int baseVertex) {
batch.add(0L, (part.vertexCount() >> 2) * 6, baseVertex + part.vertexStart());
}

private GlTessellation createRegionTessellation(CommandList commandList, RenderRegion region, SharedQuadIndexBuffer indexBuffer) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import me.jellysquid.mods.sodium.client.render.SodiumWorldRenderer;
import me.jellysquid.mods.sodium.client.render.chunk.compile.ChunkBuildResult;
import me.jellysquid.mods.sodium.client.render.chunk.data.ChunkRenderBounds;
import me.jellysquid.mods.sodium.client.render.chunk.data.ChunkRenderData;
import me.jellysquid.mods.sodium.client.render.chunk.region.RenderRegion;
import me.jellysquid.mods.sodium.client.render.texture.SpriteUtil;
Expand Down Expand Up @@ -207,10 +206,6 @@ public int getChunkZ() {
return this.chunkZ;
}

public ChunkRenderBounds getBounds() {
return this.data.getBounds();
}

public boolean isTickable() {
return this.tickable;
}
Expand Down Expand Up @@ -320,4 +315,28 @@ public boolean isCulledByFrustum(Frustum frustum) {

return !frustum.isBoxVisible(x, y, z, x + 16.0f, y + 16.0f, z + 16.0f);
}

public int getMinX() {
return this.getOriginX();
}

public int getMaxX() {
return this.getOriginX() + 16;
}

public int getMinY() {
return this.getOriginY();
}

public int getMaxY() {
return this.getOriginY() + 16;
}

public int getMinZ() {
return this.getOriginZ();
}

public int getMaxZ() {
return this.getOriginZ() + 16;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ public ChunkMeshData createMesh(TerrainRenderPass pass) {

mergedBufferBuilder.flip();

return new ChunkMeshData(mergedBuffer, vertexRanges);
return new ChunkMeshData(mergedBuffer, vertexRanges, vertexCount);
}

public void destroy() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
import me.jellysquid.mods.sodium.client.model.quad.properties.ModelQuadOrientation;
import me.jellysquid.mods.sodium.client.render.chunk.compile.ChunkBuildBuffers;
import me.jellysquid.mods.sodium.client.render.chunk.compile.buffers.ChunkModelBuilder;
import me.jellysquid.mods.sodium.client.render.chunk.data.ChunkRenderBounds;
import me.jellysquid.mods.sodium.client.render.chunk.terrain.material.DefaultMaterials;
import me.jellysquid.mods.sodium.client.render.chunk.terrain.material.Material;
import me.jellysquid.mods.sodium.client.render.chunk.vertex.format.ChunkVertexEncoder;
Expand Down Expand Up @@ -54,7 +53,7 @@ public BlockRenderer(ColorProviderRegistry colorRegistry, LightPipelineProvider
this.useAmbientOcclusion = MinecraftClient.isAmbientOcclusionEnabled();
}

public void renderModel(BlockRenderContext ctx, ChunkBuildBuffers buffers, ChunkRenderBounds.Builder bounds) {
public void renderModel(BlockRenderContext ctx, ChunkBuildBuffers buffers) {
var material = DefaultMaterials.forBlockState(ctx.state());
var meshBuilder = buffers.get(material);

Expand All @@ -67,14 +66,14 @@ public void renderModel(BlockRenderContext ctx, ChunkBuildBuffers buffers, Chunk
List<BakedQuad> quads = this.getGeometry(ctx, face);

if (!quads.isEmpty() && this.isFaceVisible(ctx, face)) {
this.renderQuadList(ctx, material, lighter, colorizer, renderOffset, meshBuilder, quads, face, bounds);
this.renderQuadList(ctx, material, lighter, colorizer, renderOffset, meshBuilder, quads, face);
}
}

List<BakedQuad> all = this.getGeometry(ctx, null);

if (!all.isEmpty()) {
this.renderQuadList(ctx, material, lighter, colorizer, renderOffset, meshBuilder, all, null, bounds);
this.renderQuadList(ctx, material, lighter, colorizer, renderOffset, meshBuilder, all, null);
}
}

Expand All @@ -90,7 +89,7 @@ private boolean isFaceVisible(BlockRenderContext ctx, Direction face) {
}

private void renderQuadList(BlockRenderContext ctx, Material material, LightPipeline lighter, ColorProvider<BlockState> colorizer, Vec3d offset,
ChunkModelBuilder builder, List<BakedQuad> quads, Direction cullFace, ChunkRenderBounds.Builder bounds) {
ChunkModelBuilder builder, List<BakedQuad> quads, Direction cullFace) {

// This is a very hot allocation, iterate over it manually
// noinspection ForLoopReplaceableByForEach
Expand All @@ -100,7 +99,7 @@ private void renderQuadList(BlockRenderContext ctx, Material material, LightPipe
final var lightData = this.getVertexLight(ctx, lighter, cullFace, quad);
final var vertexColors = this.getVertexColors(ctx, colorizer, quad);

this.writeGeometry(ctx, builder, offset, material, quad, vertexColors, lightData, bounds);
this.writeGeometry(ctx, builder, offset, material, quad, vertexColors, lightData);

Sprite sprite = quad.getSprite();

Expand Down Expand Up @@ -135,8 +134,7 @@ private void writeGeometry(BlockRenderContext ctx,
Material material,
BakedQuadView quad,
int[] colors,
QuadLightData light,
ChunkRenderBounds.Builder bounds)
QuadLightData light)
{
ModelQuadOrientation orientation = ModelQuadOrientation.orientByBrightness(light.br, light.lm);
var vertices = this.vertices;
Expand All @@ -157,8 +155,6 @@ private void writeGeometry(BlockRenderContext ctx,
out.v = quad.getTexV(srcIndex);

out.light = light.lm[srcIndex];

bounds.add(out.x, out.y, out.z, normalFace);
}

var vertexBuffer = builder.getVertexBuffer(normalFace);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
import me.jellysquid.mods.sodium.client.model.color.DefaultColorProviders;
import me.jellysquid.mods.sodium.client.render.chunk.compile.ChunkBuildBuffers;
import me.jellysquid.mods.sodium.client.render.chunk.compile.buffers.ChunkModelBuilder;
import me.jellysquid.mods.sodium.client.render.chunk.data.ChunkRenderBounds;
import me.jellysquid.mods.sodium.client.render.chunk.terrain.material.DefaultMaterials;
import me.jellysquid.mods.sodium.client.render.chunk.terrain.material.Material;
import me.jellysquid.mods.sodium.client.render.chunk.vertex.format.ChunkVertexEncoder;
Expand Down Expand Up @@ -102,7 +101,7 @@ private boolean isSideExposed(BlockRenderView world, int x, int y, int z, Direct
return true;
}

public void render(WorldSlice world, FluidState fluidState, BlockPos pos, BlockPos offset, ChunkBuildBuffers buffers, ChunkRenderBounds.Builder bounds) {
public void render(WorldSlice world, FluidState fluidState, BlockPos pos, BlockPos offset, ChunkBuildBuffers buffers) {
var material = DefaultMaterials.forFluidState(fluidState);
var meshBuilder = buffers.get(material);

Expand Down Expand Up @@ -220,10 +219,10 @@ public void render(WorldSlice world, FluidState fluidState, BlockPos pos, BlockP
setVertex(quad, 3, 1.0F, h4, 0.0f, u4, v4);

this.updateQuad(quad, world, pos, lighter, Direction.UP, 1.0F, colorProvider, fluidState);
this.writeQuad(meshBuilder, bounds, material, offset, quad, facing, false);
this.writeQuad(meshBuilder, material, offset, quad, facing, false);

if (fluidState.canFlowTo(world, this.scratchPos.set(posX, posY + 1, posZ))) {
this.writeQuad(meshBuilder, bounds, material, offset, quad,
this.writeQuad(meshBuilder, material, offset, quad,
ModelQuadFacing.DOWN, true);

}
Expand All @@ -245,7 +244,7 @@ public void render(WorldSlice world, FluidState fluidState, BlockPos pos, BlockP
setVertex(quad, 3, 1.0F, yOffset, 1.0F, maxU, maxV);

this.updateQuad(quad, world, pos, lighter, Direction.DOWN, 1.0F, colorProvider, fluidState);
this.writeQuad(meshBuilder, bounds, material, offset, quad, ModelQuadFacing.DOWN, false);
this.writeQuad(meshBuilder, material, offset, quad, ModelQuadFacing.DOWN, false);

}

Expand Down Expand Up @@ -346,10 +345,10 @@ public void render(WorldSlice world, FluidState fluidState, BlockPos pos, BlockP
ModelQuadFacing facing = ModelQuadFacing.fromDirection(dir);

this.updateQuad(quad, world, pos, lighter, dir, br, colorProvider, fluidState);
this.writeQuad(meshBuilder, bounds, material, offset, quad, facing, false);
this.writeQuad(meshBuilder, material, offset, quad, facing, false);

if (!isOverlay) {
this.writeQuad(meshBuilder, bounds, material, offset, quad, facing.getOpposite(), true);
this.writeQuad(meshBuilder, material, offset, quad, facing.getOpposite(), true);
}

}
Expand Down Expand Up @@ -391,7 +390,7 @@ private void updateQuad(ModelQuadView quad, WorldSlice world, BlockPos pos, Ligh
}
}

private void writeQuad(ChunkModelBuilder builder, ChunkRenderBounds.Builder bounds, Material material, BlockPos offset, ModelQuadView quad,
private void writeQuad(ChunkModelBuilder builder, Material material, BlockPos offset, ModelQuadView quad,
ModelQuadFacing facing, boolean flip) {
var vertices = this.vertices;

Expand All @@ -401,8 +400,6 @@ private void writeQuad(ChunkModelBuilder builder, ChunkRenderBounds.Builder boun
out.y = offset.getY() + quad.getY(i);
out.z = offset.getZ() + quad.getZ(i);

bounds.add(out.x, out.y, out.z, facing);

out.color = this.quadColors[i];
out.u = quad.getTexU(i);
out.v = quad.getTexV(i);
Expand Down
Loading

0 comments on commit 6bf02af

Please sign in to comment.