From b9490fe11a53b4109b021a32ad3af9f834ddeaa5 Mon Sep 17 00:00:00 2001 From: PepperCode1 <44146161+PepperCode1@users.noreply.github.com> Date: Fri, 26 Apr 2024 22:31:29 -0700 Subject: [PATCH] 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 --- .../lib/model/baked/BakedModelBuilder.java | 20 ++- .../lib/model/baked/BlockModelBuilder.java | 18 +- .../flywheel/lib/model/baked/MeshEmitter.java | 119 -------------- .../model/baked/MultiBlockModelBuilder.java | 22 +-- .../baked/TransformingVertexConsumer.java | 3 + .../impl/mixin/ClientChunkCacheMixin.java | 2 +- .../lib/model/baked/BakedModelBufferer.java | 154 ++++++++++++++++++ .../model/baked/FabricBakedModelBuilder.java | 77 +++++++++ .../model/baked/FabricBlockModelBuilder.java | 66 ++++++++ .../baked/FabricMultiBlockModelBuilder.java | 63 +++++++ .../flywheel/lib/model/baked/MeshEmitter.java | 57 +++++++ .../lib/model/baked/UniversalMeshEmitter.java | 146 +++++++++++++++++ .../flywheel/impl/FlwLibXplatImpl.java | 9 +- .../lib/model/baked/BakedModelBufferer.java | 43 +++-- .../model/baked/ForgeBakedModelBuilder.java | 45 ++++- .../model/baked/ForgeBlockModelBuilder.java | 38 ++++- .../lib/model/baked/ForgeMeshEmitter.java | 21 --- .../baked/ForgeMultiBlockModelBuilder.java | 36 +++- .../flywheel/lib/model/baked/MeshEmitter.java | 133 +++++++++++++++ 19 files changed, 853 insertions(+), 219 deletions(-) delete mode 100644 common/src/lib/java/com/jozufozu/flywheel/lib/model/baked/MeshEmitter.java create mode 100644 fabric/src/lib/java/com/jozufozu/flywheel/lib/model/baked/BakedModelBufferer.java create mode 100644 fabric/src/lib/java/com/jozufozu/flywheel/lib/model/baked/FabricBakedModelBuilder.java create mode 100644 fabric/src/lib/java/com/jozufozu/flywheel/lib/model/baked/FabricBlockModelBuilder.java create mode 100644 fabric/src/lib/java/com/jozufozu/flywheel/lib/model/baked/FabricMultiBlockModelBuilder.java create mode 100644 fabric/src/lib/java/com/jozufozu/flywheel/lib/model/baked/MeshEmitter.java create mode 100644 fabric/src/lib/java/com/jozufozu/flywheel/lib/model/baked/UniversalMeshEmitter.java delete mode 100644 forge/src/lib/java/com/jozufozu/flywheel/lib/model/baked/ForgeMeshEmitter.java create mode 100644 forge/src/lib/java/com/jozufozu/flywheel/lib/model/baked/MeshEmitter.java diff --git a/common/src/lib/java/com/jozufozu/flywheel/lib/model/baked/BakedModelBuilder.java b/common/src/lib/java/com/jozufozu/flywheel/lib/model/baked/BakedModelBuilder.java index 528dea4bb..db51497b0 100644 --- a/common/src/lib/java/com/jozufozu/flywheel/lib/model/baked/BakedModelBuilder.java +++ b/common/src/lib/java/com/jozufozu/flywheel/lib/model/baked/BakedModelBuilder.java @@ -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 materialFunc; + BiFunction 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; diff --git a/common/src/lib/java/com/jozufozu/flywheel/lib/model/baked/BlockModelBuilder.java b/common/src/lib/java/com/jozufozu/flywheel/lib/model/baked/BlockModelBuilder.java index 0c2b44d85..aab97fdfe 100644 --- a/common/src/lib/java/com/jozufozu/flywheel/lib/model/baked/BlockModelBuilder.java +++ b/common/src/lib/java/com/jozufozu/flywheel/lib/model/baked/BlockModelBuilder.java @@ -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 materialFunc; + BiFunction 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; diff --git a/common/src/lib/java/com/jozufozu/flywheel/lib/model/baked/MeshEmitter.java b/common/src/lib/java/com/jozufozu/flywheel/lib/model/baked/MeshEmitter.java deleted file mode 100644 index b8d5be399..000000000 --- a/common/src/lib/java/com/jozufozu/flywheel/lib/model/baked/MeshEmitter.java +++ /dev/null @@ -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); - } -} diff --git a/common/src/lib/java/com/jozufozu/flywheel/lib/model/baked/MultiBlockModelBuilder.java b/common/src/lib/java/com/jozufozu/flywheel/lib/model/baked/MultiBlockModelBuilder.java index c750f5950..fdf9db403 100644 --- a/common/src/lib/java/com/jozufozu/flywheel/lib/model/baked/MultiBlockModelBuilder.java +++ b/common/src/lib/java/com/jozufozu/flywheel/lib/model/baked/MultiBlockModelBuilder.java @@ -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 positions; + final BlockAndTintGetter level; + final Iterable positions; @Nullable - protected PoseStack poseStack; - protected boolean renderFluids = false; + PoseStack poseStack; + boolean renderFluids = false; @Nullable - protected BiFunction materialFunc; + BiFunction materialFunc; + + MultiBlockModelBuilder(BlockAndTintGetter level, Iterable positions) { + this.level = level; + this.positions = positions; + } public static MultiBlockModelBuilder create(BlockAndTintGetter level, Iterable positions) { return FlwLibXplat.INSTANCE.createMultiBlockModelBuilder(level, positions); } - protected MultiBlockModelBuilder(BlockAndTintGetter level, Iterable positions) { - this.level = level; - this.positions = positions; - } - public MultiBlockModelBuilder poseStack(PoseStack poseStack) { this.poseStack = poseStack; return this; diff --git a/common/src/lib/java/com/jozufozu/flywheel/lib/model/baked/TransformingVertexConsumer.java b/common/src/lib/java/com/jozufozu/flywheel/lib/model/baked/TransformingVertexConsumer.java index 09054e2b4..42f7c9f93 100644 --- a/common/src/lib/java/com/jozufozu/flywheel/lib/model/baked/TransformingVertexConsumer.java +++ b/common/src/lib/java/com/jozufozu/flywheel/lib/model/baked/TransformingVertexConsumer.java @@ -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) { diff --git a/common/src/main/java/com/jozufozu/flywheel/impl/mixin/ClientChunkCacheMixin.java b/common/src/main/java/com/jozufozu/flywheel/impl/mixin/ClientChunkCacheMixin.java index 4cf130204..6f032bd86 100644 --- a/common/src/main/java/com/jozufozu/flywheel/impl/mixin/ClientChunkCacheMixin.java +++ b/common/src/main/java/com/jozufozu/flywheel/impl/mixin/ClientChunkCacheMixin.java @@ -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; diff --git a/fabric/src/lib/java/com/jozufozu/flywheel/lib/model/baked/BakedModelBufferer.java b/fabric/src/lib/java/com/jozufozu/flywheel/lib/model/baked/BakedModelBufferer.java new file mode 100644 index 000000000..39b9fb9eb --- /dev/null +++ b/fabric/src/lib/java/com/jozufozu/flywheel/lib/model/baked/BakedModelBufferer.java @@ -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 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 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 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 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); + } + } +} diff --git a/fabric/src/lib/java/com/jozufozu/flywheel/lib/model/baked/FabricBakedModelBuilder.java b/fabric/src/lib/java/com/jozufozu/flywheel/lib/model/baked/FabricBakedModelBuilder.java new file mode 100644 index 000000000..a4b47d019 --- /dev/null +++ b/fabric/src/lib/java/com/jozufozu/flywheel/lib/model/baked/FabricBakedModelBuilder.java @@ -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 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.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()); + } +} diff --git a/fabric/src/lib/java/com/jozufozu/flywheel/lib/model/baked/FabricBlockModelBuilder.java b/fabric/src/lib/java/com/jozufozu/flywheel/lib/model/baked/FabricBlockModelBuilder.java new file mode 100644 index 000000000..bfc2ab0ca --- /dev/null +++ b/fabric/src/lib/java/com/jozufozu/flywheel/lib/model/baked/FabricBlockModelBuilder.java @@ -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 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.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()); + } +} diff --git a/fabric/src/lib/java/com/jozufozu/flywheel/lib/model/baked/FabricMultiBlockModelBuilder.java b/fabric/src/lib/java/com/jozufozu/flywheel/lib/model/baked/FabricMultiBlockModelBuilder.java new file mode 100644 index 000000000..d3dbf408c --- /dev/null +++ b/fabric/src/lib/java/com/jozufozu/flywheel/lib/model/baked/FabricMultiBlockModelBuilder.java @@ -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 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 materialFunc) { + super.materialFunc(materialFunc); + return this; + } + + @Override + public SimpleModel build() { + if (materialFunc == null) { + materialFunc = ModelUtil::getMaterial; + } + + var out = ImmutableList.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()); + } +} diff --git a/fabric/src/lib/java/com/jozufozu/flywheel/lib/model/baked/MeshEmitter.java b/fabric/src/lib/java/com/jozufozu/flywheel/lib/model/baked/MeshEmitter.java new file mode 100644 index 000000000..c443807af --- /dev/null +++ b/fabric/src/lib/java/com/jozufozu/flywheel/lib/model/baked/MeshEmitter.java @@ -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(); + } + } +} diff --git a/fabric/src/lib/java/com/jozufozu/flywheel/lib/model/baked/UniversalMeshEmitter.java b/fabric/src/lib/java/com/jozufozu/flywheel/lib/model/baked/UniversalMeshEmitter.java new file mode 100644 index 000000000..d56633513 --- /dev/null +++ b/fabric/src/lib/java/com/jozufozu/flywheel/lib/model/baked/UniversalMeshEmitter.java @@ -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 emitterMap; + private final WrapperModel wrapperModel = new WrapperModel(); + + @UnknownNullability + private RenderType defaultLayer; + @UnknownNullability + private BufferBuilder currentDelegate; + + UniversalMeshEmitter(Reference2ReferenceMap 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 randomSupplier, RenderContext context) { + context.pushTransform(quadTransform); + super.emitBlockQuads(level, state, pos, randomSupplier, context); + context.popTransform(); + } + } +} diff --git a/fabric/src/main/java/com/jozufozu/flywheel/impl/FlwLibXplatImpl.java b/fabric/src/main/java/com/jozufozu/flywheel/impl/FlwLibXplatImpl.java index 908dc3a5d..932a458ac 100644 --- a/fabric/src/main/java/com/jozufozu/flywheel/impl/FlwLibXplatImpl.java +++ b/fabric/src/main/java/com/jozufozu/flywheel/impl/FlwLibXplatImpl.java @@ -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 positions) { - return null; + return new FabricMultiBlockModelBuilder(level, positions); } @Override diff --git a/forge/src/lib/java/com/jozufozu/flywheel/lib/model/baked/BakedModelBufferer.java b/forge/src/lib/java/com/jozufozu/flywheel/lib/model/baked/BakedModelBufferer.java index c454aaf52..cb6882a4d 100644 --- a/forge/src/lib/java/com/jozufozu/flywheel/lib/model/baked/BakedModelBufferer.java +++ b/forge/src/lib/java/com/jozufozu/flywheel/lib/model/baked/BakedModelBufferer.java @@ -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 posIterator, BlockAndTintGetter level, @Nullable PoseStack poseStack, Function modelDataLookup, boolean renderFluids, MeshEmitter.ResultConsumer resultConsumer) { + public static void bufferMultiBlock(BlockRenderDispatcher renderDispatcher, Iterator posIterator, BlockAndTintGetter level, @Nullable PoseStack poseStack, Function 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); } } } diff --git a/forge/src/lib/java/com/jozufozu/flywheel/lib/model/baked/ForgeBakedModelBuilder.java b/forge/src/lib/java/com/jozufozu/flywheel/lib/model/baked/ForgeBakedModelBuilder.java index e313a8aa7..8ad0e0404 100644 --- a/forge/src/lib/java/com/jozufozu/flywheel/lib/model/baked/ForgeBakedModelBuilder.java +++ b/forge/src/lib/java/com/jozufozu/flywheel/lib/model/baked/ForgeBakedModelBuilder.java @@ -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 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.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()); } diff --git a/forge/src/lib/java/com/jozufozu/flywheel/lib/model/baked/ForgeBlockModelBuilder.java b/forge/src/lib/java/com/jozufozu/flywheel/lib/model/baked/ForgeBlockModelBuilder.java index 017480a30..af06eadc9 100644 --- a/forge/src/lib/java/com/jozufozu/flywheel/lib/model/baked/ForgeBlockModelBuilder.java +++ b/forge/src/lib/java/com/jozufozu/flywheel/lib/model/baked/ForgeBlockModelBuilder.java @@ -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 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.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()); } diff --git a/forge/src/lib/java/com/jozufozu/flywheel/lib/model/baked/ForgeMeshEmitter.java b/forge/src/lib/java/com/jozufozu/flywheel/lib/model/baked/ForgeMeshEmitter.java deleted file mode 100644 index ffebddce2..000000000 --- a/forge/src/lib/java/com/jozufozu/flywheel/lib/model/baked/ForgeMeshEmitter.java +++ /dev/null @@ -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); - } -} diff --git a/forge/src/lib/java/com/jozufozu/flywheel/lib/model/baked/ForgeMultiBlockModelBuilder.java b/forge/src/lib/java/com/jozufozu/flywheel/lib/model/baked/ForgeMultiBlockModelBuilder.java index bc4bc05cc..48c3363b4 100644 --- a/forge/src/lib/java/com/jozufozu/flywheel/lib/model/baked/ForgeMultiBlockModelBuilder.java +++ b/forge/src/lib/java/com/jozufozu/flywheel/lib/model/baked/ForgeMultiBlockModelBuilder.java @@ -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 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 materialFunc) { + super.materialFunc(materialFunc); + return this; + } + public ForgeMultiBlockModelBuilder modelDataLookup(Function 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.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()); } diff --git a/forge/src/lib/java/com/jozufozu/flywheel/lib/model/baked/MeshEmitter.java b/forge/src/lib/java/com/jozufozu/flywheel/lib/model/baked/MeshEmitter.java new file mode 100644 index 000000000..41e36bbdb --- /dev/null +++ b/forge/src/lib/java/com/jozufozu/flywheel/lib/model/baked/MeshEmitter.java @@ -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!"); + } +}