Skip to content

Commit e76ded5

Browse files
committed
Implement CPU backface culling
1 parent 3adda56 commit e76ded5

18 files changed

Lines changed: 554 additions & 334 deletions

src/main/java/net/vulkanmod/config/Config.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ public class Config {
2727

2828
public int ambientOcclusion = 1;
2929

30+
public boolean backFaceCulling = true;
31+
3032
public void write() {
3133

3234
if(!Files.exists(CONFIG_PATH.getParent())) {

src/main/java/net/vulkanmod/config/option/Options.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,12 @@ public static OptionBlock[] getOptimizationOpts() {
256256
},
257257
() -> config.uniqueOpaqueLayer)
258258
.setTooltip(Component.translatable("vulkanmod.options.uniqueOpaqueLayer.tooltip")),
259+
new SwitchOption(Component.translatable("Back face culling"),
260+
value -> {
261+
config.backFaceCulling = value;
262+
Minecraft.getInstance().levelRenderer.allChanged();
263+
},
264+
() -> config.backFaceCulling),
259265
new SwitchOption(Component.translatable("vulkanmod.options.indirectDraw"),
260266
value -> config.indirectDraw = value,
261267
() -> config.indirectDraw)

src/main/java/net/vulkanmod/render/chunk/RenderSection.java

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import net.vulkanmod.render.chunk.build.task.ChunkTask;
1515
import net.vulkanmod.render.chunk.build.task.CompiledSection;
1616
import net.vulkanmod.render.chunk.build.task.SortTransparencyTask;
17+
import net.vulkanmod.render.chunk.cull.QuadFacing;
1718
import net.vulkanmod.render.chunk.graph.GraphDirections;
1819
import net.vulkanmod.render.chunk.util.Util;
1920
import net.vulkanmod.render.vertex.TerrainRenderType;
@@ -61,8 +62,9 @@ public RenderSection(int index, int x, int y, int z) {
6162
this.yOffset = y;
6263
this.zOffset = z;
6364

64-
this.drawParametersArray = new DrawBuffers.DrawParameters[TerrainRenderType.VALUES.length];
65-
for (int i = 0; i < this.drawParametersArray.length; ++i) {
65+
final int size = TerrainRenderType.VALUES.length * QuadFacing.VALUES.length;
66+
this.drawParametersArray = new DrawBuffers.DrawParameters[size];
67+
for (int i = 0; i < size; ++i) {
6668
this.drawParametersArray[i] = new DrawBuffers.DrawParameters();
6769
}
6870
}
@@ -297,8 +299,14 @@ public int zOffset() {
297299
return zOffset;
298300
}
299301

300-
public DrawBuffers.DrawParameters getDrawParameters(TerrainRenderType renderType) {
301-
return drawParametersArray[renderType.ordinal()];
302+
public void resetDrawParameters(TerrainRenderType renderType) {
303+
for (int i = 0; i < QuadFacing.COUNT; ++i) {
304+
drawParametersArray[renderType.ordinal() * QuadFacing.COUNT + i].reset(this.chunkArea, renderType);
305+
}
306+
}
307+
308+
public DrawBuffers.DrawParameters getDrawParameters(TerrainRenderType renderType, int facing) {
309+
return drawParametersArray[renderType.ordinal() * QuadFacing.COUNT + facing];
302310
}
303311

304312
public void setChunkArea(ChunkArea chunkArea) {
@@ -385,8 +393,10 @@ private void resetDrawParameters() {
385393
if (this.chunkArea == null)
386394
return;
387395

388-
for (TerrainRenderType r : TerrainRenderType.VALUES) {
389-
this.getDrawParameters(r).reset(this.chunkArea, r);
396+
for (TerrainRenderType renderType : TerrainRenderType.VALUES) {
397+
for (QuadFacing facing : QuadFacing.VALUES) {
398+
this.getDrawParameters(renderType, facing.ordinal()).reset(this.chunkArea, renderType);
399+
}
390400
}
391401
}
392402

src/main/java/net/vulkanmod/render/chunk/WorldRenderer.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -338,9 +338,9 @@ public void renderSectionLayer(RenderType renderType, double camX, double camY,
338338
renderer.uploadAndBindUBOs(pipeline);
339339

340340
if (indirectDraw)
341-
drawBuffers.buildDrawBatchesIndirect(indirectBuffers[currentFrame], queue, terrainRenderType);
341+
drawBuffers.buildDrawBatchesIndirect(cameraPos, indirectBuffers[currentFrame], queue, terrainRenderType);
342342
else
343-
drawBuffers.buildDrawBatchesDirect(queue, terrainRenderType);
343+
drawBuffers.buildDrawBatchesDirect(cameraPos, queue, terrainRenderType);
344344
}
345345
}
346346
}

src/main/java/net/vulkanmod/render/chunk/buffer/DrawBuffers.java

Lines changed: 96 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
package net.vulkanmod.render.chunk.buffer;
22

3+
import net.minecraft.world.phys.Vec3;
4+
import net.vulkanmod.Initializer;
35
import net.vulkanmod.render.PipelineManager;
46
import net.vulkanmod.render.chunk.ChunkArea;
57
import net.vulkanmod.render.chunk.RenderSection;
68
import net.vulkanmod.render.chunk.build.UploadBuffer;
9+
import net.vulkanmod.render.chunk.cull.QuadFacing;
710
import net.vulkanmod.render.chunk.util.StaticQueue;
811
import net.vulkanmod.render.vertex.CustomVertexFormat;
912
import net.vulkanmod.render.vertex.TerrainRenderType;
@@ -39,28 +42,49 @@ public DrawBuffers(int index, Vector3i origin, int minHeight) {
3942
}
4043

4144
public void upload(RenderSection section, UploadBuffer buffer, TerrainRenderType renderType) {
42-
DrawParameters drawParameters = section.getDrawParameters(renderType);
43-
int vertexOffset = drawParameters.vertexOffset;
44-
int firstIndex = -1;
45+
var vertexBuffers = buffer.getVertexBuffers();
46+
47+
if (buffer.indexOnly) {
48+
DrawParameters drawParameters = section.getDrawParameters(renderType, QuadFacing.NONE.ordinal());
4549

46-
if (!buffer.indexOnly) {
47-
AreaBuffer.Segment segment = this.getAreaBufferOrAlloc(renderType).upload(buffer.getVertexBuffer(), vertexOffset, drawParameters);
48-
vertexOffset = segment.offset / VERTEX_SIZE;
50+
AreaBuffer.Segment segment = this.indexBuffer.upload(buffer.getIndexBuffer(), drawParameters.firstIndex, drawParameters);
51+
drawParameters.firstIndex = segment.offset / INDEX_SIZE;
4952

50-
drawParameters.baseInstance = encodeSectionOffset(section.xOffset(), section.yOffset(), section.zOffset());
53+
buffer.release();
54+
return;
5155
}
5256

53-
if (!buffer.autoIndices) {
54-
if (this.indexBuffer == null)
57+
for (int i = 0; i < QuadFacing.COUNT; i++) {
58+
DrawParameters drawParameters = section.getDrawParameters(renderType, i);
59+
int vertexOffset = drawParameters.vertexOffset;
60+
int firstIndex = -1;
61+
int indexCount = 0;
62+
63+
var vertexBuffer = vertexBuffers[i];
64+
65+
if (vertexBuffer != null) {
66+
AreaBuffer.Segment segment = this.getAreaBufferOrAlloc(renderType).upload(vertexBuffer, vertexOffset, drawParameters);
67+
vertexOffset = segment.offset / VERTEX_SIZE;
68+
69+
drawParameters.baseInstance = encodeSectionOffset(section.xOffset(), section.yOffset(), section.zOffset());
70+
indexCount = vertexBuffer.limit() / VERTEX_SIZE * 6 / 4;
71+
}
72+
73+
if (i == QuadFacing.NONE.ordinal() && !buffer.autoIndices) {
74+
if (this.indexBuffer == null) {
5575
this.indexBuffer = new AreaBuffer(AreaBuffer.Usage.INDEX, 60000, INDEX_SIZE);
76+
}
5677

57-
AreaBuffer.Segment segment = this.indexBuffer.upload(buffer.getIndexBuffer(), drawParameters.firstIndex, drawParameters);
58-
firstIndex = segment.offset / INDEX_SIZE;
59-
}
78+
AreaBuffer.Segment segment = this.indexBuffer.upload(buffer.getIndexBuffer(), drawParameters.firstIndex, drawParameters);
79+
firstIndex = segment.offset / INDEX_SIZE;
80+
}
6081

61-
drawParameters.indexCount = buffer.indexCount;
62-
drawParameters.firstIndex = firstIndex;
63-
drawParameters.vertexOffset = vertexOffset;
82+
// drawParameters.indexCount = buffer.indexCount;
83+
drawParameters.firstIndex = firstIndex;
84+
drawParameters.vertexOffset = vertexOffset;
85+
86+
drawParameters.indexCount = indexCount;
87+
}
6488

6589
buffer.release();
6690
}
@@ -110,11 +134,11 @@ private void updateChunkAreaOrigin(VkCommandBuffer commandBuffer, Pipeline pipel
110134
vkCmdPushConstants(commandBuffer, pipeline.getLayout(), VK_SHADER_STAGE_VERTEX_BIT, 0, byteBuffer);
111135
}
112136

113-
public void buildDrawBatchesIndirect(IndirectBuffer indirectBuffer, StaticQueue<RenderSection> queue, TerrainRenderType terrainRenderType) {
137+
public void buildDrawBatchesIndirect(Vec3 cameraPos, IndirectBuffer indirectBuffer, StaticQueue<RenderSection> queue, TerrainRenderType terrainRenderType) {
114138

115139
try (MemoryStack stack = MemoryStack.stackPush()) {
116140

117-
ByteBuffer byteBuffer = stack.malloc(20 * queue.size());
141+
ByteBuffer byteBuffer = stack.malloc(20 * queue.size() * 7);
118142
long bufferPtr = MemoryUtil.memAddress0(byteBuffer);
119143

120144
boolean isTranslucent = terrainRenderType == TerrainRenderType.TRANSLUCENT;
@@ -123,19 +147,28 @@ public void buildDrawBatchesIndirect(IndirectBuffer indirectBuffer, StaticQueue<
123147
for (var iterator = queue.iterator(isTranslucent); iterator.hasNext(); ) {
124148

125149
final RenderSection section = iterator.next();
126-
final DrawParameters drawParameters = section.getDrawParameters(terrainRenderType);
127150

128-
if (drawParameters.indexCount <= 0)
129-
continue;
151+
int mask = getMask(cameraPos, section);
152+
153+
for (int i = 0; i < QuadFacing.COUNT; i++) {
130154

131-
long ptr = bufferPtr + (drawCount * 20L);
132-
MemoryUtil.memPutInt(ptr, drawParameters.indexCount);
133-
MemoryUtil.memPutInt(ptr + 4, 1);
134-
MemoryUtil.memPutInt(ptr + 8, drawParameters.firstIndex == -1 ? 0 : drawParameters.firstIndex);
135-
MemoryUtil.memPutInt(ptr + 12, drawParameters.vertexOffset);
136-
MemoryUtil.memPutInt(ptr + 16, drawParameters.baseInstance);
155+
if((mask & 1 << i) == 0)
156+
continue;
137157

138-
drawCount++;
158+
final DrawParameters drawParameters = section.getDrawParameters(terrainRenderType, i);
159+
160+
if (drawParameters.indexCount <= 0)
161+
continue;
162+
163+
long ptr = bufferPtr + (drawCount * 20L);
164+
MemoryUtil.memPutInt(ptr, drawParameters.indexCount);
165+
MemoryUtil.memPutInt(ptr + 4, 1);
166+
MemoryUtil.memPutInt(ptr + 8, drawParameters.firstIndex == -1 ? 0 : drawParameters.firstIndex);
167+
MemoryUtil.memPutInt(ptr + 12, drawParameters.vertexOffset);
168+
MemoryUtil.memPutInt(ptr + 16, drawParameters.baseInstance);
169+
170+
drawCount++;
171+
}
139172
}
140173

141174
if (drawCount == 0) return;
@@ -149,22 +182,50 @@ public void buildDrawBatchesIndirect(IndirectBuffer indirectBuffer, StaticQueue<
149182

150183
}
151184

152-
public void buildDrawBatchesDirect(StaticQueue<RenderSection> queue, TerrainRenderType renderType) {
185+
public void buildDrawBatchesDirect(Vec3 cameraPos, StaticQueue<RenderSection> queue, TerrainRenderType renderType) {
153186
boolean isTranslucent = renderType == TerrainRenderType.TRANSLUCENT;
154187
VkCommandBuffer commandBuffer = Renderer.getCommandBuffer();
155188

156189
for (var iterator = queue.iterator(isTranslucent); iterator.hasNext(); ) {
157190
final RenderSection section = iterator.next();
158-
final DrawParameters drawParameters = section.getDrawParameters(renderType);
159191

160-
if (drawParameters.indexCount <= 0)
161-
continue;
192+
int mask = getMask(cameraPos, section);
193+
194+
for (int i = 0; i < QuadFacing.COUNT; i++) {
195+
196+
if((mask & 1 << i) == 0)
197+
continue;
198+
199+
final DrawParameters drawParameters = section.getDrawParameters(renderType, i);
200+
201+
if (drawParameters.indexCount <= 0)
202+
continue;
203+
204+
final int firstIndex = drawParameters.firstIndex == -1 ? 0 : drawParameters.firstIndex;
205+
vkCmdDrawIndexed(commandBuffer, drawParameters.indexCount, 1, firstIndex, drawParameters.vertexOffset, drawParameters.baseInstance);
206+
}
162207

163-
final int firstIndex = drawParameters.firstIndex == -1 ? 0 : drawParameters.firstIndex;
164-
vkCmdDrawIndexed(commandBuffer, drawParameters.indexCount, 1, firstIndex, drawParameters.vertexOffset, drawParameters.baseInstance);
165208
}
166209
}
167210

211+
private int getMask(Vec3 camera, RenderSection section) {
212+
final int secX = section.xOffset;
213+
final int secY = section.yOffset;
214+
final int secZ = section.zOffset;
215+
216+
int mask = 1 << QuadFacing.NONE.ordinal();
217+
218+
mask |= camera.x - secX >= 0 ? 1 << QuadFacing.X_POS.ordinal() : 0;
219+
mask |= camera.y - secY >= 0 ? 1 << QuadFacing.Y_POS.ordinal() : 0;
220+
mask |= camera.z - secZ >= 0 ? 1 << QuadFacing.Z_POS.ordinal() : 0;
221+
mask |= camera.x - (secX + 16) < 0 ? 1 << QuadFacing.X_NEG.ordinal() : 0;
222+
mask |= camera.y - (secY + 16) < 0 ? 1 << QuadFacing.Y_NEG.ordinal() : 0;
223+
mask |= camera.z - (secZ + 16) < 0 ? 1 << QuadFacing.Z_NEG.ordinal() : 0;
224+
225+
return mask;
226+
// return 0xFF;
227+
}
228+
168229
public void bindBuffers(VkCommandBuffer commandBuffer, Pipeline pipeline, TerrainRenderType terrainRenderType, double camX, double camY, double camZ) {
169230

170231
try (MemoryStack stack = MemoryStack.stackPush()) {
@@ -173,7 +234,8 @@ public void bindBuffers(VkCommandBuffer commandBuffer, Pipeline pipeline, Terrai
173234
updateChunkAreaOrigin(commandBuffer, pipeline, camX, camY, camZ, stack);
174235
}
175236

176-
if (terrainRenderType == TerrainRenderType.TRANSLUCENT) {
237+
// TODO index buffer
238+
if (terrainRenderType == TerrainRenderType.TRANSLUCENT && this.indexBuffer != null) {
177239
vkCmdBindIndexBuffer(commandBuffer, this.indexBuffer.getId(), 0, VK_INDEX_TYPE_UINT16);
178240
}
179241

src/main/java/net/vulkanmod/render/chunk/build/BlockRenderer.java

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import it.unimi.dsi.fastutil.objects.Object2ByteLinkedOpenHashMap;
44
import net.minecraft.client.Minecraft;
55
import net.minecraft.client.color.block.BlockColors;
6+
import net.minecraft.client.renderer.RenderType;
67
import net.minecraft.client.renderer.block.model.BakedQuad;
78
import net.minecraft.client.resources.model.BakedModel;
89
import net.minecraft.core.BlockPos;
@@ -16,12 +17,16 @@
1617
import net.minecraft.world.phys.shapes.BooleanOp;
1718
import net.minecraft.world.phys.shapes.Shapes;
1819
import net.minecraft.world.phys.shapes.VoxelShape;
20+
import net.vulkanmod.Initializer;
1921
import net.vulkanmod.render.chunk.build.light.LightPipeline;
2022
import net.vulkanmod.render.chunk.build.light.data.QuadLightData;
2123
import net.vulkanmod.render.chunk.build.thread.BuilderResources;
24+
import net.vulkanmod.render.chunk.cull.QuadFacing;
2225
import net.vulkanmod.render.model.quad.QuadUtils;
2326
import net.vulkanmod.render.model.quad.QuadView;
2427
import net.vulkanmod.render.vertex.TerrainBufferBuilder;
28+
import net.vulkanmod.render.vertex.TerrainBuilder;
29+
import net.vulkanmod.render.vertex.TerrainRenderType;
2530
import net.vulkanmod.render.vertex.VertexUtil;
2631
import net.vulkanmod.vulkan.util.ColorUtil;
2732
import org.joml.Vector3f;
@@ -43,6 +48,10 @@ public class BlockRenderer {
4348

4449
BlockState blockState;
4550

51+
final boolean backFaceCulling = Initializer.CONFIG.backFaceCulling;
52+
53+
private TerrainRenderType renderType;
54+
4655
public void setResources(BuilderResources resources) {
4756
this.resources = resources;
4857
}
@@ -60,18 +69,19 @@ public static void setBlockColors(BlockColors blockColors) {
6069
BlockRenderer.blockColors = blockColors;
6170
}
6271

63-
public void renderBlock(BlockState blockState, BlockPos blockPos, Vector3f pos, TerrainBufferBuilder bufferBuilder) {
72+
public void renderBlock(BlockState blockState, BlockPos blockPos, TerrainRenderType renderType, Vector3f pos, TerrainBuilder bufferBuilder) {
6473
this.pos = pos;
6574
this.blockPos = blockPos;
6675
this.blockState = blockState;
76+
this.renderType = renderType;
6777

6878
long seed = blockState.getSeed(blockPos);
6979

7080
BakedModel model = Minecraft.getInstance().getBlockRenderer().getBlockModel(blockState);
7181
tessellateBlock(model, bufferBuilder, seed);
7282
}
7383

74-
public void tessellateBlock(BakedModel bakedModel, TerrainBufferBuilder bufferBuilder, long seed) {
84+
public void tessellateBlock(BakedModel bakedModel, TerrainBuilder bufferBuilder, long seed) {
7585
Vec3 offset = blockState.getOffset(resources.region, blockPos);
7686

7787
pos.add((float) offset.x, (float) offset.y, (float) offset.z);
@@ -101,9 +111,18 @@ public void tessellateBlock(BakedModel bakedModel, TerrainBufferBuilder bufferBu
101111
}
102112
}
103113

104-
private void renderModelFace(TerrainBufferBuilder bufferBuilder, List<BakedQuad> quads, LightPipeline lightPipeline, Direction cullFace) {
114+
private void renderModelFace(TerrainBuilder terrainBuilder, List<BakedQuad> quads, LightPipeline lightPipeline, Direction cullFace) {
105115
QuadLightData quadLightData = resources.quadLightData;
106116

117+
TerrainBufferBuilder bufferBuilder;
118+
119+
if (cullFace != null && renderType != TerrainRenderType.TRANSLUCENT && this.backFaceCulling) {
120+
bufferBuilder = terrainBuilder.getBufferBuilder(QuadFacing.from(cullFace).ordinal());
121+
}
122+
else {
123+
bufferBuilder = terrainBuilder.getBufferBuilder(QuadFacing.NONE.ordinal());
124+
}
125+
107126
for (int i = 0; i < quads.size(); ++i) {
108127
BakedQuad bakedQuad = quads.get(i);
109128
QuadView quadView = (QuadView) bakedQuad;

0 commit comments

Comments
 (0)