diff --git a/src/main/java/com/jozufozu/flywheel/backend/engine/DrawManager.java b/src/main/java/com/jozufozu/flywheel/backend/engine/DrawManager.java index 78f5aa65e..c003a9264 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/engine/DrawManager.java +++ b/src/main/java/com/jozufozu/flywheel/backend/engine/DrawManager.java @@ -4,7 +4,9 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Queue; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedQueue; import com.jozufozu.flywheel.Flywheel; import com.jozufozu.flywheel.api.backend.Engine; @@ -32,7 +34,7 @@ public abstract class DrawManager> { *
* All new instancers land here before having resources allocated in {@link #flush}. */ - protected final List> initializationQueue = new ArrayList<>(); + protected final Queue> initializationQueue = new ConcurrentLinkedQueue<>(); @SuppressWarnings("unchecked") public Instancer getInstancer(Environment environment, InstanceType type, Model model, RenderStage stage) { @@ -72,7 +74,8 @@ public abstract class DrawManager> { // Only queue the instancer for initialization if it has anything to render. if (checkAndWarnEmptyModel(key.model())) { // Thread safety: this method is called atomically from within computeIfAbsent, - // so we don't need extra synchronization to protect the queue. + // so you'd think we don't need extra synchronization to protect the queue, but + // somehow threads can race here and wind up never initializing an instancer. initializationQueue.add(new UninitializedInstancer<>(key, out)); } return out; diff --git a/src/main/java/com/jozufozu/flywheel/backend/engine/uniform/FrameUniforms.java b/src/main/java/com/jozufozu/flywheel/backend/engine/uniform/FrameUniforms.java index e3ceeefe0..09f46ef8b 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/engine/uniform/FrameUniforms.java +++ b/src/main/java/com/jozufozu/flywheel/backend/engine/uniform/FrameUniforms.java @@ -19,7 +19,7 @@ import net.minecraft.world.level.Level; import net.minecraft.world.phys.Vec3; public class FrameUniforms implements UniformProvider { - public static final int SIZE = 800; + public static final int SIZE = 808; public int debugMode; @Nullable diff --git a/src/main/java/com/jozufozu/flywheel/lib/model/baked/BakedModelBufferer.java b/src/main/java/com/jozufozu/flywheel/lib/model/baked/BakedModelBufferer.java index 8ffec312d..ef0ae8a38 100644 --- a/src/main/java/com/jozufozu/flywheel/lib/model/baked/BakedModelBufferer.java +++ b/src/main/java/com/jozufozu/flywheel/lib/model/baked/BakedModelBufferer.java @@ -1,8 +1,6 @@ package com.jozufozu.flywheel.lib.model.baked; -import java.util.HashMap; import java.util.Iterator; -import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; import java.util.function.Function; @@ -14,6 +12,8 @@ import com.mojang.blaze3d.vertex.BufferBuilder.RenderedBuffer; import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.VertexConsumer; +import it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap; +import it.unimi.dsi.fastutil.objects.ReferenceArraySet; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.ItemBlockRenderTypes; import net.minecraft.client.renderer.MultiBufferSource; @@ -150,10 +150,9 @@ final class BakedModelBufferer { var emitterSource = objects.emitterSource; emitterSource.resultConsumer(consumer); - var itemRenderer = Minecraft.getInstance() - .getItemRenderer(); - - itemRenderer.render(stack, displayContext, leftHand, poseStack, emitterSource, 0, OverlayTexture.NO_OVERLAY, model); + Minecraft.getInstance() + .getItemRenderer() + .render(stack, displayContext, leftHand, poseStack, emitterSource, 0, OverlayTexture.NO_OVERLAY, model); emitterSource.end(); } @@ -182,11 +181,8 @@ final class BakedModelBufferer { } private static class MeshEmitterSource implements MultiBufferSource { - private final Map emitters = new HashMap<>(); - - private final Set active = new LinkedHashSet<>(); - // Hack: we want glint to render after everything else so track it separately here - private final Set activeGlint = new LinkedHashSet<>(); + private final Map emitters = new Reference2ReferenceOpenHashMap<>(); + private final Set active = new ReferenceArraySet<>(); @Nullable private ResultConsumer resultConsumer; @@ -195,13 +191,6 @@ final class BakedModelBufferer { public VertexConsumer getBuffer(RenderType renderType) { var out = emitters.computeIfAbsent(renderType, type -> new MeshEmitter(new BufferBuilder(type.bufferSize()), type)); - Set active; - if (renderType == RenderType.glint() || renderType == RenderType.glintDirect() || renderType == RenderType.entityGlint() || renderType == RenderType.entityGlintDirect()) { - active = this.activeGlint; - } else { - active = this.active; - } - if (active.add(out)) { out.begin(resultConsumer); } @@ -214,12 +203,7 @@ final class BakedModelBufferer { emitter.end(); } - for (var emitter : activeGlint) { - emitter.end(); - } - active.clear(); - activeGlint.clear(); resultConsumer = null; } diff --git a/src/main/java/com/jozufozu/flywheel/lib/model/baked/ItemModelBuilder.java b/src/main/java/com/jozufozu/flywheel/lib/model/baked/ItemModelBuilder.java index ca83f1290..c536a41d8 100644 --- a/src/main/java/com/jozufozu/flywheel/lib/model/baked/ItemModelBuilder.java +++ b/src/main/java/com/jozufozu/flywheel/lib/model/baked/ItemModelBuilder.java @@ -1,11 +1,14 @@ package com.jozufozu.flywheel.lib.model.baked; +import java.util.ArrayList; +import java.util.Comparator; 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.material.Transparency; import com.jozufozu.flywheel.api.model.Model; import com.jozufozu.flywheel.api.vertex.VertexView; import com.jozufozu.flywheel.lib.memory.MemoryBlock; @@ -16,24 +19,35 @@ import com.jozufozu.flywheel.lib.model.baked.BakedModelBufferer.ResultConsumer; import com.jozufozu.flywheel.lib.vertex.NoOverlayVertexView; import com.mojang.blaze3d.vertex.PoseStack; -import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.RenderType; +import net.minecraft.client.resources.model.BakedModel; import net.minecraft.world.item.ItemDisplayContext; import net.minecraft.world.item.ItemStack; public class ItemModelBuilder { + public static final Comparator GLINT_LAST = (a, b) -> { + if (a.material() + .transparency() == b.material() + .transparency()) { + return 0; + } + return a.material() + .transparency() == Transparency.GLINT ? 1 : -1; + }; + private final ItemStack itemStack; + private final BakedModel model; @Nullable private PoseStack poseStack; @Nullable private ItemDisplayContext displayContext; private boolean leftHand; - private int seed = 42; @Nullable private BiFunction materialFunc; - public ItemModelBuilder(ItemStack itemStack) { + public ItemModelBuilder(ItemStack itemStack, BakedModel model) { this.itemStack = itemStack; + this.model = model; } public ItemModelBuilder poseStack(PoseStack poseStack) { @@ -51,11 +65,6 @@ public class ItemModelBuilder { return this; } - public ItemModelBuilder seed(int seed) { - this.seed = seed; - return this; - } - public ItemModelBuilder materialFunc(BiFunction materialFunc) { this.materialFunc = materialFunc; return this; @@ -69,7 +78,7 @@ public class ItemModelBuilder { materialFunc = ModelUtil::getItemMaterial; } - var out = ImmutableList.builder(); + ArrayList out = new ArrayList<>(); ResultConsumer resultConsumer = (renderType, shaded, data) -> { Material material = materialFunc.apply(renderType, shaded); @@ -77,16 +86,19 @@ public class ItemModelBuilder { VertexView vertexView = new NoOverlayVertexView(); MemoryBlock meshData = ModelUtil.convertVanillaBuffer(data, vertexView); var mesh = new SimpleMesh(vertexView, meshData, "source=ItemModelBuilder," + "itemStack=" + itemStack + ",renderType=" + renderType + ",shaded=" + shaded); - out.add(new Model.ConfiguredMesh(material, mesh)); + if (mesh.vertexCount() == 0) { + mesh.delete(); + return; + } else { + out.add(new Model.ConfiguredMesh(material, mesh)); + } } }; - var model = Minecraft.getInstance() - .getItemRenderer() - .getModel(itemStack, null, null, seed); - BakedModelBufferer.bufferItem(model, itemStack, displayContext, leftHand, poseStack, resultConsumer); - return new SimpleModel(out.build()); + out.sort(GLINT_LAST); + + return new SimpleModel(ImmutableList.copyOf(out)); } } diff --git a/src/main/java/com/jozufozu/flywheel/vanilla/ItemVisual.java b/src/main/java/com/jozufozu/flywheel/vanilla/ItemVisual.java index 4365c450c..695d6f358 100644 --- a/src/main/java/com/jozufozu/flywheel/vanilla/ItemVisual.java +++ b/src/main/java/com/jozufozu/flywheel/vanilla/ItemVisual.java @@ -19,6 +19,9 @@ import com.mojang.math.Axis; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.LightTexture; import net.minecraft.client.resources.model.BakedModel; +import net.minecraft.client.resources.model.MultiPartBakedModel; +import net.minecraft.client.resources.model.SimpleBakedModel; +import net.minecraft.client.resources.model.WeightedBakedModel; import net.minecraft.util.Mth; import net.minecraft.util.RandomSource; import net.minecraft.world.entity.item.ItemEntity; @@ -32,23 +35,56 @@ public class ItemVisual extends SimpleEntityVisual { private static final ThreadLocal RANDOM = ThreadLocal.withInitial(RandomSource::createNewThreadLocalInstance); public static final ModelCache MODEL_CACHE = new ModelCache<>(stack -> { - return new ItemModelBuilder(stack.stack()).build(); + return new ItemModelBuilder(stack.stack(), stack.model()).build(); }); private final PoseStack pPoseStack = new PoseStack(); private final BakedModel model; + private final boolean isSupported; private final InstanceRecycler instances; public ItemVisual(VisualizationContext ctx, ItemEntity entity) { super(ctx, entity); + var item = entity.getItem(); model = Minecraft.getInstance() + .getItemRenderer() + .getModel(item, entity.level(), null, entity.getId()); + + isSupported = isSupported(model); + + var key = new ItemKey(item.copy(), model); + + instances = new InstanceRecycler<>(() -> instancerProvider.instancer(InstanceTypes.TRANSFORMED, MODEL_CACHE.get(key)) + .createInstance()); + } + + public static boolean isSupported(ItemEntity entity) { + var model = Minecraft.getInstance() .getItemRenderer() .getModel(entity.getItem(), entity.level(), null, entity.getId()); - instances = new InstanceRecycler<>(() -> instancerProvider.instancer(InstanceTypes.TRANSFORMED, MODEL_CACHE.get(new ItemKey(entity.getItem()))) - .createInstance()); + return isSupported(model); + } + + public static boolean isSupported(BakedModel model) { + if (model.isCustomRenderer()) { + return false; + } + + if (!model.getOverrides() + .getOverrides() + .isEmpty()) { + return false; + } + + Class c = model.getClass(); + if (!(c == SimpleBakedModel.class || c == MultiPartBakedModel.class || c == WeightedBakedModel.class)) { + return false; + } + + return true; } @Override @@ -63,7 +99,7 @@ public class ItemVisual extends SimpleEntityVisual { @Override public void beginFrame(Context ctx) { - if (!isVisible(ctx.frustum())) { + if (!isSupported || !isVisible(ctx.frustum())) { return; } @@ -162,11 +198,7 @@ public class ItemVisual extends SimpleEntityVisual { instances.delete(); } - public record ItemKey(ItemStack stack) { - public ItemKey(ItemStack stack) { - this.stack = stack.copy(); - } - + public record ItemKey(ItemStack stack, BakedModel model) { @Override public boolean equals(Object o) { @@ -177,20 +209,14 @@ public class ItemVisual extends SimpleEntityVisual { return false; } - ItemKey itemKey = (ItemKey) o; - if (stack.isEmpty()) { - return itemKey.stack.isEmpty(); - } else { - return !itemKey.stack.isEmpty() && stack.getItem() == itemKey.stack.getItem() && Objects.equals(stack.getTag(), itemKey.stack.getTag()); - } + var o1 = (ItemKey) o; + return Objects.equals(model, o1.model) && stack.hasFoil() == o1.stack.hasFoil(); } @Override public int hashCode() { - int out = stack.getItem() - .hashCode(); - out = 31 * out + (stack.getTag() != null ? stack.getTag() - .hashCode() : 0); + int out = model.hashCode(); + out = 31 * out + Boolean.hashCode(stack.hasFoil()); return out; } } diff --git a/src/main/java/com/jozufozu/flywheel/vanilla/VanillaVisuals.java b/src/main/java/com/jozufozu/flywheel/vanilla/VanillaVisuals.java index c1354dc90..add25c18a 100644 --- a/src/main/java/com/jozufozu/flywheel/vanilla/VanillaVisuals.java +++ b/src/main/java/com/jozufozu/flywheel/vanilla/VanillaVisuals.java @@ -79,6 +79,7 @@ public class VanillaVisuals { .apply(); builder(EntityType.ITEM).factory(ItemVisual::new) + .skipVanillaRender(ItemVisual::isSupported) .apply(); } }