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