From 7ef9ce3907497ca6aeb5b5a1048f6684c8bf6fb2 Mon Sep 17 00:00:00 2001 From: PepperCode1 <44146161+PepperCode1@users.noreply.github.com> Date: Sun, 12 May 2024 20:55:59 -0700 Subject: [PATCH] Fix mesh order of models from model builders Meshes are now always sorted by chunk layer first, then in order of how the BakedModel returned quads. This should exactly match vanilla's chunk buffering and avoid any rendering issues. --- .../flywheel/lib/model/SimpleModel.java | 5 +- .../lib/model/baked/BakedModelBufferer.java | 4 +- .../baked/ChunkLayerSortedListBuilder.java | 58 +++++++++++++++++++ .../model/baked/FabricBakedModelBuilder.java | 7 +-- .../model/baked/FabricBlockModelBuilder.java | 7 +-- .../baked/FabricMultiBlockModelBuilder.java | 7 +-- .../lib/model/baked/BakedModelBufferer.java | 4 +- .../baked/ChunkLayerSortedListBuilder.java | 54 +++++++++++++++++ .../model/baked/ForgeBakedModelBuilder.java | 7 +-- .../model/baked/ForgeBlockModelBuilder.java | 7 +-- .../baked/ForgeMultiBlockModelBuilder.java | 7 +-- 11 files changed, 136 insertions(+), 31 deletions(-) create mode 100644 fabric/src/lib/java/com/jozufozu/flywheel/lib/model/baked/ChunkLayerSortedListBuilder.java create mode 100644 forge/src/lib/java/com/jozufozu/flywheel/lib/model/baked/ChunkLayerSortedListBuilder.java diff --git a/common/src/lib/java/com/jozufozu/flywheel/lib/model/SimpleModel.java b/common/src/lib/java/com/jozufozu/flywheel/lib/model/SimpleModel.java index 3f54484ed..bd5b7bb45 100644 --- a/common/src/lib/java/com/jozufozu/flywheel/lib/model/SimpleModel.java +++ b/common/src/lib/java/com/jozufozu/flywheel/lib/model/SimpleModel.java @@ -4,14 +4,13 @@ import java.util.List; import org.joml.Vector4fc; -import com.google.common.collect.ImmutableList; import com.jozufozu.flywheel.api.model.Model; public class SimpleModel implements Model { - private final ImmutableList meshes; + private final List meshes; private final Vector4fc boundingSphere; - public SimpleModel(ImmutableList meshes) { + public SimpleModel(List meshes) { this.meshes = meshes; this.boundingSphere = ModelUtil.computeBoundingSphere(meshes); } 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 index 39b9fb9eb..a0eb2302d 100644 --- 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 @@ -23,8 +23,8 @@ 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; + static final RenderType[] CHUNK_LAYERS = RenderType.chunkBufferLayers().toArray(RenderType[]::new); + static final int CHUNK_LAYER_AMOUNT = CHUNK_LAYERS.length; private static final ThreadLocal THREAD_LOCAL_OBJECTS = ThreadLocal.withInitial(ThreadLocalObjects::new); diff --git a/fabric/src/lib/java/com/jozufozu/flywheel/lib/model/baked/ChunkLayerSortedListBuilder.java b/fabric/src/lib/java/com/jozufozu/flywheel/lib/model/baked/ChunkLayerSortedListBuilder.java new file mode 100644 index 000000000..5978d7729 --- /dev/null +++ b/fabric/src/lib/java/com/jozufozu/flywheel/lib/model/baked/ChunkLayerSortedListBuilder.java @@ -0,0 +1,58 @@ +package com.jozufozu.flywheel.lib.model.baked; + +import java.util.List; + +import com.google.common.collect.ImmutableList; + +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import it.unimi.dsi.fastutil.objects.Reference2ReferenceMap; +import it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap; +import net.minecraft.client.renderer.RenderType; + +class ChunkLayerSortedListBuilder { + private static final ThreadLocal> THREAD_LOCAL = ThreadLocal.withInitial(ChunkLayerSortedListBuilder::new); + + @SuppressWarnings("unchecked") + private final ObjectArrayList[] lists = new ObjectArrayList[BakedModelBufferer.CHUNK_LAYER_AMOUNT]; + private final Reference2ReferenceMap> map = new Reference2ReferenceOpenHashMap<>(); + + private ChunkLayerSortedListBuilder() { + for (int layerIndex = 0; layerIndex < BakedModelBufferer.CHUNK_LAYER_AMOUNT; layerIndex++) { + RenderType renderType = BakedModelBufferer.CHUNK_LAYERS[layerIndex]; + ObjectArrayList list = new ObjectArrayList<>(); + lists[layerIndex] = list; + map.put(renderType, list); + } + } + + @SuppressWarnings("unchecked") + public static ChunkLayerSortedListBuilder getThreadLocal() { + return (ChunkLayerSortedListBuilder) THREAD_LOCAL.get(); + } + + public void add(RenderType renderType, T obj) { + List list = map.get(renderType); + if (list == null) { + throw new IllegalArgumentException("RenderType '" + renderType + "' is not a chunk layer"); + } + list.add(obj); + } + + @SuppressWarnings("unchecked") + public ImmutableList build() { + int size = 0; + for (ObjectArrayList list : lists) { + size += list.size(); + } + + T[] array = (T[]) new Object[size]; + int destPos = 0; + for (ObjectArrayList list : lists) { + System.arraycopy(list.elements(), 0, array, destPos, list.size()); + destPos += list.size(); + list.clear(); + } + + return ImmutableList.copyOf(array); + } +} 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 index a4b47d019..f853ac884 100644 --- 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 @@ -2,7 +2,6 @@ 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; @@ -60,7 +59,7 @@ public final class FabricBakedModelBuilder extends BakedModelBuilder { materialFunc = ModelUtil::getMaterial; } - var out = ImmutableList.builder(); + var builder = ChunkLayerSortedListBuilder.getThreadLocal(); BakedModelBufferer.bufferSingle(ModelUtil.VANILLA_RENDERER.getModelRenderer(), level, bakedModel, blockState, poseStack, (renderType, shaded, data) -> { Material material = materialFunc.apply(renderType, shaded); @@ -68,10 +67,10 @@ public final class FabricBakedModelBuilder extends BakedModelBuilder { 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)); + builder.add(renderType, new Model.ConfiguredMesh(material, mesh)); } }); - return new SimpleModel(out.build()); + return new SimpleModel(builder.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 index bfc2ab0ca..a64144135 100644 --- 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 @@ -2,7 +2,6 @@ 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; @@ -49,7 +48,7 @@ public final class FabricBlockModelBuilder extends BlockModelBuilder { materialFunc = ModelUtil::getMaterial; } - var out = ImmutableList.builder(); + var builder = ChunkLayerSortedListBuilder.getThreadLocal(); BakedModelBufferer.bufferBlock(ModelUtil.VANILLA_RENDERER, level, state, poseStack, (renderType, shaded, data) -> { Material material = materialFunc.apply(renderType, shaded); @@ -57,10 +56,10 @@ public final class FabricBlockModelBuilder extends BlockModelBuilder { 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)); + builder.add(renderType, new Model.ConfiguredMesh(material, mesh)); } }); - return new SimpleModel(out.build()); + return new SimpleModel(builder.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 index d3dbf408c..74a4f29f1 100644 --- 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 @@ -2,7 +2,6 @@ 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; @@ -46,7 +45,7 @@ public final class FabricMultiBlockModelBuilder extends MultiBlockModelBuilder { materialFunc = ModelUtil::getMaterial; } - var out = ImmutableList.builder(); + var builder = ChunkLayerSortedListBuilder.getThreadLocal(); BakedModelBufferer.bufferMultiBlock(ModelUtil.VANILLA_RENDERER, positions.iterator(), level, poseStack, renderFluids, (renderType, shaded, data) -> { Material material = materialFunc.apply(renderType, shaded); @@ -54,10 +53,10 @@ public final class FabricMultiBlockModelBuilder extends MultiBlockModelBuilder { 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)); + builder.add(renderType, new Model.ConfiguredMesh(material, mesh)); } }); - return new SimpleModel(out.build()); + return new SimpleModel(builder.build()); } } 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 cb6882a4d..d62de8009 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 @@ -24,8 +24,8 @@ import net.minecraftforge.client.ChunkRenderTypeSet; import net.minecraftforge.client.model.data.ModelData; final class BakedModelBufferer { - private static final RenderType[] CHUNK_LAYERS = RenderType.chunkBufferLayers().toArray(RenderType[]::new); - private static final int CHUNK_LAYER_AMOUNT = CHUNK_LAYERS.length; + static final RenderType[] CHUNK_LAYERS = RenderType.chunkBufferLayers().toArray(RenderType[]::new); + static final int CHUNK_LAYER_AMOUNT = CHUNK_LAYERS.length; private static final ThreadLocal THREAD_LOCAL_OBJECTS = ThreadLocal.withInitial(ThreadLocalObjects::new); diff --git a/forge/src/lib/java/com/jozufozu/flywheel/lib/model/baked/ChunkLayerSortedListBuilder.java b/forge/src/lib/java/com/jozufozu/flywheel/lib/model/baked/ChunkLayerSortedListBuilder.java new file mode 100644 index 000000000..01890f7c7 --- /dev/null +++ b/forge/src/lib/java/com/jozufozu/flywheel/lib/model/baked/ChunkLayerSortedListBuilder.java @@ -0,0 +1,54 @@ +package com.jozufozu.flywheel.lib.model.baked; + +import java.util.List; + +import com.google.common.collect.ImmutableList; + +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import net.minecraft.client.renderer.RenderType; + +class ChunkLayerSortedListBuilder { + private static final ThreadLocal> THREAD_LOCAL = ThreadLocal.withInitial(ChunkLayerSortedListBuilder::new); + + @SuppressWarnings("unchecked") + private final ObjectArrayList[] lists = new ObjectArrayList[BakedModelBufferer.CHUNK_LAYER_AMOUNT]; + + private ChunkLayerSortedListBuilder() { + for (int layerIndex = 0; layerIndex < BakedModelBufferer.CHUNK_LAYER_AMOUNT; layerIndex++) { + ObjectArrayList list = new ObjectArrayList<>(); + lists[layerIndex] = list; + } + } + + @SuppressWarnings("unchecked") + public static ChunkLayerSortedListBuilder getThreadLocal() { + return (ChunkLayerSortedListBuilder) THREAD_LOCAL.get(); + } + + public void add(RenderType renderType, T obj) { + int layerIndex = renderType.getChunkLayerId(); + if (layerIndex == -1) { + throw new IllegalArgumentException("RenderType '" + renderType + "' is not a chunk layer"); + } + List list = lists[layerIndex]; + list.add(obj); + } + + @SuppressWarnings("unchecked") + public ImmutableList build() { + int size = 0; + for (ObjectArrayList list : lists) { + size += list.size(); + } + + T[] array = (T[]) new Object[size]; + int destPos = 0; + for (ObjectArrayList list : lists) { + System.arraycopy(list.elements(), 0, array, destPos, list.size()); + destPos += list.size(); + list.clear(); + } + + return ImmutableList.copyOf(array); + } +} 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 8ad0e0404..515def783 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 @@ -4,7 +4,6 @@ import java.util.function.BiFunction; import org.jetbrains.annotations.Nullable; -import com.google.common.collect.ImmutableList; import com.jozufozu.flywheel.api.material.Material; import com.jozufozu.flywheel.api.model.Model; import com.jozufozu.flywheel.api.vertex.VertexView; @@ -74,7 +73,7 @@ public final class ForgeBakedModelBuilder extends BakedModelBuilder { modelData = ModelData.EMPTY; } - var out = ImmutableList.builder(); + var builder = ChunkLayerSortedListBuilder.getThreadLocal(); BakedModelBufferer.bufferSingle(ModelUtil.VANILLA_RENDERER.getModelRenderer(), level, bakedModel, blockState, poseStack, modelData, (renderType, shaded, data) -> { Material material = materialFunc.apply(renderType, shaded); @@ -82,10 +81,10 @@ public final class ForgeBakedModelBuilder extends BakedModelBuilder { 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)); + builder.add(renderType, new Model.ConfiguredMesh(material, mesh)); } }); - return new SimpleModel(out.build()); + return new SimpleModel(builder.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 af06eadc9..ad385e40c 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 @@ -4,7 +4,6 @@ import java.util.function.BiFunction; import org.jetbrains.annotations.Nullable; -import com.google.common.collect.ImmutableList; import com.jozufozu.flywheel.api.material.Material; import com.jozufozu.flywheel.api.model.Model; import com.jozufozu.flywheel.api.vertex.VertexView; @@ -63,7 +62,7 @@ public final class ForgeBlockModelBuilder extends BlockModelBuilder { modelData = ModelData.EMPTY; } - var out = ImmutableList.builder(); + var builder = ChunkLayerSortedListBuilder.getThreadLocal(); BakedModelBufferer.bufferBlock(ModelUtil.VANILLA_RENDERER, level, state, poseStack, modelData, (renderType, shaded, data) -> { Material material = materialFunc.apply(renderType, shaded); @@ -71,10 +70,10 @@ public final class ForgeBlockModelBuilder extends BlockModelBuilder { 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)); + builder.add(renderType, new Model.ConfiguredMesh(material, mesh)); } }); - return new SimpleModel(out.build()); + return new SimpleModel(builder.build()); } } 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 48c3363b4..d84379598 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 @@ -5,7 +5,6 @@ import java.util.function.Function; import org.jetbrains.annotations.Nullable; -import com.google.common.collect.ImmutableList; import com.jozufozu.flywheel.api.material.Material; import com.jozufozu.flywheel.api.model.Model; import com.jozufozu.flywheel.api.vertex.VertexView; @@ -61,7 +60,7 @@ public final class ForgeMultiBlockModelBuilder extends MultiBlockModelBuilder { modelDataLookup = pos -> ModelData.EMPTY; } - var out = ImmutableList.builder(); + var builder = ChunkLayerSortedListBuilder.getThreadLocal(); BakedModelBufferer.bufferMultiBlock(ModelUtil.VANILLA_RENDERER, positions.iterator(), level, poseStack, modelDataLookup, renderFluids, (renderType, shaded, data) -> { Material material = materialFunc.apply(renderType, shaded); @@ -69,10 +68,10 @@ public final class ForgeMultiBlockModelBuilder extends MultiBlockModelBuilder { 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)); + builder.add(renderType, new Model.ConfiguredMesh(material, mesh)); } }); - return new SimpleModel(out.build()); + return new SimpleModel(builder.build()); } }