From 96eb5ea47c44fc072a138efda1f6e646e2804d05 Mon Sep 17 00:00:00 2001 From: Jozufozu Date: Tue, 26 Mar 2024 12:06:20 -0700 Subject: [PATCH] Less omissions in mesh emission - Cannibalize shade separated vertex consumer to function as the mesh emitter for baked model buffering - Create one MeshEmitter per chunk RenderType to allow more coherency during multi block buffering - MeshEmitters own BufferBuilders, and the buffering functions call #begin on the mesh emitters directly - Each time the emitter sees a quad with a different shade flag than the previous run of quads, it emits a mesh and begins again - Finally, the buffering functions call #end on the mesh emitters to emit trailing meshes and clear transient state - Add diffuse debug mode - Order indirect draws by mesh index first - Remove non-shade separated option from buffering utilities because it allows you to opt in to incorrect behaviour - Remove TessellatedModel - Document mesh ordering contract in Model --- .../jozufozu/flywheel/api/model/Model.java | 10 + .../engine/indirect/IndirectCullingGroup.java | 16 +- .../backend/engine/indirect/IndirectDraw.java | 8 +- .../jozufozu/flywheel/config/DebugMode.java | 1 + .../lib/model/baked/BakedModelBufferer.java | 194 ++---------------- .../lib/model/baked/BakedModelBuilder.java | 52 ++--- .../lib/model/baked/BlockModelBuilder.java | 51 ++--- .../flywheel/lib/model/baked/MeshEmitter.java | 122 +++++++++++ .../model/baked/MultiBlockModelBuilder.java | 50 ++--- .../baked/ShadeSeparatingVertexConsumer.java | 84 -------- .../lib/model/baked/TessellatedModel.java | 17 -- .../flywheel/flywheel/internal/common.frag | 28 ++- 12 files changed, 246 insertions(+), 387 deletions(-) create mode 100644 src/main/java/com/jozufozu/flywheel/lib/model/baked/MeshEmitter.java delete mode 100644 src/main/java/com/jozufozu/flywheel/lib/model/baked/ShadeSeparatingVertexConsumer.java delete mode 100644 src/main/java/com/jozufozu/flywheel/lib/model/baked/TessellatedModel.java diff --git a/src/main/java/com/jozufozu/flywheel/api/model/Model.java b/src/main/java/com/jozufozu/flywheel/api/model/Model.java index 42fb8a2cf..ad76b132d 100644 --- a/src/main/java/com/jozufozu/flywheel/api/model/Model.java +++ b/src/main/java/com/jozufozu/flywheel/api/model/Model.java @@ -7,6 +7,16 @@ import org.joml.Vector4fc; import com.jozufozu.flywheel.api.material.Material; public interface Model { + /** + * Get a list of all meshes in this model. + * + *

The contents of the returned list will be queried, but never modified.

+ * + *

Meshes will be rendered in the order they appear in this list, though + * no render order guarantees are made for meshes between different models.

+ * + * @return A list of meshes. + */ List meshes(); /** diff --git a/src/main/java/com/jozufozu/flywheel/backend/engine/indirect/IndirectCullingGroup.java b/src/main/java/com/jozufozu/flywheel/backend/engine/indirect/IndirectCullingGroup.java index 986b7637c..9c2347d8b 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/engine/indirect/IndirectCullingGroup.java +++ b/src/main/java/com/jozufozu/flywheel/backend/engine/indirect/IndirectCullingGroup.java @@ -33,6 +33,7 @@ import com.jozufozu.flywheel.backend.gl.shader.GlProgram; public class IndirectCullingGroup { private static final Comparator DRAW_COMPARATOR = Comparator.comparing(IndirectDraw::stage) + .thenComparing(IndirectDraw::indexOfMeshInModel) .thenComparing(IndirectDraw::material, MaterialRenderState.COMPARATOR); private static final int DRAW_BARRIER_BITS = GL_SHADER_STORAGE_BARRIER_BIT | GL_COMMAND_BARRIER_BIT; @@ -179,12 +180,15 @@ public class IndirectCullingGroup { instancer.index = instancers.size(); instancers.add(instancer); - for (var entry : model.meshes()) { - MeshPool.PooledMesh mesh = meshPool.alloc(entry.mesh()); - var draw = new IndirectDraw(instancer, entry.material(), mesh, stage); - indirectDraws.add(draw); - instancer.addDraw(draw); - } + List meshes = model.meshes(); + for (int i = 0; i < meshes.size(); i++) { + var entry = meshes.get(i); + + MeshPool.PooledMesh mesh = meshPool.alloc(entry.mesh()); + var draw = new IndirectDraw(instancer, entry.material(), mesh, stage, i); + indirectDraws.add(draw); + instancer.addDraw(draw); + } needsDrawSort = true; } diff --git a/src/main/java/com/jozufozu/flywheel/backend/engine/indirect/IndirectDraw.java b/src/main/java/com/jozufozu/flywheel/backend/engine/indirect/IndirectDraw.java index 84b2ccd24..77e1111b4 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/engine/indirect/IndirectDraw.java +++ b/src/main/java/com/jozufozu/flywheel/backend/engine/indirect/IndirectDraw.java @@ -13,6 +13,7 @@ public class IndirectDraw { private final Material material; private final MeshPool.PooledMesh mesh; private final RenderStage stage; + private final int indexOfMeshInModel; private final int materialVertexIndex; private final int materialFragmentIndex; @@ -20,11 +21,12 @@ public class IndirectDraw { private final int packedMaterialProperties; private boolean deleted; - public IndirectDraw(IndirectInstancer model, Material material, MeshPool.PooledMesh mesh, RenderStage stage) { + public IndirectDraw(IndirectInstancer model, Material material, MeshPool.PooledMesh mesh, RenderStage stage, int indexOfMeshInModel) { this.model = model; this.material = material; this.mesh = mesh; this.stage = stage; + this.indexOfMeshInModel = indexOfMeshInModel; mesh.acquire(); @@ -50,6 +52,10 @@ public class IndirectDraw { return stage; } + public int indexOfMeshInModel() { + return indexOfMeshInModel; + } + public void write(long ptr) { MemoryUtil.memPutInt(ptr, mesh.indexCount()); // count MemoryUtil.memPutInt(ptr + 4, 0); // instanceCount - to be set by the apply shader diff --git a/src/main/java/com/jozufozu/flywheel/config/DebugMode.java b/src/main/java/com/jozufozu/flywheel/config/DebugMode.java index 4148aeb97..d8cbdbf50 100644 --- a/src/main/java/com/jozufozu/flywheel/config/DebugMode.java +++ b/src/main/java/com/jozufozu/flywheel/config/DebugMode.java @@ -7,5 +7,6 @@ public enum DebugMode { LIGHT_LEVEL, LIGHT_COLOR, OVERLAY, + DIFFUSE, LIGHT_VOLUME, } diff --git a/src/main/java/com/jozufozu/flywheel/lib/model/baked/BakedModelBufferer.java b/src/main/java/com/jozufozu/flywheel/lib/model/baked/BakedModelBufferer.java index 0820471d4..148a10d12 100644 --- a/src/main/java/com/jozufozu/flywheel/lib/model/baked/BakedModelBufferer.java +++ b/src/main/java/com/jozufozu/flywheel/lib/model/baked/BakedModelBufferer.java @@ -7,9 +7,7 @@ import org.jetbrains.annotations.Nullable; import com.mojang.blaze3d.vertex.BufferBuilder; import com.mojang.blaze3d.vertex.BufferBuilder.RenderedBuffer; -import com.mojang.blaze3d.vertex.DefaultVertexFormat; import com.mojang.blaze3d.vertex.PoseStack; -import com.mojang.blaze3d.vertex.VertexFormat; import net.minecraft.client.renderer.ItemBlockRenderTypes; import net.minecraft.client.renderer.RenderType; @@ -41,7 +39,7 @@ final class BakedModelBufferer { poseStack = objects.identityPoseStack; } RandomSource random = objects.random; - BufferBuilder[] buffers = objects.shadedBuffers; + var consumers = objects.emitters; modelData = model.getModelData(renderWorld, BlockPos.ZERO, state, modelData); random.setSeed(42L); @@ -49,64 +47,18 @@ final class BakedModelBufferer { for (RenderType renderType : renderTypes) { int layerIndex = renderType.getChunkLayerId(); + var consumer = consumers[layerIndex]; - BufferBuilder buffer = buffers[layerIndex]; - buffer.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.BLOCK); + consumer.begin(resultConsumer); poseStack.pushPose(); - blockRenderer.tesselateBlock(renderWorld, model, state, BlockPos.ZERO, poseStack, buffer, false, random, 42L, OverlayTexture.NO_OVERLAY, modelData, renderType); + blockRenderer.tesselateBlock(renderWorld, model, state, BlockPos.ZERO, poseStack, consumer, false, random, 42L, OverlayTexture.NO_OVERLAY, modelData, renderType); poseStack.popPose(); - RenderedBuffer data = buffer.endOrDiscardIfEmpty(); - if (data != null) { - resultConsumer.accept(renderType, data); - data.release(); - } + consumer.end(); } } - public static void bufferSingleShadeSeparated(ModelBlockRenderer blockRenderer, BlockAndTintGetter renderWorld, BakedModel model, BlockState state, @Nullable PoseStack poseStack, ModelData modelData, ShadeSeparatedResultConsumer resultConsumer) { - ThreadLocalObjects objects = THREAD_LOCAL_OBJECTS.get(); - if (poseStack == null) { - poseStack = objects.identityPoseStack; - } - RandomSource random = objects.random; - ShadeSeparatingVertexConsumer shadeSeparatingWrapper = objects.shadeSeparatingWrapper; - BufferBuilder[] shadedBuffers = objects.shadedBuffers; - BufferBuilder[] unshadedBuffers = objects.unshadedBuffers; - - modelData = model.getModelData(renderWorld, BlockPos.ZERO, state, modelData); - random.setSeed(42L); - ChunkRenderTypeSet renderTypes = model.getRenderTypes(state, random, modelData); - - for (RenderType renderType : renderTypes) { - int layerIndex = renderType.getChunkLayerId(); - - BufferBuilder shadedBuffer = shadedBuffers[layerIndex]; - BufferBuilder unshadedBuffer = unshadedBuffers[layerIndex]; - shadeSeparatingWrapper.prepare(shadedBuffer, unshadedBuffer); - shadedBuffer.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.BLOCK); - unshadedBuffer.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.BLOCK); - - poseStack.pushPose(); - blockRenderer.tesselateBlock(renderWorld, model, state, BlockPos.ZERO, poseStack, shadeSeparatingWrapper, false, random, 42L, OverlayTexture.NO_OVERLAY, modelData, renderType); - poseStack.popPose(); - - RenderedBuffer shadedData = shadedBuffer.endOrDiscardIfEmpty(); - if (shadedData != null) { - resultConsumer.accept(renderType, true, shadedData); - shadedData.release(); - } - RenderedBuffer unshadedData = unshadedBuffer.endOrDiscardIfEmpty(); - if (unshadedData != null) { - resultConsumer.accept(renderType, false, unshadedData); - unshadedData.release(); - } - } - - shadeSeparatingWrapper.clear(); - } - public static void bufferBlock(BlockRenderDispatcher renderDispatcher, BlockAndTintGetter renderWorld, BlockState state, @Nullable PoseStack poseStack, ModelData modelData, ResultConsumer resultConsumer) { if (state.getRenderShape() != RenderShape.MODEL) { return; @@ -115,14 +67,6 @@ final class BakedModelBufferer { bufferSingle(renderDispatcher.getModelRenderer(), renderWorld, renderDispatcher.getBlockModel(state), state, poseStack, modelData, resultConsumer); } - public static void bufferBlockShadeSeparated(BlockRenderDispatcher renderDispatcher, BlockAndTintGetter renderWorld, BlockState state, @Nullable PoseStack poseStack, ModelData modelData, ShadeSeparatedResultConsumer resultConsumer) { - if (state.getRenderShape() != RenderShape.MODEL) { - return; - } - - bufferSingleShadeSeparated(renderDispatcher.getModelRenderer(), renderWorld, renderDispatcher.getBlockModel(state), state, poseStack, modelData, resultConsumer); - } - public static void bufferMultiBlock(BlockRenderDispatcher renderDispatcher, Iterator posIterator, BlockAndTintGetter renderWorld, @Nullable PoseStack poseStack, Function modelDataLookup, boolean renderFluids, ResultConsumer resultConsumer) { ThreadLocalObjects objects = THREAD_LOCAL_OBJECTS.get(); if (poseStack == null) { @@ -131,9 +75,10 @@ final class BakedModelBufferer { RandomSource random = objects.random; TransformingVertexConsumer transformingWrapper = objects.transformingWrapper; - BufferBuilder[] buffers = objects.shadedBuffers; - for (BufferBuilder buffer : buffers) { - buffer.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.BLOCK); + var emitters = objects.emitters; + + for (var emitter : emitters) { + emitter.begin(resultConsumer); } ModelBlockRenderer blockRenderer = renderDispatcher.getModelRenderer(); @@ -145,89 +90,12 @@ final class BakedModelBufferer { if (renderFluids) { FluidState fluidState = state.getFluidState(); - + if (!fluidState.isEmpty()) { RenderType layer = ItemBlockRenderTypes.getRenderLayer(fluidState); int layerIndex = layer.getChunkLayerId(); - - transformingWrapper.prepare(buffers[layerIndex], poseStack); - poseStack.pushPose(); - poseStack.translate(pos.getX() - (pos.getX() & 0xF), pos.getY() - (pos.getY() & 0xF), pos.getZ() - (pos.getZ() & 0xF)); - renderDispatcher.renderLiquid(pos, renderWorld, transformingWrapper, state, fluidState); - poseStack.popPose(); - } - } - - if (state.getRenderShape() == RenderShape.MODEL) { - long seed = state.getSeed(pos); - BakedModel model = renderDispatcher.getBlockModel(state); - ModelData modelData = modelDataLookup.apply(pos); - modelData = model.getModelData(renderWorld, pos, state, modelData); - random.setSeed(seed); - ChunkRenderTypeSet renderTypes = model.getRenderTypes(state, random, modelData); - - for (RenderType renderType : renderTypes) { - int layerIndex = renderType.getChunkLayerId(); - - BufferBuilder buffer = buffers[layerIndex]; - - poseStack.pushPose(); - poseStack.translate(pos.getX(), pos.getY(), pos.getZ()); - blockRenderer.tesselateBlock(renderWorld, model, state, pos, poseStack, buffer, true, random, seed, OverlayTexture.NO_OVERLAY, modelData, renderType); - poseStack.popPose(); - } - } - } - - ModelBlockRenderer.clearCache(); - - transformingWrapper.clear(); - - for (int layerIndex = 0; layerIndex < CHUNK_LAYER_AMOUNT; layerIndex++) { - RenderType renderType = CHUNK_LAYERS[layerIndex]; - BufferBuilder buffer = buffers[layerIndex]; - RenderedBuffer data = buffer.endOrDiscardIfEmpty(); - if (data != null) { - resultConsumer.accept(renderType, data); - data.release(); - } - } - } - - public static void bufferMultiBlockShadeSeparated(BlockRenderDispatcher renderDispatcher, Iterator posIterator, BlockAndTintGetter renderWorld, @Nullable PoseStack poseStack, Function modelDataLookup, boolean renderFluids, ShadeSeparatedResultConsumer resultConsumer) { - ThreadLocalObjects objects = THREAD_LOCAL_OBJECTS.get(); - if (poseStack == null) { - poseStack = objects.identityPoseStack; - } - RandomSource random = objects.random; - ShadeSeparatingVertexConsumer shadeSeparatingWrapper = objects.shadeSeparatingWrapper; - TransformingVertexConsumer transformingWrapper = objects.transformingWrapper; - - BufferBuilder[] shadedBuffers = objects.shadedBuffers; - BufferBuilder[] unshadedBuffers = objects.unshadedBuffers; - for (BufferBuilder buffer : shadedBuffers) { - buffer.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.BLOCK); - } - for (BufferBuilder buffer : unshadedBuffers) { - buffer.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.BLOCK); - } - - ModelBlockRenderer blockRenderer = renderDispatcher.getModelRenderer(); - ModelBlockRenderer.enableCaching(); - - while (posIterator.hasNext()) { - BlockPos pos = posIterator.next(); - BlockState state = renderWorld.getBlockState(pos); - - if (renderFluids) { - FluidState fluidState = state.getFluidState(); - - if (!fluidState.isEmpty()) { - RenderType layer = ItemBlockRenderTypes.getRenderLayer(fluidState); - int layerIndex = layer.getChunkLayerId(); - - transformingWrapper.prepare(shadedBuffers[layerIndex], poseStack); + transformingWrapper.prepare(emitters[layerIndex], poseStack); poseStack.pushPose(); poseStack.translate(pos.getX() - (pos.getX() & 0xF), pos.getY() - (pos.getY() & 0xF), pos.getZ() - (pos.getZ() & 0xF)); @@ -247,11 +115,9 @@ final class BakedModelBufferer { for (RenderType renderType : renderTypes) { int layerIndex = renderType.getChunkLayerId(); - shadeSeparatingWrapper.prepare(shadedBuffers[layerIndex], unshadedBuffers[layerIndex]); - poseStack.pushPose(); poseStack.translate(pos.getX(), pos.getY(), pos.getZ()); - blockRenderer.tesselateBlock(renderWorld, model, state, pos, poseStack, shadeSeparatingWrapper, true, random, seed, OverlayTexture.NO_OVERLAY, modelData, renderType); + blockRenderer.tesselateBlock(renderWorld, model, state, pos, poseStack, emitters[layerIndex], true, random, seed, OverlayTexture.NO_OVERLAY, modelData, renderType); poseStack.popPose(); } } @@ -259,31 +125,14 @@ final class BakedModelBufferer { ModelBlockRenderer.clearCache(); - shadeSeparatingWrapper.clear(); - transformingWrapper.clear(); - - for (int layerIndex = 0; layerIndex < CHUNK_LAYER_AMOUNT; layerIndex++) { - RenderType renderType = CHUNK_LAYERS[layerIndex]; - BufferBuilder shadedBuffer = shadedBuffers[layerIndex]; - BufferBuilder unshadedBuffer = unshadedBuffers[layerIndex]; - RenderedBuffer shadedData = shadedBuffer.endOrDiscardIfEmpty(); - if (shadedData != null) { - resultConsumer.accept(renderType, true, shadedData); - shadedData.release(); - } - RenderedBuffer unshadedData = unshadedBuffer.endOrDiscardIfEmpty(); - if (unshadedData != null) { - resultConsumer.accept(renderType, false, unshadedData); - unshadedData.release(); - } + for (var emitter : emitters) { + emitter.end(); } + + transformingWrapper.clear(); } public interface ResultConsumer { - void accept(RenderType renderType, RenderedBuffer data); - } - - public interface ShadeSeparatedResultConsumer { void accept(RenderType renderType, boolean shaded, RenderedBuffer data); } @@ -291,17 +140,16 @@ final class BakedModelBufferer { public final PoseStack identityPoseStack = new PoseStack(); public final RandomSource random = RandomSource.createNewThreadLocalInstance(); - public final ShadeSeparatingVertexConsumer shadeSeparatingWrapper = new ShadeSeparatingVertexConsumer(); public final TransformingVertexConsumer transformingWrapper = new TransformingVertexConsumer(); - public final BufferBuilder[] shadedBuffers = new BufferBuilder[CHUNK_LAYER_AMOUNT]; - public final BufferBuilder[] unshadedBuffers = new BufferBuilder[CHUNK_LAYER_AMOUNT]; + public final MeshEmitter[] emitters = new MeshEmitter[CHUNK_LAYER_AMOUNT]; { for (int layerIndex = 0; layerIndex < CHUNK_LAYER_AMOUNT; layerIndex++) { - int initialSize = CHUNK_LAYERS[layerIndex].bufferSize(); - shadedBuffers[layerIndex] = new BufferBuilder(initialSize); - unshadedBuffers[layerIndex] = new BufferBuilder(initialSize); + var renderType = CHUNK_LAYERS[layerIndex]; + // FIXME: We leak the memory owned by the BufferBuilder here. + var buffer = new BufferBuilder(renderType.bufferSize()); + emitters[layerIndex] = new MeshEmitter(buffer, renderType); } } } diff --git a/src/main/java/com/jozufozu/flywheel/lib/model/baked/BakedModelBuilder.java b/src/main/java/com/jozufozu/flywheel/lib/model/baked/BakedModelBuilder.java index 9847cf00e..213e3bc0d 100644 --- a/src/main/java/com/jozufozu/flywheel/lib/model/baked/BakedModelBuilder.java +++ b/src/main/java/com/jozufozu/flywheel/lib/model/baked/BakedModelBuilder.java @@ -2,6 +2,8 @@ package com.jozufozu.flywheel.lib.model.baked; import java.util.function.BiFunction; +import org.jetbrains.annotations.Nullable; + import com.google.common.collect.ImmutableList; import com.jozufozu.flywheel.api.material.Material; import com.jozufozu.flywheel.api.model.Model; @@ -9,8 +11,8 @@ import com.jozufozu.flywheel.api.vertex.VertexView; import com.jozufozu.flywheel.lib.memory.MemoryBlock; import com.jozufozu.flywheel.lib.model.ModelUtil; import com.jozufozu.flywheel.lib.model.SimpleMesh; +import com.jozufozu.flywheel.lib.model.SimpleModel; import com.jozufozu.flywheel.lib.model.baked.BakedModelBufferer.ResultConsumer; -import com.jozufozu.flywheel.lib.model.baked.BakedModelBufferer.ShadeSeparatedResultConsumer; import com.jozufozu.flywheel.lib.vertex.NoOverlayVertexView; import com.mojang.blaze3d.vertex.PoseStack; @@ -23,11 +25,15 @@ import net.minecraftforge.client.model.data.ModelData; public class BakedModelBuilder { private final BakedModel bakedModel; + @Nullable private BlockAndTintGetter renderWorld; + @Nullable private BlockState blockState; + @Nullable private PoseStack poseStack; + @Nullable private ModelData modelData; - private boolean shadeSeparated = true; + @Nullable private BiFunction materialFunc; public BakedModelBuilder(BakedModel bakedModel) { @@ -54,17 +60,12 @@ public class BakedModelBuilder { return this; } - public BakedModelBuilder disableShadeSeparation() { - shadeSeparated = false; - return this; - } - public BakedModelBuilder materialFunc(BiFunction materialFunc) { this.materialFunc = materialFunc; return this; } - public TessellatedModel build() { + public SimpleModel build() { if (renderWorld == null) { renderWorld = VirtualEmptyBlockGetter.INSTANCE; } @@ -80,30 +81,17 @@ public class BakedModelBuilder { var out = ImmutableList.builder(); - if (shadeSeparated) { - ShadeSeparatedResultConsumer resultConsumer = (renderType, shaded, data) -> { - Material material = materialFunc.apply(renderType, shaded); - if (material != null) { - VertexView vertexView = new NoOverlayVertexView(); - MemoryBlock meshData = ModelUtil.convertVanillaBuffer(data, vertexView); - var mesh = new SimpleMesh(vertexView, meshData, "source=BakedModelBuilder," + "bakedModel=" + bakedModel + ",renderType=" + renderType + ",shaded=" + shaded); - out.add(new Model.ConfiguredMesh(material, mesh)); - } - }; - BakedModelBufferer.bufferSingleShadeSeparated(ModelUtil.VANILLA_RENDERER.getModelRenderer(), renderWorld, bakedModel, blockState, poseStack, modelData, resultConsumer); - } else { - ResultConsumer resultConsumer = (renderType, data) -> { - Material material = materialFunc.apply(renderType, true); - if (material != null) { - VertexView vertexView = new NoOverlayVertexView(); - MemoryBlock meshData = ModelUtil.convertVanillaBuffer(data, vertexView); - var mesh = new SimpleMesh(vertexView, meshData, "source=BakedModelBuilder," + "bakedModel=" + bakedModel + ",renderType=" + renderType); - out.add(new Model.ConfiguredMesh(material, mesh)); - } - }; - BakedModelBufferer.bufferSingle(ModelUtil.VANILLA_RENDERER.getModelRenderer(), renderWorld, bakedModel, blockState, poseStack, modelData, resultConsumer); - } + ResultConsumer resultConsumer = (renderType, shaded, data) -> { + Material material = materialFunc.apply(renderType, shaded); + if (material != null) { + VertexView vertexView = new NoOverlayVertexView(); + MemoryBlock meshData = ModelUtil.convertVanillaBuffer(data, vertexView); + var mesh = new SimpleMesh(vertexView, meshData, "source=BakedModelBuilder," + "bakedModel=" + bakedModel + ",renderType=" + renderType + ",shaded=" + shaded); + out.add(new Model.ConfiguredMesh(material, mesh)); + } + }; + BakedModelBufferer.bufferSingle(ModelUtil.VANILLA_RENDERER.getModelRenderer(), renderWorld, bakedModel, blockState, poseStack, modelData, resultConsumer); - return new TessellatedModel(out.build(), shadeSeparated); + return new SimpleModel(out.build()); } } diff --git a/src/main/java/com/jozufozu/flywheel/lib/model/baked/BlockModelBuilder.java b/src/main/java/com/jozufozu/flywheel/lib/model/baked/BlockModelBuilder.java index 5a77226ec..8eea1d180 100644 --- a/src/main/java/com/jozufozu/flywheel/lib/model/baked/BlockModelBuilder.java +++ b/src/main/java/com/jozufozu/flywheel/lib/model/baked/BlockModelBuilder.java @@ -2,6 +2,8 @@ package com.jozufozu.flywheel.lib.model.baked; import java.util.function.BiFunction; +import org.jetbrains.annotations.Nullable; + import com.google.common.collect.ImmutableList; import com.jozufozu.flywheel.api.material.Material; import com.jozufozu.flywheel.api.model.Model; @@ -9,8 +11,8 @@ import com.jozufozu.flywheel.api.vertex.VertexView; import com.jozufozu.flywheel.lib.memory.MemoryBlock; import com.jozufozu.flywheel.lib.model.ModelUtil; import com.jozufozu.flywheel.lib.model.SimpleMesh; +import com.jozufozu.flywheel.lib.model.SimpleModel; import com.jozufozu.flywheel.lib.model.baked.BakedModelBufferer.ResultConsumer; -import com.jozufozu.flywheel.lib.model.baked.BakedModelBufferer.ShadeSeparatedResultConsumer; import com.jozufozu.flywheel.lib.vertex.NoOverlayVertexView; import com.mojang.blaze3d.vertex.PoseStack; @@ -21,10 +23,13 @@ import net.minecraftforge.client.model.data.ModelData; public class BlockModelBuilder { private final BlockState state; + @Nullable private BlockAndTintGetter renderWorld; + @Nullable private PoseStack poseStack; + @Nullable private ModelData modelData; - private boolean shadeSeparated = true; + @Nullable private BiFunction materialFunc; public BlockModelBuilder(BlockState state) { @@ -46,17 +51,12 @@ public class BlockModelBuilder { return this; } - public BlockModelBuilder disableShadeSeparation() { - shadeSeparated = false; - return this; - } - public BlockModelBuilder materialFunc(BiFunction materialFunc) { this.materialFunc = materialFunc; return this; } - public TessellatedModel build() { + public SimpleModel build() { if (renderWorld == null) { renderWorld = VirtualEmptyBlockGetter.INSTANCE; } @@ -69,30 +69,17 @@ public class BlockModelBuilder { var out = ImmutableList.builder(); - if (shadeSeparated) { - ShadeSeparatedResultConsumer resultConsumer = (renderType, shaded, data) -> { - Material material = materialFunc.apply(renderType, shaded); - if (material != null) { - VertexView vertexView = new NoOverlayVertexView(); - MemoryBlock meshData = ModelUtil.convertVanillaBuffer(data, vertexView); - var mesh = new SimpleMesh(vertexView, meshData, "source=BlockModelBuilder," + "blockState=" + state + ",renderType=" + renderType + ",shaded=" + shaded); - out.add(new Model.ConfiguredMesh(material, mesh)); - } - }; - BakedModelBufferer.bufferBlockShadeSeparated(ModelUtil.VANILLA_RENDERER, renderWorld, state, poseStack, modelData, resultConsumer); - } else { - ResultConsumer resultConsumer = (renderType, data) -> { - Material material = materialFunc.apply(renderType, true); - if (material != null) { - VertexView vertexView = new NoOverlayVertexView(); - MemoryBlock meshData = ModelUtil.convertVanillaBuffer(data, vertexView); - var mesh = new SimpleMesh(vertexView, meshData, "source=BlockModelBuilder," + "blockState=" + state + ",renderType=" + renderType); - out.add(new Model.ConfiguredMesh(material, mesh)); - } - }; - BakedModelBufferer.bufferBlock(ModelUtil.VANILLA_RENDERER, renderWorld, state, poseStack, modelData, resultConsumer); - } + ResultConsumer resultConsumer = (renderType, shaded, data) -> { + Material material = materialFunc.apply(renderType, shaded); + if (material != null) { + VertexView vertexView = new NoOverlayVertexView(); + MemoryBlock meshData = ModelUtil.convertVanillaBuffer(data, vertexView); + var mesh = new SimpleMesh(vertexView, meshData, "source=BlockModelBuilder," + "blockState=" + state + ",renderType=" + renderType + ",shaded=" + shaded); + out.add(new Model.ConfiguredMesh(material, mesh)); + } + }; + BakedModelBufferer.bufferBlock(ModelUtil.VANILLA_RENDERER, renderWorld, state, poseStack, modelData, resultConsumer); - return new TessellatedModel(out.build(), shadeSeparated); + return new SimpleModel(out.build()); } } diff --git a/src/main/java/com/jozufozu/flywheel/lib/model/baked/MeshEmitter.java b/src/main/java/com/jozufozu/flywheel/lib/model/baked/MeshEmitter.java new file mode 100644 index 000000000..8f00b36a2 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/lib/model/baked/MeshEmitter.java @@ -0,0 +1,122 @@ +package com.jozufozu.flywheel.lib.model.baked; + +import org.jetbrains.annotations.Nullable; + +import com.mojang.blaze3d.vertex.BufferBuilder; +import com.mojang.blaze3d.vertex.DefaultVertexFormat; +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.vertex.VertexConsumer; +import com.mojang.blaze3d.vertex.VertexFormat; + +import net.minecraft.client.renderer.RenderType; +import net.minecraft.client.renderer.block.model.BakedQuad; + +class MeshEmitter implements VertexConsumer { + private final BufferBuilder bufferBuilder; + private final RenderType renderType; + private boolean lastQuadWasShaded; + private boolean seenFirstQuad; + @Nullable + private BakedModelBufferer.ResultConsumer resultConsumer; + + MeshEmitter(BufferBuilder bufferBuilder, RenderType renderType) { + this.bufferBuilder = bufferBuilder; + this.renderType = renderType; + } + + public void begin(BakedModelBufferer.ResultConsumer resultConsumer) { + this.resultConsumer = resultConsumer; + + begin(); + } + + public void end() { + emit(); + seenFirstQuad = false; + resultConsumer = null; + } + + private void begin() { + bufferBuilder.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.BLOCK); + } + + private void emit() { + var renderedBuffer = bufferBuilder.endOrDiscardIfEmpty(); + + if (renderedBuffer != null) { + if (resultConsumer != null) { + resultConsumer.accept(renderType, lastQuadWasShaded, renderedBuffer); + } + renderedBuffer.release(); + } + } + + private void observeQuadAndEmitIfNecessary(BakedQuad quad) { + if (seenFirstQuad && lastQuadWasShaded != quad.isShade()) { + emit(); + begin(); + } + + seenFirstQuad = true; + lastQuadWasShaded = quad.isShade(); + } + + @Override + public void putBulkData(PoseStack.Pose poseEntry, BakedQuad quad, float[] colorMuls, float red, float green, float blue, int[] combinedLights, int combinedOverlay, boolean mulColor) { + observeQuadAndEmitIfNecessary(quad); + + bufferBuilder.putBulkData(poseEntry, quad, colorMuls, red, green, blue, combinedLights, combinedOverlay, mulColor); + } + + @Override + public void putBulkData(PoseStack.Pose matrixEntry, BakedQuad quad, float[] baseBrightness, float red, float green, float blue, float alpha, int[] lightmapCoords, int overlayCoords, boolean readExistingColor) { + observeQuadAndEmitIfNecessary(quad); + + bufferBuilder.putBulkData(matrixEntry, quad, baseBrightness, red, green, blue, alpha, lightmapCoords, overlayCoords, readExistingColor); + } + + @Override + public VertexConsumer vertex(double x, double y, double z) { + throw new UnsupportedOperationException("ShadeSeparatingVertexConsumer only supports putBulkData!"); + } + + @Override + public VertexConsumer color(int red, int green, int blue, int alpha) { + throw new UnsupportedOperationException("ShadeSeparatingVertexConsumer only supports putBulkData!"); + } + + @Override + public VertexConsumer uv(float u, float v) { + throw new UnsupportedOperationException("ShadeSeparatingVertexConsumer only supports putBulkData!"); + } + + @Override + public VertexConsumer overlayCoords(int u, int v) { + throw new UnsupportedOperationException("ShadeSeparatingVertexConsumer only supports putBulkData!"); + } + + @Override + public VertexConsumer uv2(int u, int v) { + throw new UnsupportedOperationException("ShadeSeparatingVertexConsumer only supports putBulkData!"); + } + + @Override + public VertexConsumer normal(float x, float y, float z) { + throw new UnsupportedOperationException("ShadeSeparatingVertexConsumer only supports putBulkData!"); + } + + @Override + public void endVertex() { + throw new UnsupportedOperationException("ShadeSeparatingVertexConsumer only supports putBulkData!"); + } + + @Override + public void defaultColor(int red, int green, int blue, int alpha) { + throw new UnsupportedOperationException("ShadeSeparatingVertexConsumer only supports putBulkData!"); + } + + @Override + public void unsetDefaultColor() { + throw new UnsupportedOperationException("ShadeSeparatingVertexConsumer only supports putBulkData!"); + } +} diff --git a/src/main/java/com/jozufozu/flywheel/lib/model/baked/MultiBlockModelBuilder.java b/src/main/java/com/jozufozu/flywheel/lib/model/baked/MultiBlockModelBuilder.java index f221b6a84..b42834fa1 100644 --- a/src/main/java/com/jozufozu/flywheel/lib/model/baked/MultiBlockModelBuilder.java +++ b/src/main/java/com/jozufozu/flywheel/lib/model/baked/MultiBlockModelBuilder.java @@ -3,6 +3,8 @@ package com.jozufozu.flywheel.lib.model.baked; import java.util.function.BiFunction; import java.util.function.Function; +import org.jetbrains.annotations.Nullable; + import com.google.common.collect.ImmutableList; import com.jozufozu.flywheel.api.material.Material; import com.jozufozu.flywheel.api.model.Model; @@ -10,8 +12,8 @@ import com.jozufozu.flywheel.api.vertex.VertexView; import com.jozufozu.flywheel.lib.memory.MemoryBlock; import com.jozufozu.flywheel.lib.model.ModelUtil; import com.jozufozu.flywheel.lib.model.SimpleMesh; +import com.jozufozu.flywheel.lib.model.SimpleModel; import com.jozufozu.flywheel.lib.model.baked.BakedModelBufferer.ResultConsumer; -import com.jozufozu.flywheel.lib.model.baked.BakedModelBufferer.ShadeSeparatedResultConsumer; import com.jozufozu.flywheel.lib.vertex.NoOverlayVertexView; import com.mojang.blaze3d.vertex.PoseStack; @@ -23,10 +25,12 @@ import net.minecraftforge.client.model.data.ModelData; public class MultiBlockModelBuilder { private final BlockAndTintGetter renderWorld; private final Iterable positions; + @Nullable private PoseStack poseStack; + @Nullable private Function modelDataLookup; private boolean renderFluids = false; - private boolean shadeSeparated = true; + @Nullable private BiFunction materialFunc; public MultiBlockModelBuilder(BlockAndTintGetter renderWorld, Iterable positions) { @@ -49,17 +53,12 @@ public class MultiBlockModelBuilder { return this; } - public MultiBlockModelBuilder disableShadeSeparation() { - shadeSeparated = false; - return this; - } - public MultiBlockModelBuilder materialFunc(BiFunction materialFunc) { this.materialFunc = materialFunc; return this; } - public TessellatedModel build() { + public SimpleModel build() { if (modelDataLookup == null) { modelDataLookup = pos -> ModelData.EMPTY; } @@ -69,30 +68,17 @@ public class MultiBlockModelBuilder { var out = ImmutableList.builder(); - if (shadeSeparated) { - ShadeSeparatedResultConsumer resultConsumer = (renderType, shaded, data) -> { - Material material = materialFunc.apply(renderType, shaded); - if (material != null) { - VertexView vertexView = new NoOverlayVertexView(); - MemoryBlock meshData = ModelUtil.convertVanillaBuffer(data, vertexView); - var mesh = new SimpleMesh(vertexView, meshData, "source=MultiBlockModelBuilder," + "renderType=" + renderType + ",shaded=" + shaded); - out.add(new Model.ConfiguredMesh(material, mesh)); - } - }; - BakedModelBufferer.bufferMultiBlockShadeSeparated(ModelUtil.VANILLA_RENDERER, positions.iterator(), renderWorld, poseStack, modelDataLookup, renderFluids, resultConsumer); - } else { - ResultConsumer resultConsumer = (renderType, data) -> { - Material material = materialFunc.apply(renderType, true); - if (material != null) { - VertexView vertexView = new NoOverlayVertexView(); - MemoryBlock meshData = ModelUtil.convertVanillaBuffer(data, vertexView); - var mesh = new SimpleMesh(vertexView, meshData, "source=MultiBlockModelBuilder," + "renderType=" + renderType); - out.add(new Model.ConfiguredMesh(material, mesh)); - } - }; - BakedModelBufferer.bufferMultiBlock(ModelUtil.VANILLA_RENDERER, positions.iterator(), renderWorld, poseStack, modelDataLookup, renderFluids, resultConsumer); - } + ResultConsumer resultConsumer = (renderType, shaded, data) -> { + Material material = materialFunc.apply(renderType, shaded); + if (material != null) { + VertexView vertexView = new NoOverlayVertexView(); + MemoryBlock meshData = ModelUtil.convertVanillaBuffer(data, vertexView); + var mesh = new SimpleMesh(vertexView, meshData, "source=MultiBlockModelBuilder," + "renderType=" + renderType + ",shaded=" + shaded); + out.add(new Model.ConfiguredMesh(material, mesh)); + } + }; + BakedModelBufferer.bufferMultiBlock(ModelUtil.VANILLA_RENDERER, positions.iterator(), renderWorld, poseStack, modelDataLookup, renderFluids, resultConsumer); - return new TessellatedModel(out.build(), shadeSeparated); + return new SimpleModel(out.build()); } } diff --git a/src/main/java/com/jozufozu/flywheel/lib/model/baked/ShadeSeparatingVertexConsumer.java b/src/main/java/com/jozufozu/flywheel/lib/model/baked/ShadeSeparatingVertexConsumer.java deleted file mode 100644 index 6d0165403..000000000 --- a/src/main/java/com/jozufozu/flywheel/lib/model/baked/ShadeSeparatingVertexConsumer.java +++ /dev/null @@ -1,84 +0,0 @@ -package com.jozufozu.flywheel.lib.model.baked; - -import com.mojang.blaze3d.vertex.PoseStack; -import com.mojang.blaze3d.vertex.VertexConsumer; - -import net.minecraft.client.renderer.block.model.BakedQuad; - -class ShadeSeparatingVertexConsumer implements VertexConsumer { - private VertexConsumer shadedConsumer; - private VertexConsumer unshadedConsumer; - - public void prepare(VertexConsumer shadedConsumer, VertexConsumer unshadedConsumer) { - this.shadedConsumer = shadedConsumer; - this.unshadedConsumer = unshadedConsumer; - } - - public void clear() { - shadedConsumer = null; - unshadedConsumer = null; - } - - @Override - public void putBulkData(PoseStack.Pose poseEntry, BakedQuad quad, float[] colorMuls, float red, float green, float blue, int[] combinedLights, int combinedOverlay, boolean mulColor) { - if (quad.isShade()) { - shadedConsumer.putBulkData(poseEntry, quad, colorMuls, red, green, blue, combinedLights, combinedOverlay, mulColor); - } else { - unshadedConsumer.putBulkData(poseEntry, quad, colorMuls, red, green, blue, combinedLights, combinedOverlay, mulColor); - } - } - - @Override - public void putBulkData(PoseStack.Pose matrixEntry, BakedQuad bakedQuad, float[] baseBrightness, float red, float green, float blue, float alpha, int[] lightmapCoords, int overlayCoords, boolean readExistingColor) { - if (bakedQuad.isShade()) { - shadedConsumer.putBulkData(matrixEntry, bakedQuad, baseBrightness, red, green, blue, alpha, lightmapCoords, overlayCoords, readExistingColor); - } else { - unshadedConsumer.putBulkData(matrixEntry, bakedQuad, baseBrightness, red, green, blue, alpha, lightmapCoords, overlayCoords, readExistingColor); - } - } - - @Override - public VertexConsumer vertex(double x, double y, double z) { - throw new UnsupportedOperationException("ShadeSeparatingVertexConsumer only supports putBulkData!"); - } - - @Override - public VertexConsumer color(int red, int green, int blue, int alpha) { - throw new UnsupportedOperationException("ShadeSeparatingVertexConsumer only supports putBulkData!"); - } - - @Override - public VertexConsumer uv(float u, float v) { - throw new UnsupportedOperationException("ShadeSeparatingVertexConsumer only supports putBulkData!"); - } - - @Override - public VertexConsumer overlayCoords(int u, int v) { - throw new UnsupportedOperationException("ShadeSeparatingVertexConsumer only supports putBulkData!"); - } - - @Override - public VertexConsumer uv2(int u, int v) { - throw new UnsupportedOperationException("ShadeSeparatingVertexConsumer only supports putBulkData!"); - } - - @Override - public VertexConsumer normal(float x, float y, float z) { - throw new UnsupportedOperationException("ShadeSeparatingVertexConsumer only supports putBulkData!"); - } - - @Override - public void endVertex() { - throw new UnsupportedOperationException("ShadeSeparatingVertexConsumer only supports putBulkData!"); - } - - @Override - public void defaultColor(int red, int green, int blue, int alpha) { - throw new UnsupportedOperationException("ShadeSeparatingVertexConsumer only supports putBulkData!"); - } - - @Override - public void unsetDefaultColor() { - throw new UnsupportedOperationException("ShadeSeparatingVertexConsumer only supports putBulkData!"); - } -} diff --git a/src/main/java/com/jozufozu/flywheel/lib/model/baked/TessellatedModel.java b/src/main/java/com/jozufozu/flywheel/lib/model/baked/TessellatedModel.java deleted file mode 100644 index 691b25b4e..000000000 --- a/src/main/java/com/jozufozu/flywheel/lib/model/baked/TessellatedModel.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.jozufozu.flywheel.lib.model.baked; - -import com.google.common.collect.ImmutableList; -import com.jozufozu.flywheel.lib.model.SimpleModel; - -public class TessellatedModel extends SimpleModel { - private final boolean shadeSeparated; - - public TessellatedModel(ImmutableList meshes, boolean shadeSeparated) { - super(meshes); - this.shadeSeparated = shadeSeparated; - } - - public boolean isShadeSeparated() { - return shadeSeparated; - } -} diff --git a/src/main/resources/assets/flywheel/flywheel/internal/common.frag b/src/main/resources/assets/flywheel/flywheel/internal/common.frag index e0186dd26..f73846dea 100644 --- a/src/main/resources/assets/flywheel/flywheel/internal/common.frag +++ b/src/main/resources/assets/flywheel/flywheel/internal/common.frag @@ -25,6 +25,18 @@ flat in uint _flw_instanceID; out vec4 _flw_outputColor; +float _flw_diffuseFactor() { + if (flw_material.diffuse) { + if (flw_constantAmbientLight == 1u) { + return diffuseNether(flw_vertexNormal); + } else { + return diffuse(flw_vertexNormal); + } + } else { + return 1.; + } +} + void _flw_main() { flw_sampleColor = texture(flw_diffuseTex, flw_vertexTexCoord); flw_fragColor = flw_vertexColor * flw_sampleColor; @@ -49,15 +61,8 @@ void _flw_main() { vec4 color = flw_fragColor; - if (flw_material.diffuse) { - float diffuseFactor; - if (flw_constantAmbientLight == 1u) { - diffuseFactor = diffuseNether(flw_vertexNormal); - } else { - diffuseFactor = diffuse(flw_vertexNormal); - } - color.rgb *= diffuseFactor; - } + float diffuseFactor = _flw_diffuseFactor(); + color.rgb *= diffuseFactor; if (flw_material.useOverlay) { vec4 overlayColor = texelFetch(flw_overlayTex, flw_fragOverlay, 0); @@ -90,8 +95,11 @@ void _flw_main() { case 5u: color = vec4(flw_fragOverlay / 16., 0., 1.); break; - #ifdef _FLW_EMBEDDED case 6u: + color = vec4(vec3(diffuseFactor), 1.); + break; + #ifdef _FLW_EMBEDDED + case 7u: color = vec4(_flw_lightVolumeCoord, 1.); break; #endif