Best-effort untested Fabric model builders

- Also fix crash buffering fluids in BakedModelBufferer#bufferMultiBlock (Forge)
- The mesh order in models created by model builders is currently incorrect and will be fixed later
This commit is contained in:
PepperCode1 2024-04-26 22:31:29 -07:00 committed by Jozufozu
parent 9ae4065c1c
commit b9490fe11a
19 changed files with 853 additions and 219 deletions

View file

@ -2,6 +2,7 @@ package com.jozufozu.flywheel.lib.model.baked;
import java.util.function.BiFunction;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
import com.jozufozu.flywheel.api.material.Material;
@ -14,25 +15,26 @@ import net.minecraft.client.resources.model.BakedModel;
import net.minecraft.world.level.BlockAndTintGetter;
import net.minecraft.world.level.block.state.BlockState;
@ApiStatus.NonExtendable
public abstract class BakedModelBuilder {
protected final BakedModel bakedModel;
final BakedModel bakedModel;
@Nullable
protected BlockAndTintGetter level;
BlockAndTintGetter level;
@Nullable
protected BlockState blockState;
BlockState blockState;
@Nullable
protected PoseStack poseStack;
PoseStack poseStack;
@Nullable
protected BiFunction<RenderType, Boolean, Material> materialFunc;
BiFunction<RenderType, Boolean, Material> materialFunc;
BakedModelBuilder(BakedModel bakedModel) {
this.bakedModel = bakedModel;
}
public static BakedModelBuilder create(BakedModel bakedModel) {
return FlwLibXplat.INSTANCE.createBakedModelBuilder(bakedModel);
}
protected BakedModelBuilder(BakedModel bakedModel) {
this.bakedModel = bakedModel;
}
public BakedModelBuilder level(BlockAndTintGetter level) {
this.level = level;
return this;

View file

@ -2,6 +2,7 @@ package com.jozufozu.flywheel.lib.model.baked;
import java.util.function.BiFunction;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
import com.jozufozu.flywheel.api.material.Material;
@ -13,23 +14,24 @@ import net.minecraft.client.renderer.RenderType;
import net.minecraft.world.level.BlockAndTintGetter;
import net.minecraft.world.level.block.state.BlockState;
@ApiStatus.NonExtendable
public abstract class BlockModelBuilder {
protected final BlockState state;
final BlockState state;
@Nullable
protected BlockAndTintGetter level;
BlockAndTintGetter level;
@Nullable
protected PoseStack poseStack;
PoseStack poseStack;
@Nullable
protected BiFunction<RenderType, Boolean, Material> materialFunc;
BiFunction<RenderType, Boolean, Material> materialFunc;
BlockModelBuilder(BlockState state) {
this.state = state;
}
public static BlockModelBuilder create(BlockState state) {
return FlwLibXplat.INSTANCE.createBlockModelBuilder(state);
}
protected BlockModelBuilder(BlockState state) {
this.state = state;
}
public BlockModelBuilder level(BlockAndTintGetter level) {
this.level = level;
return this;

View file

@ -1,119 +0,0 @@
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 {
protected final BufferBuilder bufferBuilder;
private final RenderType renderType;
private boolean lastQuadWasShaded;
private boolean seenFirstQuad;
@Nullable
private MeshEmitter.ResultConsumer resultConsumer;
MeshEmitter(BufferBuilder bufferBuilder, RenderType renderType) {
this.bufferBuilder = bufferBuilder;
this.renderType = renderType;
}
public void begin(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();
}
}
protected 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 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!");
}
public interface ResultConsumer {
void accept(RenderType renderType, boolean shaded, BufferBuilder.RenderedBuffer data);
}
}

View file

@ -2,6 +2,7 @@ package com.jozufozu.flywheel.lib.model.baked;
import java.util.function.BiFunction;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
import com.jozufozu.flywheel.api.material.Material;
@ -13,24 +14,25 @@ import net.minecraft.client.renderer.RenderType;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.BlockAndTintGetter;
@ApiStatus.NonExtendable
public abstract class MultiBlockModelBuilder {
protected final BlockAndTintGetter level;
protected final Iterable<BlockPos> positions;
final BlockAndTintGetter level;
final Iterable<BlockPos> positions;
@Nullable
protected PoseStack poseStack;
protected boolean renderFluids = false;
PoseStack poseStack;
boolean renderFluids = false;
@Nullable
protected BiFunction<RenderType, Boolean, Material> materialFunc;
BiFunction<RenderType, Boolean, Material> materialFunc;
MultiBlockModelBuilder(BlockAndTintGetter level, Iterable<BlockPos> positions) {
this.level = level;
this.positions = positions;
}
public static MultiBlockModelBuilder create(BlockAndTintGetter level, Iterable<BlockPos> positions) {
return FlwLibXplat.INSTANCE.createMultiBlockModelBuilder(level, positions);
}
protected MultiBlockModelBuilder(BlockAndTintGetter level, Iterable<BlockPos> positions) {
this.level = level;
this.positions = positions;
}
public MultiBlockModelBuilder poseStack(PoseStack poseStack) {
this.poseStack = poseStack;
return this;

View file

@ -1,5 +1,6 @@
package com.jozufozu.flywheel.lib.model.baked;
import org.jetbrains.annotations.UnknownNullability;
import org.joml.Matrix3f;
import org.joml.Matrix4f;
@ -8,7 +9,9 @@ import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
class TransformingVertexConsumer implements VertexConsumer {
@UnknownNullability
private VertexConsumer delegate;
@UnknownNullability
private PoseStack poseStack;
public void prepare(VertexConsumer delegate, PoseStack poseStack) {

View file

@ -15,7 +15,7 @@ import net.minecraft.core.SectionPos;
import net.minecraft.world.level.LightLayer;
@Mixin(ClientChunkCache.class)
public class ClientChunkCacheMixin {
abstract class ClientChunkCacheMixin {
@Shadow
@Final
ClientLevel level;

View file

@ -0,0 +1,154 @@
package com.jozufozu.flywheel.lib.model.baked;
import java.util.Iterator;
import org.jetbrains.annotations.Nullable;
import com.mojang.blaze3d.vertex.BufferBuilder.RenderedBuffer;
import com.mojang.blaze3d.vertex.PoseStack;
import it.unimi.dsi.fastutil.objects.Reference2ReferenceMap;
import it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap;
import net.minecraft.client.renderer.ItemBlockRenderTypes;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.block.BlockRenderDispatcher;
import net.minecraft.client.renderer.block.ModelBlockRenderer;
import net.minecraft.client.renderer.texture.OverlayTexture;
import net.minecraft.client.resources.model.BakedModel;
import net.minecraft.core.BlockPos;
import net.minecraft.util.RandomSource;
import net.minecraft.world.level.BlockAndTintGetter;
import net.minecraft.world.level.block.RenderShape;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.material.FluidState;
final class BakedModelBufferer {
private static final RenderType[] CHUNK_LAYERS = RenderType.chunkBufferLayers().toArray(RenderType[]::new);
private static final int CHUNK_LAYER_AMOUNT = CHUNK_LAYERS.length;
private static final ThreadLocal<ThreadLocalObjects> THREAD_LOCAL_OBJECTS = ThreadLocal.withInitial(ThreadLocalObjects::new);
private BakedModelBufferer() {
}
public static void bufferSingle(ModelBlockRenderer blockRenderer, BlockAndTintGetter level, BakedModel model, BlockState state, @Nullable PoseStack poseStack, ResultConsumer resultConsumer) {
ThreadLocalObjects objects = THREAD_LOCAL_OBJECTS.get();
if (poseStack == null) {
poseStack = objects.identityPoseStack;
}
RandomSource random = objects.random;
MeshEmitter[] emitters = objects.emitters;
UniversalMeshEmitter universalEmitter = objects.universalEmitter;
for (MeshEmitter emitter : emitters) {
emitter.prepare(resultConsumer);
}
RenderType defaultLayer = ItemBlockRenderTypes.getChunkRenderType(state);
universalEmitter.prepare(defaultLayer);
model = universalEmitter.wrapModel(model);
poseStack.pushPose();
blockRenderer.tesselateBlock(level, model, state, BlockPos.ZERO, poseStack, universalEmitter, false, random, 42L, OverlayTexture.NO_OVERLAY);
poseStack.popPose();
universalEmitter.clear();
for (MeshEmitter emitter : emitters) {
emitter.end();
}
}
public static void bufferBlock(BlockRenderDispatcher renderDispatcher, BlockAndTintGetter level, BlockState state, @Nullable PoseStack poseStack, ResultConsumer resultConsumer) {
if (state.getRenderShape() != RenderShape.MODEL) {
return;
}
bufferSingle(renderDispatcher.getModelRenderer(), level, renderDispatcher.getBlockModel(state), state, poseStack, resultConsumer);
}
public static void bufferMultiBlock(BlockRenderDispatcher renderDispatcher, Iterator<BlockPos> posIterator, BlockAndTintGetter level, @Nullable PoseStack poseStack, boolean renderFluids, ResultConsumer resultConsumer) {
ThreadLocalObjects objects = THREAD_LOCAL_OBJECTS.get();
if (poseStack == null) {
poseStack = objects.identityPoseStack;
}
RandomSource random = objects.random;
MeshEmitter[] emitters = objects.emitters;
Reference2ReferenceMap<RenderType, MeshEmitter> emitterMap = objects.emitterMap;
UniversalMeshEmitter universalEmitter = objects.universalEmitter;
TransformingVertexConsumer transformingWrapper = objects.transformingWrapper;
for (MeshEmitter emitter : emitters) {
emitter.prepare(resultConsumer);
}
ModelBlockRenderer blockRenderer = renderDispatcher.getModelRenderer();
ModelBlockRenderer.enableCaching();
while (posIterator.hasNext()) {
BlockPos pos = posIterator.next();
BlockState state = level.getBlockState(pos);
if (renderFluids) {
FluidState fluidState = state.getFluidState();
if (!fluidState.isEmpty()) {
RenderType renderType = ItemBlockRenderTypes.getRenderLayer(fluidState);
transformingWrapper.prepare(emitterMap.get(renderType).getBuffer(true), poseStack);
poseStack.pushPose();
poseStack.translate(pos.getX() - (pos.getX() & 0xF), pos.getY() - (pos.getY() & 0xF), pos.getZ() - (pos.getZ() & 0xF));
renderDispatcher.renderLiquid(pos, level, transformingWrapper, state, fluidState);
poseStack.popPose();
}
}
if (state.getRenderShape() == RenderShape.MODEL) {
long seed = state.getSeed(pos);
BakedModel model = renderDispatcher.getBlockModel(state);
RenderType defaultLayer = ItemBlockRenderTypes.getChunkRenderType(state);
universalEmitter.prepare(defaultLayer);
model = universalEmitter.wrapModel(model);
poseStack.pushPose();
poseStack.translate(pos.getX(), pos.getY(), pos.getZ());
blockRenderer.tesselateBlock(level, model, state, pos, poseStack, universalEmitter, true, random, seed, OverlayTexture.NO_OVERLAY);
poseStack.popPose();
}
}
ModelBlockRenderer.clearCache();
transformingWrapper.clear();
universalEmitter.clear();
for (MeshEmitter emitter : emitters) {
emitter.end();
}
}
public interface ResultConsumer {
void accept(RenderType renderType, boolean shaded, RenderedBuffer data);
}
private static class ThreadLocalObjects {
public final PoseStack identityPoseStack = new PoseStack();
public final RandomSource random = RandomSource.createNewThreadLocalInstance();
public final MeshEmitter[] emitters = new MeshEmitter[CHUNK_LAYER_AMOUNT];
public final Reference2ReferenceMap<RenderType, MeshEmitter> emitterMap = new Reference2ReferenceOpenHashMap<>();
public final UniversalMeshEmitter universalEmitter;
public final TransformingVertexConsumer transformingWrapper = new TransformingVertexConsumer();
{
for (int layerIndex = 0; layerIndex < CHUNK_LAYER_AMOUNT; layerIndex++) {
RenderType renderType = CHUNK_LAYERS[layerIndex];
MeshEmitter emitter = new MeshEmitter(renderType);
emitters[layerIndex] = emitter;
emitterMap.put(renderType, emitter);
}
universalEmitter = new UniversalMeshEmitter(emitterMap);
}
}
}

View file

@ -0,0 +1,77 @@
package com.jozufozu.flywheel.lib.model.baked;
import java.util.function.BiFunction;
import com.google.common.collect.ImmutableList;
import com.jozufozu.flywheel.api.material.Material;
import com.jozufozu.flywheel.api.model.Model;
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.vertex.NoOverlayVertexView;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.resources.model.BakedModel;
import net.minecraft.world.level.BlockAndTintGetter;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.state.BlockState;
public final class FabricBakedModelBuilder extends BakedModelBuilder {
public FabricBakedModelBuilder(BakedModel bakedModel) {
super(bakedModel);
}
@Override
public FabricBakedModelBuilder level(BlockAndTintGetter level) {
super.level(level);
return this;
}
@Override
public FabricBakedModelBuilder blockState(BlockState blockState) {
super.blockState(blockState);
return this;
}
@Override
public FabricBakedModelBuilder poseStack(PoseStack poseStack) {
super.poseStack(poseStack);
return this;
}
@Override
public FabricBakedModelBuilder materialFunc(BiFunction<RenderType, Boolean, Material> materialFunc) {
super.materialFunc(materialFunc);
return this;
}
@Override
public SimpleModel build() {
if (level == null) {
level = VirtualEmptyBlockGetter.INSTANCE;
}
if (blockState == null) {
blockState = Blocks.AIR.defaultBlockState();
}
if (materialFunc == null) {
materialFunc = ModelUtil::getMaterial;
}
var out = ImmutableList.<Model.ConfiguredMesh>builder();
BakedModelBufferer.bufferSingle(ModelUtil.VANILLA_RENDERER.getModelRenderer(), level, bakedModel, blockState, poseStack, (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));
}
});
return new SimpleModel(out.build());
}
}

View file

@ -0,0 +1,66 @@
package com.jozufozu.flywheel.lib.model.baked;
import java.util.function.BiFunction;
import com.google.common.collect.ImmutableList;
import com.jozufozu.flywheel.api.material.Material;
import com.jozufozu.flywheel.api.model.Model;
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.vertex.NoOverlayVertexView;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.world.level.BlockAndTintGetter;
import net.minecraft.world.level.block.state.BlockState;
public final class FabricBlockModelBuilder extends BlockModelBuilder {
public FabricBlockModelBuilder(BlockState state) {
super(state);
}
@Override
public FabricBlockModelBuilder level(BlockAndTintGetter level) {
super.level(level);
return this;
}
@Override
public FabricBlockModelBuilder poseStack(PoseStack poseStack) {
super.poseStack(poseStack);
return this;
}
@Override
public FabricBlockModelBuilder materialFunc(BiFunction<RenderType, Boolean, Material> materialFunc) {
super.materialFunc(materialFunc);
return this;
}
@Override
public SimpleModel build() {
if (level == null) {
level = VirtualEmptyBlockGetter.INSTANCE;
}
if (materialFunc == null) {
materialFunc = ModelUtil::getMaterial;
}
var out = ImmutableList.<Model.ConfiguredMesh>builder();
BakedModelBufferer.bufferBlock(ModelUtil.VANILLA_RENDERER, level, state, poseStack, (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));
}
});
return new SimpleModel(out.build());
}
}

View file

@ -0,0 +1,63 @@
package com.jozufozu.flywheel.lib.model.baked;
import java.util.function.BiFunction;
import com.google.common.collect.ImmutableList;
import com.jozufozu.flywheel.api.material.Material;
import com.jozufozu.flywheel.api.model.Model;
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.vertex.NoOverlayVertexView;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.BlockAndTintGetter;
public final class FabricMultiBlockModelBuilder extends MultiBlockModelBuilder {
public FabricMultiBlockModelBuilder(BlockAndTintGetter level, Iterable<BlockPos> positions) {
super(level, positions);
}
@Override
public FabricMultiBlockModelBuilder poseStack(PoseStack poseStack) {
super.poseStack(poseStack);
return this;
}
@Override
public FabricMultiBlockModelBuilder enableFluidRendering() {
super.enableFluidRendering();
return this;
}
@Override
public FabricMultiBlockModelBuilder materialFunc(BiFunction<RenderType, Boolean, Material> materialFunc) {
super.materialFunc(materialFunc);
return this;
}
@Override
public SimpleModel build() {
if (materialFunc == null) {
materialFunc = ModelUtil::getMaterial;
}
var out = ImmutableList.<Model.ConfiguredMesh>builder();
BakedModelBufferer.bufferMultiBlock(ModelUtil.VANILLA_RENDERER, positions.iterator(), level, poseStack, renderFluids, (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));
}
});
return new SimpleModel(out.build());
}
}

View file

@ -0,0 +1,57 @@
package com.jozufozu.flywheel.lib.model.baked;
import org.jetbrains.annotations.UnknownNullability;
import com.mojang.blaze3d.vertex.BufferBuilder;
import com.mojang.blaze3d.vertex.DefaultVertexFormat;
import com.mojang.blaze3d.vertex.VertexFormat;
import net.minecraft.client.renderer.RenderType;
class MeshEmitter {
private final RenderType renderType;
private final BufferBuilder bufferBuilder;
private BakedModelBufferer.@UnknownNullability ResultConsumer resultConsumer;
private boolean currentShade;
MeshEmitter(RenderType renderType) {
this.renderType = renderType;
this.bufferBuilder = new BufferBuilder(renderType.bufferSize());
}
public void prepare(BakedModelBufferer.ResultConsumer resultConsumer) {
this.resultConsumer = resultConsumer;
}
public void end() {
if (bufferBuilder.building()) {
emit();
}
resultConsumer = null;
}
public BufferBuilder getBuffer(boolean shade) {
prepareForGeometry(shade);
return bufferBuilder;
}
void prepareForGeometry(boolean shade) {
if (!bufferBuilder.building()) {
bufferBuilder.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.BLOCK);
} else if (shade != currentShade) {
emit();
}
currentShade = shade;
}
void emit() {
var renderedBuffer = bufferBuilder.endOrDiscardIfEmpty();
if (renderedBuffer != null) {
resultConsumer.accept(renderType, currentShade, renderedBuffer);
renderedBuffer.release();
}
}
}

View file

@ -0,0 +1,146 @@
package com.jozufozu.flywheel.lib.model.baked;
import java.util.function.Supplier;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.UnknownNullability;
import com.mojang.blaze3d.vertex.BufferBuilder;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import it.unimi.dsi.fastutil.objects.Reference2ReferenceMap;
import net.fabricmc.fabric.api.renderer.v1.material.BlendMode;
import net.fabricmc.fabric.api.renderer.v1.material.RenderMaterial;
import net.fabricmc.fabric.api.renderer.v1.model.ForwardingBakedModel;
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.block.model.BakedQuad;
import net.minecraft.client.resources.model.BakedModel;
import net.minecraft.core.BlockPos;
import net.minecraft.util.RandomSource;
import net.minecraft.world.level.BlockAndTintGetter;
import net.minecraft.world.level.block.state.BlockState;
class UniversalMeshEmitter implements VertexConsumer {
private final Reference2ReferenceMap<RenderType, MeshEmitter> emitterMap;
private final WrapperModel wrapperModel = new WrapperModel();
@UnknownNullability
private RenderType defaultLayer;
@UnknownNullability
private BufferBuilder currentDelegate;
UniversalMeshEmitter(Reference2ReferenceMap<RenderType, MeshEmitter> emitterMap) {
this.emitterMap = emitterMap;
}
public void prepare(RenderType defaultLayer) {
this.defaultLayer = defaultLayer;
}
public void clear() {
wrapperModel.setWrapped(null);
}
public BakedModel wrapModel(BakedModel model) {
wrapperModel.setWrapped(model);
return wrapperModel;
}
private void prepareForGeometry(RenderMaterial material) {
BlendMode blendMode = material.blendMode();
RenderType layer = blendMode == BlendMode.DEFAULT ? defaultLayer : blendMode.blockRenderLayer;
boolean shade = !material.disableDiffuse();
currentDelegate = emitterMap.get(layer).getBuffer(shade);
}
@Override
public VertexConsumer vertex(double x, double y, double z) {
currentDelegate.vertex(x, y, z);
return this;
}
@Override
public VertexConsumer color(int red, int green, int blue, int alpha) {
currentDelegate.color(red, green, blue, alpha);
return this;
}
@Override
public VertexConsumer uv(float u, float v) {
currentDelegate.uv(u, v);
return this;
}
@Override
public VertexConsumer overlayCoords(int u, int v) {
currentDelegate.overlayCoords(u, v);
return this;
}
@Override
public VertexConsumer uv2(int u, int v) {
currentDelegate.uv2(u, v);
return this;
}
@Override
public VertexConsumer normal(float x, float y, float z) {
currentDelegate.normal(x, y, z);
return this;
}
@Override
public void endVertex() {
currentDelegate.endVertex();
}
@Override
public void defaultColor(int red, int green, int blue, int alpha) {
currentDelegate.defaultColor(red, green, blue, alpha);
}
@Override
public void unsetDefaultColor() {
currentDelegate.unsetDefaultColor();
}
@Override
public void vertex(float x, float y, float z, float red, float green, float blue, float alpha, float u, float v, int overlay, int light, float normalX, float normalY, float normalZ) {
currentDelegate.vertex(x, y, z, red, green, blue, alpha, u, v, overlay, light, normalX, normalY, normalZ);
}
@Override
public void putBulkData(PoseStack.Pose pose, BakedQuad quad, float red, float green, float blue, int light, int overlay) {
currentDelegate.putBulkData(pose, quad, red, green, blue, light, overlay);
}
@Override
public void putBulkData(PoseStack.Pose pose, BakedQuad quad, float[] brightnesses, float red, float green, float blue, int[] lights, int overlay, boolean readExistingColor) {
currentDelegate.putBulkData(pose, quad, brightnesses, red, green, blue, lights, overlay, readExistingColor);
}
private class WrapperModel extends ForwardingBakedModel {
private final RenderContext.QuadTransform quadTransform = quad -> {
UniversalMeshEmitter.this.prepareForGeometry(quad.material());
return true;
};
public void setWrapped(@Nullable BakedModel wrapped) {
this.wrapped = wrapped;
}
@Override
public boolean isVanillaAdapter() {
return false;
}
@Override
public void emitBlockQuads(BlockAndTintGetter level, BlockState state, BlockPos pos, Supplier<RandomSource> randomSupplier, RenderContext context) {
context.pushTransform(quadTransform);
super.emitBlockQuads(level, state, pos, randomSupplier, context);
context.popTransform();
}
}
}

View file

@ -5,6 +5,9 @@ import org.jetbrains.annotations.Nullable;
import com.jozufozu.flywheel.lib.internal.FlwLibXplat;
import com.jozufozu.flywheel.lib.model.baked.BakedModelBuilder;
import com.jozufozu.flywheel.lib.model.baked.BlockModelBuilder;
import com.jozufozu.flywheel.lib.model.baked.FabricBakedModelBuilder;
import com.jozufozu.flywheel.lib.model.baked.FabricBlockModelBuilder;
import com.jozufozu.flywheel.lib.model.baked.FabricMultiBlockModelBuilder;
import com.jozufozu.flywheel.lib.model.baked.MultiBlockModelBuilder;
import com.jozufozu.flywheel.lib.util.ShadersModHandler;
@ -25,17 +28,17 @@ public class FlwLibXplatImpl implements FlwLibXplat {
@Override
public BakedModelBuilder createBakedModelBuilder(BakedModel bakedModel) {
return null;
return new FabricBakedModelBuilder(bakedModel);
}
@Override
public BlockModelBuilder createBlockModelBuilder(BlockState state) {
return null;
return new FabricBlockModelBuilder(state);
}
@Override
public MultiBlockModelBuilder createMultiBlockModelBuilder(BlockAndTintGetter level, Iterable<BlockPos> positions) {
return null;
return new FabricMultiBlockModelBuilder(level, positions);
}
@Override

View file

@ -5,7 +5,6 @@ import java.util.function.Function;
import org.jetbrains.annotations.Nullable;
import com.mojang.blaze3d.vertex.BufferBuilder;
import com.mojang.blaze3d.vertex.BufferBuilder.RenderedBuffer;
import com.mojang.blaze3d.vertex.PoseStack;
@ -33,13 +32,13 @@ final class BakedModelBufferer {
private BakedModelBufferer() {
}
public static void bufferSingle(ModelBlockRenderer blockRenderer, BlockAndTintGetter level, BakedModel model, BlockState state, @Nullable PoseStack poseStack, ModelData modelData, MeshEmitter.ResultConsumer resultConsumer) {
public static void bufferSingle(ModelBlockRenderer blockRenderer, BlockAndTintGetter level, BakedModel model, BlockState state, @Nullable PoseStack poseStack, ModelData modelData, ResultConsumer resultConsumer) {
ThreadLocalObjects objects = THREAD_LOCAL_OBJECTS.get();
if (poseStack == null) {
poseStack = objects.identityPoseStack;
}
RandomSource random = objects.random;
var consumers = objects.emitters;
MeshEmitter[] emitters = objects.emitters;
modelData = model.getModelData(level, BlockPos.ZERO, state, modelData);
random.setSeed(42L);
@ -47,19 +46,19 @@ final class BakedModelBufferer {
for (RenderType renderType : renderTypes) {
int layerIndex = renderType.getChunkLayerId();
var consumer = consumers[layerIndex];
MeshEmitter emitter = emitters[layerIndex];
consumer.begin(resultConsumer);
emitter.prepare(resultConsumer);
poseStack.pushPose();
blockRenderer.tesselateBlock(level, model, state, BlockPos.ZERO, poseStack, consumer, false, random, 42L, OverlayTexture.NO_OVERLAY, modelData, renderType);
blockRenderer.tesselateBlock(level, model, state, BlockPos.ZERO, poseStack, emitter, false, random, 42L, OverlayTexture.NO_OVERLAY, modelData, renderType);
poseStack.popPose();
consumer.end();
emitter.end();
}
}
public static void bufferBlock(BlockRenderDispatcher renderDispatcher, BlockAndTintGetter level, BlockState state, @Nullable PoseStack poseStack, ModelData modelData, MeshEmitter.ResultConsumer resultConsumer) {
public static void bufferBlock(BlockRenderDispatcher renderDispatcher, BlockAndTintGetter level, BlockState state, @Nullable PoseStack poseStack, ModelData modelData, ResultConsumer resultConsumer) {
if (state.getRenderShape() != RenderShape.MODEL) {
return;
}
@ -67,18 +66,17 @@ final class BakedModelBufferer {
bufferSingle(renderDispatcher.getModelRenderer(), level, renderDispatcher.getBlockModel(state), state, poseStack, modelData, resultConsumer);
}
public static void bufferMultiBlock(BlockRenderDispatcher renderDispatcher, Iterator<BlockPos> posIterator, BlockAndTintGetter level, @Nullable PoseStack poseStack, Function<BlockPos, ModelData> modelDataLookup, boolean renderFluids, MeshEmitter.ResultConsumer resultConsumer) {
public static void bufferMultiBlock(BlockRenderDispatcher renderDispatcher, Iterator<BlockPos> posIterator, BlockAndTintGetter level, @Nullable PoseStack poseStack, Function<BlockPos, ModelData> modelDataLookup, boolean renderFluids, ResultConsumer resultConsumer) {
ThreadLocalObjects objects = THREAD_LOCAL_OBJECTS.get();
if (poseStack == null) {
poseStack = objects.identityPoseStack;
}
RandomSource random = objects.random;
MeshEmitter[] emitters = objects.emitters;
TransformingVertexConsumer transformingWrapper = objects.transformingWrapper;
var emitters = objects.emitters;
for (var emitter : emitters) {
emitter.begin(resultConsumer);
for (MeshEmitter emitter : emitters) {
emitter.prepare(resultConsumer);
}
ModelBlockRenderer blockRenderer = renderDispatcher.getModelRenderer();
@ -92,10 +90,10 @@ final class BakedModelBufferer {
FluidState fluidState = state.getFluidState();
if (!fluidState.isEmpty()) {
RenderType layer = ItemBlockRenderTypes.getRenderLayer(fluidState);
int layerIndex = layer.getChunkLayerId();
RenderType renderType = ItemBlockRenderTypes.getRenderLayer(fluidState);
int layerIndex = renderType.getChunkLayerId();
transformingWrapper.prepare(emitters[layerIndex], poseStack);
transformingWrapper.prepare(emitters[layerIndex].unwrap(true), poseStack);
poseStack.pushPose();
poseStack.translate(pos.getX() - (pos.getX() & 0xF), pos.getY() - (pos.getY() & 0xF), pos.getZ() - (pos.getZ() & 0xF));
@ -124,12 +122,11 @@ final class BakedModelBufferer {
}
ModelBlockRenderer.clearCache();
transformingWrapper.clear();
for (var emitter : emitters) {
for (MeshEmitter emitter : emitters) {
emitter.end();
}
transformingWrapper.clear();
}
public interface ResultConsumer {
@ -140,15 +137,13 @@ final class BakedModelBufferer {
public final PoseStack identityPoseStack = new PoseStack();
public final RandomSource random = RandomSource.createNewThreadLocalInstance();
public final MeshEmitter[] emitters = new MeshEmitter[CHUNK_LAYER_AMOUNT];
public final TransformingVertexConsumer transformingWrapper = new TransformingVertexConsumer();
public final ForgeMeshEmitter[] emitters = new ForgeMeshEmitter[CHUNK_LAYER_AMOUNT];
{
for (int layerIndex = 0; layerIndex < CHUNK_LAYER_AMOUNT; layerIndex++) {
var renderType = CHUNK_LAYERS[layerIndex];
var buffer = new BufferBuilder(renderType.bufferSize());
emitters[layerIndex] = new ForgeMeshEmitter(buffer, renderType);
RenderType renderType = CHUNK_LAYERS[layerIndex];
emitters[layerIndex] = new MeshEmitter(renderType);
}
}
}

View file

@ -1,5 +1,7 @@
package com.jozufozu.flywheel.lib.model.baked;
import java.util.function.BiFunction;
import org.jetbrains.annotations.Nullable;
import com.google.common.collect.ImmutableList;
@ -10,14 +12,17 @@ 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.MeshEmitter.ResultConsumer;
import com.jozufozu.flywheel.lib.vertex.NoOverlayVertexView;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.resources.model.BakedModel;
import net.minecraft.world.level.BlockAndTintGetter;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraftforge.client.model.data.ModelData;
public class ForgeBakedModelBuilder extends BakedModelBuilder {
public final class ForgeBakedModelBuilder extends BakedModelBuilder {
@Nullable
private ModelData modelData;
@ -25,11 +30,36 @@ public class ForgeBakedModelBuilder extends BakedModelBuilder {
super(bakedModel);
}
@Override
public ForgeBakedModelBuilder level(BlockAndTintGetter level) {
super.level(level);
return this;
}
@Override
public ForgeBakedModelBuilder blockState(BlockState blockState) {
super.blockState(blockState);
return this;
}
@Override
public ForgeBakedModelBuilder poseStack(PoseStack poseStack) {
super.poseStack(poseStack);
return this;
}
@Override
public ForgeBakedModelBuilder materialFunc(BiFunction<RenderType, Boolean, Material> materialFunc) {
super.materialFunc(materialFunc);
return this;
}
public ForgeBakedModelBuilder modelData(ModelData modelData) {
this.modelData = modelData;
return this;
}
@Override
public SimpleModel build() {
if (level == null) {
level = VirtualEmptyBlockGetter.INSTANCE;
@ -37,16 +67,16 @@ public class ForgeBakedModelBuilder extends BakedModelBuilder {
if (blockState == null) {
blockState = Blocks.AIR.defaultBlockState();
}
if (modelData == null) {
modelData = ModelData.EMPTY;
}
if (materialFunc == null) {
materialFunc = ModelUtil::getMaterial;
}
if (modelData == null) {
modelData = ModelData.EMPTY;
}
var out = ImmutableList.<Model.ConfiguredMesh>builder();
ResultConsumer resultConsumer = (renderType, shaded, data) -> {
BakedModelBufferer.bufferSingle(ModelUtil.VANILLA_RENDERER.getModelRenderer(), level, bakedModel, blockState, poseStack, modelData, (renderType, shaded, data) -> {
Material material = materialFunc.apply(renderType, shaded);
if (material != null) {
VertexView vertexView = new NoOverlayVertexView();
@ -54,8 +84,7 @@ public class ForgeBakedModelBuilder extends BakedModelBuilder {
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(), level, bakedModel, blockState, poseStack, modelData, resultConsumer);
});
return new SimpleModel(out.build());
}

View file

@ -1,5 +1,7 @@
package com.jozufozu.flywheel.lib.model.baked;
import java.util.function.BiFunction;
import org.jetbrains.annotations.Nullable;
import com.google.common.collect.ImmutableList;
@ -10,13 +12,15 @@ 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.MeshEmitter.ResultConsumer;
import com.jozufozu.flywheel.lib.vertex.NoOverlayVertexView;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.world.level.BlockAndTintGetter;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraftforge.client.model.data.ModelData;
public class ForgeBlockModelBuilder extends BlockModelBuilder {
public final class ForgeBlockModelBuilder extends BlockModelBuilder {
@Nullable
private ModelData modelData;
@ -24,25 +28,44 @@ public class ForgeBlockModelBuilder extends BlockModelBuilder {
super(state);
}
@Override
public ForgeBlockModelBuilder level(BlockAndTintGetter level) {
super.level(level);
return this;
}
@Override
public ForgeBlockModelBuilder poseStack(PoseStack poseStack) {
super.poseStack(poseStack);
return this;
}
@Override
public ForgeBlockModelBuilder materialFunc(BiFunction<RenderType, Boolean, Material> materialFunc) {
super.materialFunc(materialFunc);
return this;
}
public ForgeBlockModelBuilder modelData(ModelData modelData) {
this.modelData = modelData;
return this;
}
@Override
public SimpleModel build() {
if (level == null) {
level = VirtualEmptyBlockGetter.INSTANCE;
}
if (modelData == null) {
modelData = ModelData.EMPTY;
}
if (materialFunc == null) {
materialFunc = ModelUtil::getMaterial;
}
if (modelData == null) {
modelData = ModelData.EMPTY;
}
var out = ImmutableList.<Model.ConfiguredMesh>builder();
ResultConsumer resultConsumer = (renderType, shaded, data) -> {
BakedModelBufferer.bufferBlock(ModelUtil.VANILLA_RENDERER, level, state, poseStack, modelData, (renderType, shaded, data) -> {
Material material = materialFunc.apply(renderType, shaded);
if (material != null) {
VertexView vertexView = new NoOverlayVertexView();
@ -50,8 +73,7 @@ public class ForgeBlockModelBuilder extends BlockModelBuilder {
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, level, state, poseStack, modelData, resultConsumer);
});
return new SimpleModel(out.build());
}

View file

@ -1,21 +0,0 @@
package com.jozufozu.flywheel.lib.model.baked;
import com.mojang.blaze3d.vertex.BufferBuilder;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.block.model.BakedQuad;
class ForgeMeshEmitter extends MeshEmitter {
ForgeMeshEmitter(BufferBuilder bufferBuilder, RenderType renderType) {
super(bufferBuilder, renderType);
}
// Forge has another putBulkData that we need to override
@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);
}
}

View file

@ -1,5 +1,6 @@
package com.jozufozu.flywheel.lib.model.baked;
import java.util.function.BiFunction;
import java.util.function.Function;
import org.jetbrains.annotations.Nullable;
@ -12,14 +13,15 @@ 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.MeshEmitter.ResultConsumer;
import com.jozufozu.flywheel.lib.vertex.NoOverlayVertexView;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.BlockAndTintGetter;
import net.minecraftforge.client.model.data.ModelData;
public class ForgeMultiBlockModelBuilder extends MultiBlockModelBuilder {
public final class ForgeMultiBlockModelBuilder extends MultiBlockModelBuilder {
@Nullable
private Function<BlockPos, ModelData> modelDataLookup;
@ -27,22 +29,41 @@ public class ForgeMultiBlockModelBuilder extends MultiBlockModelBuilder {
super(level, positions);
}
@Override
public ForgeMultiBlockModelBuilder poseStack(PoseStack poseStack) {
super.poseStack(poseStack);
return this;
}
@Override
public ForgeMultiBlockModelBuilder enableFluidRendering() {
super.enableFluidRendering();
return this;
}
@Override
public ForgeMultiBlockModelBuilder materialFunc(BiFunction<RenderType, Boolean, Material> materialFunc) {
super.materialFunc(materialFunc);
return this;
}
public ForgeMultiBlockModelBuilder modelDataLookup(Function<BlockPos, ModelData> modelDataLookup) {
this.modelDataLookup = modelDataLookup;
return this;
}
@Override
public SimpleModel build() {
if (modelDataLookup == null) {
modelDataLookup = pos -> ModelData.EMPTY;
}
if (materialFunc == null) {
materialFunc = ModelUtil::getMaterial;
}
if (modelDataLookup == null) {
modelDataLookup = pos -> ModelData.EMPTY;
}
var out = ImmutableList.<Model.ConfiguredMesh>builder();
ResultConsumer resultConsumer = (renderType, shaded, data) -> {
BakedModelBufferer.bufferMultiBlock(ModelUtil.VANILLA_RENDERER, positions.iterator(), level, poseStack, modelDataLookup, renderFluids, (renderType, shaded, data) -> {
Material material = materialFunc.apply(renderType, shaded);
if (material != null) {
VertexView vertexView = new NoOverlayVertexView();
@ -50,8 +71,7 @@ public class ForgeMultiBlockModelBuilder extends MultiBlockModelBuilder {
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(), level, poseStack, modelDataLookup, renderFluids, resultConsumer);
});
return new SimpleModel(out.build());
}

View file

@ -0,0 +1,133 @@
package com.jozufozu.flywheel.lib.model.baked;
import org.jetbrains.annotations.UnknownNullability;
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 RenderType renderType;
private final BufferBuilder bufferBuilder;
private BakedModelBufferer.@UnknownNullability ResultConsumer resultConsumer;
private boolean currentShade;
MeshEmitter(RenderType renderType) {
this.renderType = renderType;
this.bufferBuilder = new BufferBuilder(renderType.bufferSize());
}
public void prepare(BakedModelBufferer.ResultConsumer resultConsumer) {
this.resultConsumer = resultConsumer;
}
public void end() {
if (bufferBuilder.building()) {
emit();
}
resultConsumer = null;
}
public BufferBuilder unwrap(boolean shade) {
prepareForGeometry(shade);
return bufferBuilder;
}
private void prepareForGeometry(boolean shade) {
if (!bufferBuilder.building()) {
bufferBuilder.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.BLOCK);
} else if (shade != currentShade) {
emit();
}
currentShade = shade;
}
private void prepareForGeometry(BakedQuad quad) {
prepareForGeometry(quad.isShade());
}
private void emit() {
var renderedBuffer = bufferBuilder.endOrDiscardIfEmpty();
if (renderedBuffer != null) {
resultConsumer.accept(renderType, currentShade, renderedBuffer);
renderedBuffer.release();
}
}
@Override
public void putBulkData(PoseStack.Pose pose, BakedQuad quad, float red, float green, float blue, int light, int overlay) {
prepareForGeometry(quad);
bufferBuilder.putBulkData(pose, quad, red, green, blue, light, overlay);
}
@Override
public void putBulkData(PoseStack.Pose pose, BakedQuad quad, float red, float green, float blue, float alpha, int light, int overlay, boolean readExistingColor) {
prepareForGeometry(quad);
bufferBuilder.putBulkData(pose, quad, red, green, blue, alpha, light, overlay, readExistingColor);
}
@Override
public void putBulkData(PoseStack.Pose pose, BakedQuad quad, float[] brightnesses, float red, float green, float blue, int[] lights, int overlay, boolean readExistingColor) {
prepareForGeometry(quad);
bufferBuilder.putBulkData(pose, quad, brightnesses, red, green, blue, lights, overlay, readExistingColor);
}
@Override
public void putBulkData(PoseStack.Pose pose, BakedQuad quad, float[] brightnesses, float red, float green, float blue, float alpha, int[] lights, int overlay, boolean readExistingColor) {
prepareForGeometry(quad);
bufferBuilder.putBulkData(pose, quad, brightnesses, red, green, blue, alpha, lights, overlay, readExistingColor);
}
@Override
public VertexConsumer vertex(double x, double y, double z) {
throw new UnsupportedOperationException("MeshEmitter only supports putBulkData!");
}
@Override
public VertexConsumer color(int red, int green, int blue, int alpha) {
throw new UnsupportedOperationException("MeshEmitter only supports putBulkData!");
}
@Override
public VertexConsumer uv(float u, float v) {
throw new UnsupportedOperationException("MeshEmitter only supports putBulkData!");
}
@Override
public VertexConsumer overlayCoords(int u, int v) {
throw new UnsupportedOperationException("MeshEmitter only supports putBulkData!");
}
@Override
public VertexConsumer uv2(int u, int v) {
throw new UnsupportedOperationException("MeshEmitter only supports putBulkData!");
}
@Override
public VertexConsumer normal(float x, float y, float z) {
throw new UnsupportedOperationException("MeshEmitter only supports putBulkData!");
}
@Override
public void endVertex() {
throw new UnsupportedOperationException("MeshEmitter only supports putBulkData!");
}
@Override
public void defaultColor(int red, int green, int blue, int alpha) {
throw new UnsupportedOperationException("MeshEmitter only supports putBulkData!");
}
@Override
public void unsetDefaultColor() {
throw new UnsupportedOperationException("MeshEmitter only supports putBulkData!");
}
}