mirror of
https://github.com/Jozufozu/Flywheel.git
synced 2024-12-27 23:47:09 +01:00
First try
- Fix race condition causing instancers to never be initialized - Fix frame uniforms being 8 bytes too small - Don't have any bias in BakedModelBufferer#bufferItem, instead sort meshes with glint transparency to be rendered last - Only visualized item entities if their items are supported - Key item models directly on the BakedModel + if the stack has foil
This commit is contained in:
parent
163649e792
commit
b51ccd43e2
6 changed files with 86 additions and 60 deletions
|
@ -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<N extends AbstractInstancer<?>> {
|
|||
* <br>
|
||||
* All new instancers land here before having resources allocated in {@link #flush}.
|
||||
*/
|
||||
protected final List<UninitializedInstancer<N, ?>> initializationQueue = new ArrayList<>();
|
||||
protected final Queue<UninitializedInstancer<N, ?>> initializationQueue = new ConcurrentLinkedQueue<>();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <I extends Instance> Instancer<I> getInstancer(Environment environment, InstanceType<I> type, Model model, RenderStage stage) {
|
||||
|
@ -72,7 +74,8 @@ public abstract class DrawManager<N extends AbstractInstancer<?>> {
|
|||
// 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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<RenderType, MeshEmitter> emitters = new HashMap<>();
|
||||
|
||||
private final Set<MeshEmitter> active = new LinkedHashSet<>();
|
||||
// Hack: we want glint to render after everything else so track it separately here
|
||||
private final Set<MeshEmitter> activeGlint = new LinkedHashSet<>();
|
||||
private final Map<RenderType, MeshEmitter> emitters = new Reference2ReferenceOpenHashMap<>();
|
||||
private final Set<MeshEmitter> 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<MeshEmitter> 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;
|
||||
}
|
||||
|
|
|
@ -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<Model.ConfiguredMesh> 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<RenderType, Boolean, Material> 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<RenderType, Boolean, Material> materialFunc) {
|
||||
this.materialFunc = materialFunc;
|
||||
return this;
|
||||
|
@ -69,7 +78,7 @@ public class ItemModelBuilder {
|
|||
materialFunc = ModelUtil::getItemMaterial;
|
||||
}
|
||||
|
||||
var out = ImmutableList.<Model.ConfiguredMesh>builder();
|
||||
ArrayList<Model.ConfiguredMesh> 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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<ItemEntity> {
|
|||
private static final ThreadLocal<RandomSource> RANDOM = ThreadLocal.withInitial(RandomSource::createNewThreadLocalInstance);
|
||||
|
||||
public static final ModelCache<ItemKey> 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<TransformedInstance> 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<? extends BakedModel> 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<ItemEntity> {
|
|||
|
||||
@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<ItemEntity> {
|
|||
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<ItemEntity> {
|
|||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -79,6 +79,7 @@ public class VanillaVisuals {
|
|||
.apply();
|
||||
|
||||
builder(EntityType.ITEM).factory(ItemVisual::new)
|
||||
.skipVanillaRender(ItemVisual::isSupported)
|
||||
.apply();
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue