mirror of
https://github.com/Jozufozu/Flywheel.git
synced 2025-02-27 12:14:40 +01:00
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
This commit is contained in:
parent
183ea1f0f8
commit
96eb5ea47c
12 changed files with 246 additions and 387 deletions
|
@ -7,6 +7,16 @@ import org.joml.Vector4fc;
|
||||||
import com.jozufozu.flywheel.api.material.Material;
|
import com.jozufozu.flywheel.api.material.Material;
|
||||||
|
|
||||||
public interface Model {
|
public interface Model {
|
||||||
|
/**
|
||||||
|
* Get a list of all meshes in this model.
|
||||||
|
*
|
||||||
|
* <p>The contents of the returned list will be queried, but never modified.</p>
|
||||||
|
*
|
||||||
|
* <p>Meshes will be rendered in the order they appear in this list, though
|
||||||
|
* no render order guarantees are made for meshes between different models.</p>
|
||||||
|
*
|
||||||
|
* @return A list of meshes.
|
||||||
|
*/
|
||||||
List<ConfiguredMesh> meshes();
|
List<ConfiguredMesh> meshes();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -33,6 +33,7 @@ import com.jozufozu.flywheel.backend.gl.shader.GlProgram;
|
||||||
|
|
||||||
public class IndirectCullingGroup<I extends Instance> {
|
public class IndirectCullingGroup<I extends Instance> {
|
||||||
private static final Comparator<IndirectDraw> DRAW_COMPARATOR = Comparator.comparing(IndirectDraw::stage)
|
private static final Comparator<IndirectDraw> DRAW_COMPARATOR = Comparator.comparing(IndirectDraw::stage)
|
||||||
|
.thenComparing(IndirectDraw::indexOfMeshInModel)
|
||||||
.thenComparing(IndirectDraw::material, MaterialRenderState.COMPARATOR);
|
.thenComparing(IndirectDraw::material, MaterialRenderState.COMPARATOR);
|
||||||
|
|
||||||
private static final int DRAW_BARRIER_BITS = GL_SHADER_STORAGE_BARRIER_BIT | GL_COMMAND_BARRIER_BIT;
|
private static final int DRAW_BARRIER_BITS = GL_SHADER_STORAGE_BARRIER_BIT | GL_COMMAND_BARRIER_BIT;
|
||||||
|
@ -179,9 +180,12 @@ public class IndirectCullingGroup<I extends Instance> {
|
||||||
instancer.index = instancers.size();
|
instancer.index = instancers.size();
|
||||||
instancers.add(instancer);
|
instancers.add(instancer);
|
||||||
|
|
||||||
for (var entry : model.meshes()) {
|
List<Model.ConfiguredMesh> meshes = model.meshes();
|
||||||
|
for (int i = 0; i < meshes.size(); i++) {
|
||||||
|
var entry = meshes.get(i);
|
||||||
|
|
||||||
MeshPool.PooledMesh mesh = meshPool.alloc(entry.mesh());
|
MeshPool.PooledMesh mesh = meshPool.alloc(entry.mesh());
|
||||||
var draw = new IndirectDraw(instancer, entry.material(), mesh, stage);
|
var draw = new IndirectDraw(instancer, entry.material(), mesh, stage, i);
|
||||||
indirectDraws.add(draw);
|
indirectDraws.add(draw);
|
||||||
instancer.addDraw(draw);
|
instancer.addDraw(draw);
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ public class IndirectDraw {
|
||||||
private final Material material;
|
private final Material material;
|
||||||
private final MeshPool.PooledMesh mesh;
|
private final MeshPool.PooledMesh mesh;
|
||||||
private final RenderStage stage;
|
private final RenderStage stage;
|
||||||
|
private final int indexOfMeshInModel;
|
||||||
|
|
||||||
private final int materialVertexIndex;
|
private final int materialVertexIndex;
|
||||||
private final int materialFragmentIndex;
|
private final int materialFragmentIndex;
|
||||||
|
@ -20,11 +21,12 @@ public class IndirectDraw {
|
||||||
private final int packedMaterialProperties;
|
private final int packedMaterialProperties;
|
||||||
private boolean deleted;
|
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.model = model;
|
||||||
this.material = material;
|
this.material = material;
|
||||||
this.mesh = mesh;
|
this.mesh = mesh;
|
||||||
this.stage = stage;
|
this.stage = stage;
|
||||||
|
this.indexOfMeshInModel = indexOfMeshInModel;
|
||||||
|
|
||||||
mesh.acquire();
|
mesh.acquire();
|
||||||
|
|
||||||
|
@ -50,6 +52,10 @@ public class IndirectDraw {
|
||||||
return stage;
|
return stage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int indexOfMeshInModel() {
|
||||||
|
return indexOfMeshInModel;
|
||||||
|
}
|
||||||
|
|
||||||
public void write(long ptr) {
|
public void write(long ptr) {
|
||||||
MemoryUtil.memPutInt(ptr, mesh.indexCount()); // count
|
MemoryUtil.memPutInt(ptr, mesh.indexCount()); // count
|
||||||
MemoryUtil.memPutInt(ptr + 4, 0); // instanceCount - to be set by the apply shader
|
MemoryUtil.memPutInt(ptr + 4, 0); // instanceCount - to be set by the apply shader
|
||||||
|
|
|
@ -7,5 +7,6 @@ public enum DebugMode {
|
||||||
LIGHT_LEVEL,
|
LIGHT_LEVEL,
|
||||||
LIGHT_COLOR,
|
LIGHT_COLOR,
|
||||||
OVERLAY,
|
OVERLAY,
|
||||||
|
DIFFUSE,
|
||||||
LIGHT_VOLUME,
|
LIGHT_VOLUME,
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,9 +7,7 @@ import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import com.mojang.blaze3d.vertex.BufferBuilder;
|
import com.mojang.blaze3d.vertex.BufferBuilder;
|
||||||
import com.mojang.blaze3d.vertex.BufferBuilder.RenderedBuffer;
|
import com.mojang.blaze3d.vertex.BufferBuilder.RenderedBuffer;
|
||||||
import com.mojang.blaze3d.vertex.DefaultVertexFormat;
|
|
||||||
import com.mojang.blaze3d.vertex.PoseStack;
|
import com.mojang.blaze3d.vertex.PoseStack;
|
||||||
import com.mojang.blaze3d.vertex.VertexFormat;
|
|
||||||
|
|
||||||
import net.minecraft.client.renderer.ItemBlockRenderTypes;
|
import net.minecraft.client.renderer.ItemBlockRenderTypes;
|
||||||
import net.minecraft.client.renderer.RenderType;
|
import net.minecraft.client.renderer.RenderType;
|
||||||
|
@ -41,7 +39,7 @@ final class BakedModelBufferer {
|
||||||
poseStack = objects.identityPoseStack;
|
poseStack = objects.identityPoseStack;
|
||||||
}
|
}
|
||||||
RandomSource random = objects.random;
|
RandomSource random = objects.random;
|
||||||
BufferBuilder[] buffers = objects.shadedBuffers;
|
var consumers = objects.emitters;
|
||||||
|
|
||||||
modelData = model.getModelData(renderWorld, BlockPos.ZERO, state, modelData);
|
modelData = model.getModelData(renderWorld, BlockPos.ZERO, state, modelData);
|
||||||
random.setSeed(42L);
|
random.setSeed(42L);
|
||||||
|
@ -49,63 +47,17 @@ final class BakedModelBufferer {
|
||||||
|
|
||||||
for (RenderType renderType : renderTypes) {
|
for (RenderType renderType : renderTypes) {
|
||||||
int layerIndex = renderType.getChunkLayerId();
|
int layerIndex = renderType.getChunkLayerId();
|
||||||
|
var consumer = consumers[layerIndex];
|
||||||
|
|
||||||
BufferBuilder buffer = buffers[layerIndex];
|
consumer.begin(resultConsumer);
|
||||||
buffer.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.BLOCK);
|
|
||||||
|
|
||||||
poseStack.pushPose();
|
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();
|
poseStack.popPose();
|
||||||
|
|
||||||
RenderedBuffer data = buffer.endOrDiscardIfEmpty();
|
consumer.end();
|
||||||
if (data != null) {
|
|
||||||
resultConsumer.accept(renderType, data);
|
|
||||||
data.release();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
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) {
|
public static void bufferBlock(BlockRenderDispatcher renderDispatcher, BlockAndTintGetter renderWorld, BlockState state, @Nullable PoseStack poseStack, ModelData modelData, ResultConsumer resultConsumer) {
|
||||||
if (state.getRenderShape() != RenderShape.MODEL) {
|
if (state.getRenderShape() != RenderShape.MODEL) {
|
||||||
|
@ -115,14 +67,6 @@ final class BakedModelBufferer {
|
||||||
bufferSingle(renderDispatcher.getModelRenderer(), renderWorld, renderDispatcher.getBlockModel(state), state, poseStack, modelData, resultConsumer);
|
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<BlockPos> posIterator, BlockAndTintGetter renderWorld, @Nullable PoseStack poseStack, Function<BlockPos, ModelData> modelDataLookup, boolean renderFluids, ResultConsumer resultConsumer) {
|
public static void bufferMultiBlock(BlockRenderDispatcher renderDispatcher, Iterator<BlockPos> posIterator, BlockAndTintGetter renderWorld, @Nullable PoseStack poseStack, Function<BlockPos, ModelData> modelDataLookup, boolean renderFluids, ResultConsumer resultConsumer) {
|
||||||
ThreadLocalObjects objects = THREAD_LOCAL_OBJECTS.get();
|
ThreadLocalObjects objects = THREAD_LOCAL_OBJECTS.get();
|
||||||
if (poseStack == null) {
|
if (poseStack == null) {
|
||||||
|
@ -131,9 +75,10 @@ final class BakedModelBufferer {
|
||||||
RandomSource random = objects.random;
|
RandomSource random = objects.random;
|
||||||
TransformingVertexConsumer transformingWrapper = objects.transformingWrapper;
|
TransformingVertexConsumer transformingWrapper = objects.transformingWrapper;
|
||||||
|
|
||||||
BufferBuilder[] buffers = objects.shadedBuffers;
|
var emitters = objects.emitters;
|
||||||
for (BufferBuilder buffer : buffers) {
|
|
||||||
buffer.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.BLOCK);
|
for (var emitter : emitters) {
|
||||||
|
emitter.begin(resultConsumer);
|
||||||
}
|
}
|
||||||
|
|
||||||
ModelBlockRenderer blockRenderer = renderDispatcher.getModelRenderer();
|
ModelBlockRenderer blockRenderer = renderDispatcher.getModelRenderer();
|
||||||
|
@ -150,7 +95,7 @@ final class BakedModelBufferer {
|
||||||
RenderType layer = ItemBlockRenderTypes.getRenderLayer(fluidState);
|
RenderType layer = ItemBlockRenderTypes.getRenderLayer(fluidState);
|
||||||
int layerIndex = layer.getChunkLayerId();
|
int layerIndex = layer.getChunkLayerId();
|
||||||
|
|
||||||
transformingWrapper.prepare(buffers[layerIndex], poseStack);
|
transformingWrapper.prepare(emitters[layerIndex], poseStack);
|
||||||
|
|
||||||
poseStack.pushPose();
|
poseStack.pushPose();
|
||||||
poseStack.translate(pos.getX() - (pos.getX() & 0xF), pos.getY() - (pos.getY() & 0xF), pos.getZ() - (pos.getZ() & 0xF));
|
poseStack.translate(pos.getX() - (pos.getX() & 0xF), pos.getY() - (pos.getY() & 0xF), pos.getZ() - (pos.getZ() & 0xF));
|
||||||
|
@ -170,11 +115,9 @@ final class BakedModelBufferer {
|
||||||
for (RenderType renderType : renderTypes) {
|
for (RenderType renderType : renderTypes) {
|
||||||
int layerIndex = renderType.getChunkLayerId();
|
int layerIndex = renderType.getChunkLayerId();
|
||||||
|
|
||||||
BufferBuilder buffer = buffers[layerIndex];
|
|
||||||
|
|
||||||
poseStack.pushPose();
|
poseStack.pushPose();
|
||||||
poseStack.translate(pos.getX(), pos.getY(), pos.getZ());
|
poseStack.translate(pos.getX(), pos.getY(), pos.getZ());
|
||||||
blockRenderer.tesselateBlock(renderWorld, model, state, pos, poseStack, buffer, 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();
|
poseStack.popPose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -182,108 +125,14 @@ final class BakedModelBufferer {
|
||||||
|
|
||||||
ModelBlockRenderer.clearCache();
|
ModelBlockRenderer.clearCache();
|
||||||
|
|
||||||
|
for (var emitter : emitters) {
|
||||||
|
emitter.end();
|
||||||
|
}
|
||||||
|
|
||||||
transformingWrapper.clear();
|
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<BlockPos> posIterator, BlockAndTintGetter renderWorld, @Nullable PoseStack poseStack, Function<BlockPos, ModelData> 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);
|
|
||||||
|
|
||||||
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();
|
|
||||||
|
|
||||||
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);
|
|
||||||
poseStack.popPose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface ResultConsumer {
|
public interface ResultConsumer {
|
||||||
void accept(RenderType renderType, RenderedBuffer data);
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface ShadeSeparatedResultConsumer {
|
|
||||||
void accept(RenderType renderType, boolean shaded, RenderedBuffer data);
|
void accept(RenderType renderType, boolean shaded, RenderedBuffer data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -291,17 +140,16 @@ final class BakedModelBufferer {
|
||||||
public final PoseStack identityPoseStack = new PoseStack();
|
public final PoseStack identityPoseStack = new PoseStack();
|
||||||
public final RandomSource random = RandomSource.createNewThreadLocalInstance();
|
public final RandomSource random = RandomSource.createNewThreadLocalInstance();
|
||||||
|
|
||||||
public final ShadeSeparatingVertexConsumer shadeSeparatingWrapper = new ShadeSeparatingVertexConsumer();
|
|
||||||
public final TransformingVertexConsumer transformingWrapper = new TransformingVertexConsumer();
|
public final TransformingVertexConsumer transformingWrapper = new TransformingVertexConsumer();
|
||||||
|
|
||||||
public final BufferBuilder[] shadedBuffers = new BufferBuilder[CHUNK_LAYER_AMOUNT];
|
public final MeshEmitter[] emitters = new MeshEmitter[CHUNK_LAYER_AMOUNT];
|
||||||
public final BufferBuilder[] unshadedBuffers = new BufferBuilder[CHUNK_LAYER_AMOUNT];
|
|
||||||
|
|
||||||
{
|
{
|
||||||
for (int layerIndex = 0; layerIndex < CHUNK_LAYER_AMOUNT; layerIndex++) {
|
for (int layerIndex = 0; layerIndex < CHUNK_LAYER_AMOUNT; layerIndex++) {
|
||||||
int initialSize = CHUNK_LAYERS[layerIndex].bufferSize();
|
var renderType = CHUNK_LAYERS[layerIndex];
|
||||||
shadedBuffers[layerIndex] = new BufferBuilder(initialSize);
|
// FIXME: We leak the memory owned by the BufferBuilder here.
|
||||||
unshadedBuffers[layerIndex] = new BufferBuilder(initialSize);
|
var buffer = new BufferBuilder(renderType.bufferSize());
|
||||||
|
emitters[layerIndex] = new MeshEmitter(buffer, renderType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,8 @@ package com.jozufozu.flywheel.lib.model.baked;
|
||||||
|
|
||||||
import java.util.function.BiFunction;
|
import java.util.function.BiFunction;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.jozufozu.flywheel.api.material.Material;
|
import com.jozufozu.flywheel.api.material.Material;
|
||||||
import com.jozufozu.flywheel.api.model.Model;
|
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.memory.MemoryBlock;
|
||||||
import com.jozufozu.flywheel.lib.model.ModelUtil;
|
import com.jozufozu.flywheel.lib.model.ModelUtil;
|
||||||
import com.jozufozu.flywheel.lib.model.SimpleMesh;
|
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.ResultConsumer;
|
||||||
import com.jozufozu.flywheel.lib.model.baked.BakedModelBufferer.ShadeSeparatedResultConsumer;
|
|
||||||
import com.jozufozu.flywheel.lib.vertex.NoOverlayVertexView;
|
import com.jozufozu.flywheel.lib.vertex.NoOverlayVertexView;
|
||||||
import com.mojang.blaze3d.vertex.PoseStack;
|
import com.mojang.blaze3d.vertex.PoseStack;
|
||||||
|
|
||||||
|
@ -23,11 +25,15 @@ import net.minecraftforge.client.model.data.ModelData;
|
||||||
|
|
||||||
public class BakedModelBuilder {
|
public class BakedModelBuilder {
|
||||||
private final BakedModel bakedModel;
|
private final BakedModel bakedModel;
|
||||||
|
@Nullable
|
||||||
private BlockAndTintGetter renderWorld;
|
private BlockAndTintGetter renderWorld;
|
||||||
|
@Nullable
|
||||||
private BlockState blockState;
|
private BlockState blockState;
|
||||||
|
@Nullable
|
||||||
private PoseStack poseStack;
|
private PoseStack poseStack;
|
||||||
|
@Nullable
|
||||||
private ModelData modelData;
|
private ModelData modelData;
|
||||||
private boolean shadeSeparated = true;
|
@Nullable
|
||||||
private BiFunction<RenderType, Boolean, Material> materialFunc;
|
private BiFunction<RenderType, Boolean, Material> materialFunc;
|
||||||
|
|
||||||
public BakedModelBuilder(BakedModel bakedModel) {
|
public BakedModelBuilder(BakedModel bakedModel) {
|
||||||
|
@ -54,17 +60,12 @@ public class BakedModelBuilder {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BakedModelBuilder disableShadeSeparation() {
|
|
||||||
shadeSeparated = false;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public BakedModelBuilder materialFunc(BiFunction<RenderType, Boolean, Material> materialFunc) {
|
public BakedModelBuilder materialFunc(BiFunction<RenderType, Boolean, Material> materialFunc) {
|
||||||
this.materialFunc = materialFunc;
|
this.materialFunc = materialFunc;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TessellatedModel build() {
|
public SimpleModel build() {
|
||||||
if (renderWorld == null) {
|
if (renderWorld == null) {
|
||||||
renderWorld = VirtualEmptyBlockGetter.INSTANCE;
|
renderWorld = VirtualEmptyBlockGetter.INSTANCE;
|
||||||
}
|
}
|
||||||
|
@ -80,8 +81,7 @@ public class BakedModelBuilder {
|
||||||
|
|
||||||
var out = ImmutableList.<Model.ConfiguredMesh>builder();
|
var out = ImmutableList.<Model.ConfiguredMesh>builder();
|
||||||
|
|
||||||
if (shadeSeparated) {
|
ResultConsumer resultConsumer = (renderType, shaded, data) -> {
|
||||||
ShadeSeparatedResultConsumer resultConsumer = (renderType, shaded, data) -> {
|
|
||||||
Material material = materialFunc.apply(renderType, shaded);
|
Material material = materialFunc.apply(renderType, shaded);
|
||||||
if (material != null) {
|
if (material != null) {
|
||||||
VertexView vertexView = new NoOverlayVertexView();
|
VertexView vertexView = new NoOverlayVertexView();
|
||||||
|
@ -90,20 +90,8 @@ public class BakedModelBuilder {
|
||||||
out.add(new Model.ConfiguredMesh(material, mesh));
|
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);
|
BakedModelBufferer.bufferSingle(ModelUtil.VANILLA_RENDERER.getModelRenderer(), renderWorld, bakedModel, blockState, poseStack, modelData, resultConsumer);
|
||||||
}
|
|
||||||
|
|
||||||
return new TessellatedModel(out.build(), shadeSeparated);
|
return new SimpleModel(out.build());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,8 @@ package com.jozufozu.flywheel.lib.model.baked;
|
||||||
|
|
||||||
import java.util.function.BiFunction;
|
import java.util.function.BiFunction;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.jozufozu.flywheel.api.material.Material;
|
import com.jozufozu.flywheel.api.material.Material;
|
||||||
import com.jozufozu.flywheel.api.model.Model;
|
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.memory.MemoryBlock;
|
||||||
import com.jozufozu.flywheel.lib.model.ModelUtil;
|
import com.jozufozu.flywheel.lib.model.ModelUtil;
|
||||||
import com.jozufozu.flywheel.lib.model.SimpleMesh;
|
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.ResultConsumer;
|
||||||
import com.jozufozu.flywheel.lib.model.baked.BakedModelBufferer.ShadeSeparatedResultConsumer;
|
|
||||||
import com.jozufozu.flywheel.lib.vertex.NoOverlayVertexView;
|
import com.jozufozu.flywheel.lib.vertex.NoOverlayVertexView;
|
||||||
import com.mojang.blaze3d.vertex.PoseStack;
|
import com.mojang.blaze3d.vertex.PoseStack;
|
||||||
|
|
||||||
|
@ -21,10 +23,13 @@ import net.minecraftforge.client.model.data.ModelData;
|
||||||
|
|
||||||
public class BlockModelBuilder {
|
public class BlockModelBuilder {
|
||||||
private final BlockState state;
|
private final BlockState state;
|
||||||
|
@Nullable
|
||||||
private BlockAndTintGetter renderWorld;
|
private BlockAndTintGetter renderWorld;
|
||||||
|
@Nullable
|
||||||
private PoseStack poseStack;
|
private PoseStack poseStack;
|
||||||
|
@Nullable
|
||||||
private ModelData modelData;
|
private ModelData modelData;
|
||||||
private boolean shadeSeparated = true;
|
@Nullable
|
||||||
private BiFunction<RenderType, Boolean, Material> materialFunc;
|
private BiFunction<RenderType, Boolean, Material> materialFunc;
|
||||||
|
|
||||||
public BlockModelBuilder(BlockState state) {
|
public BlockModelBuilder(BlockState state) {
|
||||||
|
@ -46,17 +51,12 @@ public class BlockModelBuilder {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BlockModelBuilder disableShadeSeparation() {
|
|
||||||
shadeSeparated = false;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public BlockModelBuilder materialFunc(BiFunction<RenderType, Boolean, Material> materialFunc) {
|
public BlockModelBuilder materialFunc(BiFunction<RenderType, Boolean, Material> materialFunc) {
|
||||||
this.materialFunc = materialFunc;
|
this.materialFunc = materialFunc;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TessellatedModel build() {
|
public SimpleModel build() {
|
||||||
if (renderWorld == null) {
|
if (renderWorld == null) {
|
||||||
renderWorld = VirtualEmptyBlockGetter.INSTANCE;
|
renderWorld = VirtualEmptyBlockGetter.INSTANCE;
|
||||||
}
|
}
|
||||||
|
@ -69,8 +69,7 @@ public class BlockModelBuilder {
|
||||||
|
|
||||||
var out = ImmutableList.<Model.ConfiguredMesh>builder();
|
var out = ImmutableList.<Model.ConfiguredMesh>builder();
|
||||||
|
|
||||||
if (shadeSeparated) {
|
ResultConsumer resultConsumer = (renderType, shaded, data) -> {
|
||||||
ShadeSeparatedResultConsumer resultConsumer = (renderType, shaded, data) -> {
|
|
||||||
Material material = materialFunc.apply(renderType, shaded);
|
Material material = materialFunc.apply(renderType, shaded);
|
||||||
if (material != null) {
|
if (material != null) {
|
||||||
VertexView vertexView = new NoOverlayVertexView();
|
VertexView vertexView = new NoOverlayVertexView();
|
||||||
|
@ -79,20 +78,8 @@ public class BlockModelBuilder {
|
||||||
out.add(new Model.ConfiguredMesh(material, mesh));
|
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);
|
BakedModelBufferer.bufferBlock(ModelUtil.VANILLA_RENDERER, renderWorld, state, poseStack, modelData, resultConsumer);
|
||||||
}
|
|
||||||
|
|
||||||
return new TessellatedModel(out.build(), shadeSeparated);
|
return new SimpleModel(out.build());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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!");
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,6 +3,8 @@ package com.jozufozu.flywheel.lib.model.baked;
|
||||||
import java.util.function.BiFunction;
|
import java.util.function.BiFunction;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.jozufozu.flywheel.api.material.Material;
|
import com.jozufozu.flywheel.api.material.Material;
|
||||||
import com.jozufozu.flywheel.api.model.Model;
|
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.memory.MemoryBlock;
|
||||||
import com.jozufozu.flywheel.lib.model.ModelUtil;
|
import com.jozufozu.flywheel.lib.model.ModelUtil;
|
||||||
import com.jozufozu.flywheel.lib.model.SimpleMesh;
|
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.ResultConsumer;
|
||||||
import com.jozufozu.flywheel.lib.model.baked.BakedModelBufferer.ShadeSeparatedResultConsumer;
|
|
||||||
import com.jozufozu.flywheel.lib.vertex.NoOverlayVertexView;
|
import com.jozufozu.flywheel.lib.vertex.NoOverlayVertexView;
|
||||||
import com.mojang.blaze3d.vertex.PoseStack;
|
import com.mojang.blaze3d.vertex.PoseStack;
|
||||||
|
|
||||||
|
@ -23,10 +25,12 @@ import net.minecraftforge.client.model.data.ModelData;
|
||||||
public class MultiBlockModelBuilder {
|
public class MultiBlockModelBuilder {
|
||||||
private final BlockAndTintGetter renderWorld;
|
private final BlockAndTintGetter renderWorld;
|
||||||
private final Iterable<BlockPos> positions;
|
private final Iterable<BlockPos> positions;
|
||||||
|
@Nullable
|
||||||
private PoseStack poseStack;
|
private PoseStack poseStack;
|
||||||
|
@Nullable
|
||||||
private Function<BlockPos, ModelData> modelDataLookup;
|
private Function<BlockPos, ModelData> modelDataLookup;
|
||||||
private boolean renderFluids = false;
|
private boolean renderFluids = false;
|
||||||
private boolean shadeSeparated = true;
|
@Nullable
|
||||||
private BiFunction<RenderType, Boolean, Material> materialFunc;
|
private BiFunction<RenderType, Boolean, Material> materialFunc;
|
||||||
|
|
||||||
public MultiBlockModelBuilder(BlockAndTintGetter renderWorld, Iterable<BlockPos> positions) {
|
public MultiBlockModelBuilder(BlockAndTintGetter renderWorld, Iterable<BlockPos> positions) {
|
||||||
|
@ -49,17 +53,12 @@ public class MultiBlockModelBuilder {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public MultiBlockModelBuilder disableShadeSeparation() {
|
|
||||||
shadeSeparated = false;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MultiBlockModelBuilder materialFunc(BiFunction<RenderType, Boolean, Material> materialFunc) {
|
public MultiBlockModelBuilder materialFunc(BiFunction<RenderType, Boolean, Material> materialFunc) {
|
||||||
this.materialFunc = materialFunc;
|
this.materialFunc = materialFunc;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TessellatedModel build() {
|
public SimpleModel build() {
|
||||||
if (modelDataLookup == null) {
|
if (modelDataLookup == null) {
|
||||||
modelDataLookup = pos -> ModelData.EMPTY;
|
modelDataLookup = pos -> ModelData.EMPTY;
|
||||||
}
|
}
|
||||||
|
@ -69,8 +68,7 @@ public class MultiBlockModelBuilder {
|
||||||
|
|
||||||
var out = ImmutableList.<Model.ConfiguredMesh>builder();
|
var out = ImmutableList.<Model.ConfiguredMesh>builder();
|
||||||
|
|
||||||
if (shadeSeparated) {
|
ResultConsumer resultConsumer = (renderType, shaded, data) -> {
|
||||||
ShadeSeparatedResultConsumer resultConsumer = (renderType, shaded, data) -> {
|
|
||||||
Material material = materialFunc.apply(renderType, shaded);
|
Material material = materialFunc.apply(renderType, shaded);
|
||||||
if (material != null) {
|
if (material != null) {
|
||||||
VertexView vertexView = new NoOverlayVertexView();
|
VertexView vertexView = new NoOverlayVertexView();
|
||||||
|
@ -79,20 +77,8 @@ public class MultiBlockModelBuilder {
|
||||||
out.add(new Model.ConfiguredMesh(material, mesh));
|
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);
|
BakedModelBufferer.bufferMultiBlock(ModelUtil.VANILLA_RENDERER, positions.iterator(), renderWorld, poseStack, modelDataLookup, renderFluids, resultConsumer);
|
||||||
}
|
|
||||||
|
|
||||||
return new TessellatedModel(out.build(), shadeSeparated);
|
return new SimpleModel(out.build());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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!");
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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<ConfiguredMesh> meshes, boolean shadeSeparated) {
|
|
||||||
super(meshes);
|
|
||||||
this.shadeSeparated = shadeSeparated;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isShadeSeparated() {
|
|
||||||
return shadeSeparated;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -25,6 +25,18 @@ flat in uint _flw_instanceID;
|
||||||
|
|
||||||
out vec4 _flw_outputColor;
|
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() {
|
void _flw_main() {
|
||||||
flw_sampleColor = texture(flw_diffuseTex, flw_vertexTexCoord);
|
flw_sampleColor = texture(flw_diffuseTex, flw_vertexTexCoord);
|
||||||
flw_fragColor = flw_vertexColor * flw_sampleColor;
|
flw_fragColor = flw_vertexColor * flw_sampleColor;
|
||||||
|
@ -49,15 +61,8 @@ void _flw_main() {
|
||||||
|
|
||||||
vec4 color = flw_fragColor;
|
vec4 color = flw_fragColor;
|
||||||
|
|
||||||
if (flw_material.diffuse) {
|
float diffuseFactor = _flw_diffuseFactor();
|
||||||
float diffuseFactor;
|
|
||||||
if (flw_constantAmbientLight == 1u) {
|
|
||||||
diffuseFactor = diffuseNether(flw_vertexNormal);
|
|
||||||
} else {
|
|
||||||
diffuseFactor = diffuse(flw_vertexNormal);
|
|
||||||
}
|
|
||||||
color.rgb *= diffuseFactor;
|
color.rgb *= diffuseFactor;
|
||||||
}
|
|
||||||
|
|
||||||
if (flw_material.useOverlay) {
|
if (flw_material.useOverlay) {
|
||||||
vec4 overlayColor = texelFetch(flw_overlayTex, flw_fragOverlay, 0);
|
vec4 overlayColor = texelFetch(flw_overlayTex, flw_fragOverlay, 0);
|
||||||
|
@ -90,8 +95,11 @@ void _flw_main() {
|
||||||
case 5u:
|
case 5u:
|
||||||
color = vec4(flw_fragOverlay / 16., 0., 1.);
|
color = vec4(flw_fragOverlay / 16., 0., 1.);
|
||||||
break;
|
break;
|
||||||
#ifdef _FLW_EMBEDDED
|
|
||||||
case 6u:
|
case 6u:
|
||||||
|
color = vec4(vec3(diffuseFactor), 1.);
|
||||||
|
break;
|
||||||
|
#ifdef _FLW_EMBEDDED
|
||||||
|
case 7u:
|
||||||
color = vec4(_flw_lightVolumeCoord, 1.);
|
color = vec4(_flw_lightVolumeCoord, 1.);
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Add table
Reference in a new issue