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.
This commit is contained in:
PepperCode1 2024-05-12 20:55:59 -07:00 committed by Jozufozu
parent 914ce0a7de
commit 7ef9ce3907
11 changed files with 136 additions and 31 deletions

View File

@ -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<ConfiguredMesh> meshes;
private final List<ConfiguredMesh> meshes;
private final Vector4fc boundingSphere;
public SimpleModel(ImmutableList<ConfiguredMesh> meshes) {
public SimpleModel(List<ConfiguredMesh> meshes) {
this.meshes = meshes;
this.boundingSphere = ModelUtil.computeBoundingSphere(meshes);
}

View File

@ -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<ThreadLocalObjects> THREAD_LOCAL_OBJECTS = ThreadLocal.withInitial(ThreadLocalObjects::new);

View File

@ -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<T> {
private static final ThreadLocal<ChunkLayerSortedListBuilder<?>> THREAD_LOCAL = ThreadLocal.withInitial(ChunkLayerSortedListBuilder::new);
@SuppressWarnings("unchecked")
private final ObjectArrayList<T>[] lists = new ObjectArrayList[BakedModelBufferer.CHUNK_LAYER_AMOUNT];
private final Reference2ReferenceMap<RenderType, ObjectArrayList<T>> map = new Reference2ReferenceOpenHashMap<>();
private ChunkLayerSortedListBuilder() {
for (int layerIndex = 0; layerIndex < BakedModelBufferer.CHUNK_LAYER_AMOUNT; layerIndex++) {
RenderType renderType = BakedModelBufferer.CHUNK_LAYERS[layerIndex];
ObjectArrayList<T> list = new ObjectArrayList<>();
lists[layerIndex] = list;
map.put(renderType, list);
}
}
@SuppressWarnings("unchecked")
public static <T> ChunkLayerSortedListBuilder<T> getThreadLocal() {
return (ChunkLayerSortedListBuilder<T>) THREAD_LOCAL.get();
}
public void add(RenderType renderType, T obj) {
List<T> list = map.get(renderType);
if (list == null) {
throw new IllegalArgumentException("RenderType '" + renderType + "' is not a chunk layer");
}
list.add(obj);
}
@SuppressWarnings("unchecked")
public ImmutableList<T> build() {
int size = 0;
for (ObjectArrayList<T> list : lists) {
size += list.size();
}
T[] array = (T[]) new Object[size];
int destPos = 0;
for (ObjectArrayList<T> list : lists) {
System.arraycopy(list.elements(), 0, array, destPos, list.size());
destPos += list.size();
list.clear();
}
return ImmutableList.copyOf(array);
}
}

View File

@ -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.<Model.ConfiguredMesh>builder();
var builder = ChunkLayerSortedListBuilder.<Model.ConfiguredMesh>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());
}
}

View File

@ -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.<Model.ConfiguredMesh>builder();
var builder = ChunkLayerSortedListBuilder.<Model.ConfiguredMesh>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());
}
}

View File

@ -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.<Model.ConfiguredMesh>builder();
var builder = ChunkLayerSortedListBuilder.<Model.ConfiguredMesh>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());
}
}

View File

@ -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<ThreadLocalObjects> THREAD_LOCAL_OBJECTS = ThreadLocal.withInitial(ThreadLocalObjects::new);

View File

@ -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<T> {
private static final ThreadLocal<ChunkLayerSortedListBuilder<?>> THREAD_LOCAL = ThreadLocal.withInitial(ChunkLayerSortedListBuilder::new);
@SuppressWarnings("unchecked")
private final ObjectArrayList<T>[] lists = new ObjectArrayList[BakedModelBufferer.CHUNK_LAYER_AMOUNT];
private ChunkLayerSortedListBuilder() {
for (int layerIndex = 0; layerIndex < BakedModelBufferer.CHUNK_LAYER_AMOUNT; layerIndex++) {
ObjectArrayList<T> list = new ObjectArrayList<>();
lists[layerIndex] = list;
}
}
@SuppressWarnings("unchecked")
public static <T> ChunkLayerSortedListBuilder<T> getThreadLocal() {
return (ChunkLayerSortedListBuilder<T>) 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<T> list = lists[layerIndex];
list.add(obj);
}
@SuppressWarnings("unchecked")
public ImmutableList<T> build() {
int size = 0;
for (ObjectArrayList<T> list : lists) {
size += list.size();
}
T[] array = (T[]) new Object[size];
int destPos = 0;
for (ObjectArrayList<T> list : lists) {
System.arraycopy(list.elements(), 0, array, destPos, list.size());
destPos += list.size();
list.clear();
}
return ImmutableList.copyOf(array);
}
}

View File

@ -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.<Model.ConfiguredMesh>builder();
var builder = ChunkLayerSortedListBuilder.<Model.ConfiguredMesh>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());
}
}

View File

@ -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.<Model.ConfiguredMesh>builder();
var builder = ChunkLayerSortedListBuilder.<Model.ConfiguredMesh>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());
}
}

View File

@ -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.<Model.ConfiguredMesh>builder();
var builder = ChunkLayerSortedListBuilder.<Model.ConfiguredMesh>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());
}
}