Get shulked

- Convert ShulkerBoxVisual to use InstanceTree
- Add "pruning" helper visitors
- Remove ModelPartConverter
- Remove TextureMapper and related code from VertexWriter
This commit is contained in:
Jozufozu 2024-09-15 15:29:09 -07:00
parent 31b3507d62
commit d342ae740c
7 changed files with 75 additions and 151 deletions

View file

@ -1,6 +1,7 @@
package dev.engine_room.flywheel.lib.model.part;
import java.util.ArrayList;
import java.util.Set;
import org.jetbrains.annotations.Nullable;
@ -24,14 +25,48 @@ public interface LoweringVisitor {
return ModelTree.create(model, initialPose, new ModelTree[0], new String[0]);
}
static LoweringVisitor materialApplyingVisitor(Material material) {
static LoweringVisitor create(Material material) {
return (path, mesh) -> new SingleMeshModel(mesh, material);
}
static LoweringVisitor retexturingVisitor(Material material, TextureAtlasSprite sprite) {
static LoweringVisitor create(Material material, TextureAtlasSprite sprite) {
return (path, mesh) -> new SingleMeshModel(new RetexturedMesh(mesh, sprite), material);
}
static LoweringVisitor pruning(Set<String> prune, Material material) {
return new LoweringVisitor() {
@Override
public @Nullable ModelTree visit(String path, MeshTree meshTree) {
if (prune.contains(path)) {
return null;
}
return LoweringVisitor.super.visit(path, meshTree);
}
@Override
public Model visit(String path, Mesh mesh) {
return new SingleMeshModel(mesh, material);
}
};
}
static LoweringVisitor pruning(Set<String> prune, Material material, TextureAtlasSprite sprite) {
return new LoweringVisitor() {
@Override
public @Nullable ModelTree visit(String path, MeshTree meshTree) {
if (prune.contains(path)) {
return null;
}
return LoweringVisitor.super.visit(path, meshTree);
}
@Override
public Model visit(String path, Mesh mesh) {
return new SingleMeshModel(new RetexturedMesh(mesh, sprite), material);
}
};
}
static String append(String path, String child) {
if (path.isEmpty()) {
return child;

View file

@ -1,70 +0,0 @@
package dev.engine_room.flywheel.lib.model.part;
import org.jetbrains.annotations.Nullable;
import org.joml.Vector2f;
import com.mojang.blaze3d.vertex.PoseStack;
import dev.engine_room.flywheel.api.model.Mesh;
import dev.engine_room.flywheel.lib.memory.MemoryBlock;
import dev.engine_room.flywheel.lib.model.SimpleQuadMesh;
import dev.engine_room.flywheel.lib.vertex.PosTexNormalVertexView;
import dev.engine_room.flywheel.lib.vertex.VertexView;
import net.minecraft.client.Minecraft;
import net.minecraft.client.model.geom.EntityModelSet;
import net.minecraft.client.model.geom.ModelLayerLocation;
import net.minecraft.client.model.geom.ModelPart;
import net.minecraft.client.renderer.LightTexture;
import net.minecraft.client.renderer.texture.OverlayTexture;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
@Deprecated(forRemoval = true)
public final class ModelPartConverter {
private static final ThreadLocal<ThreadLocalObjects> THREAD_LOCAL_OBJECTS = ThreadLocal.withInitial(ThreadLocalObjects::new);
private ModelPartConverter() {
}
public static Mesh convert(ModelPart modelPart, @Nullable PoseStack poseStack, @Nullable TextureMapper textureMapper) {
ThreadLocalObjects objects = THREAD_LOCAL_OBJECTS.get();
if (poseStack == null) {
poseStack = objects.identityPoseStack;
}
VertexWriter vertexWriter = objects.vertexWriter;
vertexWriter.setTextureMapper(textureMapper);
modelPart.render(poseStack, vertexWriter, LightTexture.FULL_BRIGHT, OverlayTexture.NO_OVERLAY);
MemoryBlock data = vertexWriter.copyDataAndReset();
VertexView vertexView = new PosTexNormalVertexView();
vertexView.load(data);
return new SimpleQuadMesh(vertexView, "source=ModelPartConverter");
}
public static Mesh convert(ModelLayerLocation layer, @Nullable TextureAtlasSprite sprite, String... childPath) {
EntityModelSet entityModels = Minecraft.getInstance().getEntityModels();
ModelPart modelPart = entityModels.bakeLayer(layer);
for (String pathPart : childPath) {
modelPart = modelPart.getChild(pathPart);
}
TextureMapper textureMapper = sprite == null ? null : TextureMapper.toSprite(sprite);
return convert(modelPart, null, textureMapper);
}
public static Mesh convert(ModelLayerLocation layer, String... childPath) {
return convert(layer, null, childPath);
}
public interface TextureMapper {
void map(Vector2f uv);
static TextureMapper toSprite(TextureAtlasSprite sprite) {
return uv -> uv.set(sprite.getU(uv.x * 16), sprite.getV(uv.y * 16));
}
}
private static class ThreadLocalObjects {
public final PoseStack identityPoseStack = new PoseStack();
public final VertexWriter vertexWriter = new VertexWriter();
}
}

View file

@ -1,14 +1,11 @@
package dev.engine_room.flywheel.lib.model.part;
import org.jetbrains.annotations.Nullable;
import org.joml.Vector2f;
import org.lwjgl.system.MemoryUtil;
import com.mojang.blaze3d.vertex.VertexConsumer;
import dev.engine_room.flywheel.lib.math.DataPacker;
import dev.engine_room.flywheel.lib.memory.MemoryBlock;
import dev.engine_room.flywheel.lib.model.part.ModelPartConverter.TextureMapper;
import dev.engine_room.flywheel.lib.vertex.PosTexNormalVertexView;
class VertexWriter implements VertexConsumer {
@ -16,10 +13,6 @@ class VertexWriter implements VertexConsumer {
private MemoryBlock data;
@Nullable
private TextureMapper textureMapper;
private final Vector2f uvVec = new Vector2f();
private int vertexCount;
private boolean filledPosition;
private boolean filledTexture;
@ -29,10 +22,6 @@ class VertexWriter implements VertexConsumer {
data = MemoryBlock.malloc(128 * STRIDE);
}
public void setTextureMapper(@Nullable TextureMapper mapper) {
textureMapper = mapper;
}
@Override
public VertexConsumer vertex(double x, double y, double z) {
if (!filledPosition) {
@ -54,13 +43,6 @@ class VertexWriter implements VertexConsumer {
@Override
public VertexConsumer uv(float u, float v) {
if (!filledTexture) {
if (textureMapper != null) {
uvVec.set(u, v);
textureMapper.map(uvVec);
u = uvVec.x;
v = uvVec.y;
}
long ptr = vertexPtr();
MemoryUtil.memPutFloat(ptr + 12, u);
MemoryUtil.memPutFloat(ptr + 16, v);
@ -131,7 +113,6 @@ class VertexWriter implements VertexConsumer {
filledPosition = false;
filledTexture = false;
filledNormal = false;
textureMapper = null;
return dataCopy;
}

View file

@ -27,7 +27,7 @@ public class BellVisual extends AbstractBlockEntityVisual<BellBlockEntity> imple
.build();
// Need to hold the visitor in a ResourceReloadHolder to ensure we have a valid sprite.
private static final ResourceReloadHolder<LoweringVisitor> VISITOR = new ResourceReloadHolder<>(() -> LoweringVisitor.retexturingVisitor(MATERIAL, BellRenderer.BELL_RESOURCE_LOCATION.sprite()));
private static final ResourceReloadHolder<LoweringVisitor> VISITOR = new ResourceReloadHolder<>(() -> LoweringVisitor.create(MATERIAL, BellRenderer.BELL_RESOURCE_LOCATION.sprite()));
private final InstanceTree instances;
private final InstanceTree bellBody;

View file

@ -12,7 +12,6 @@ import org.joml.Matrix4fc;
import dev.engine_room.flywheel.api.instance.Instance;
import dev.engine_room.flywheel.api.material.Material;
import dev.engine_room.flywheel.api.model.Model;
import dev.engine_room.flywheel.api.visualization.VisualizationContext;
import dev.engine_room.flywheel.lib.material.CutoutShaders;
import dev.engine_room.flywheel.lib.material.SimpleMaterial;
@ -20,7 +19,6 @@ import dev.engine_room.flywheel.lib.model.ResourceReloadCache;
import dev.engine_room.flywheel.lib.model.part.InstanceTree;
import dev.engine_room.flywheel.lib.model.part.LoweringVisitor;
import dev.engine_room.flywheel.lib.model.part.ModelTree;
import dev.engine_room.flywheel.lib.transform.TransformStack;
import dev.engine_room.flywheel.lib.visual.AbstractBlockEntityVisual;
import dev.engine_room.flywheel.lib.visual.SimpleDynamicVisual;
import it.unimi.dsi.fastutil.floats.Float2FloatFunction;
@ -57,7 +55,7 @@ public class ChestVisual<T extends BlockEntity & LidBlockEntity> extends Abstrac
LAYER_LOCATIONS.put(ChestType.RIGHT, ModelLayers.DOUBLE_CHEST_RIGHT);
}
private static final Function<TextureAtlasSprite, LoweringVisitor> VISITOR = new ResourceReloadCache<>(s -> LoweringVisitor.retexturingVisitor(MATERIAL, s));
private static final Function<TextureAtlasSprite, LoweringVisitor> VISITOR = new ResourceReloadCache<>(s -> LoweringVisitor.create(MATERIAL, s));
@Nullable
private final InstanceTree instances;

View file

@ -36,7 +36,7 @@ public class MinecartVisual<T extends AbstractMinecart> extends ComponentEntityV
.texture(TEXTURE)
.mipmap(false)
.build();
private static final LoweringVisitor VISITOR = LoweringVisitor.materialApplyingVisitor(MATERIAL);
private static final LoweringVisitor VISITOR = LoweringVisitor.create(MATERIAL);
private final InstanceTree instances;
@Nullable

View file

@ -1,28 +1,26 @@
package dev.engine_room.flywheel.vanilla;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Function;
import org.joml.Quaternionf;
import com.mojang.math.Axis;
import org.joml.Matrix4f;
import dev.engine_room.flywheel.api.instance.Instance;
import dev.engine_room.flywheel.api.visualization.VisualizationContext;
import dev.engine_room.flywheel.lib.instance.InstanceTypes;
import dev.engine_room.flywheel.lib.instance.TransformedInstance;
import dev.engine_room.flywheel.lib.material.CutoutShaders;
import dev.engine_room.flywheel.lib.material.SimpleMaterial;
import dev.engine_room.flywheel.lib.model.ModelCache;
import dev.engine_room.flywheel.lib.model.SingleMeshModel;
import dev.engine_room.flywheel.lib.model.part.ModelPartConverter;
import dev.engine_room.flywheel.lib.transform.TransformStack;
import dev.engine_room.flywheel.lib.util.RecyclingPoseStack;
import dev.engine_room.flywheel.lib.model.ResourceReloadCache;
import dev.engine_room.flywheel.lib.model.part.InstanceTree;
import dev.engine_room.flywheel.lib.model.part.LoweringVisitor;
import dev.engine_room.flywheel.lib.model.part.ModelTree;
import dev.engine_room.flywheel.lib.visual.AbstractBlockEntityVisual;
import dev.engine_room.flywheel.lib.visual.SimpleDynamicVisual;
import net.minecraft.client.model.geom.ModelLayers;
import net.minecraft.client.renderer.Sheets;
import net.minecraft.client.resources.model.Material;
import net.minecraft.core.Direction;
import net.minecraft.util.Mth;
import net.minecraft.world.item.DyeColor;
import net.minecraft.world.level.block.ShulkerBoxBlock;
import net.minecraft.world.level.block.entity.ShulkerBoxBlockEntity;
@ -35,17 +33,13 @@ public class ShulkerBoxVisual extends AbstractBlockEntityVisual<ShulkerBoxBlockE
.backfaceCulling(false)
.build();
private static final ModelCache<Material> BASE_MODELS = new ModelCache<>(texture -> {
return new SingleMeshModel(ModelPartConverter.convert(ModelLayers.SHULKER, texture.sprite(), "base"), MATERIAL);
});
private static final ModelCache<Material> LID_MODELS = new ModelCache<>(texture -> {
return new SingleMeshModel(ModelPartConverter.convert(ModelLayers.SHULKER, texture.sprite(), "lid"), MATERIAL);
});
private final TransformedInstance base;
private final TransformedInstance lid;
private static final Function<Material, LoweringVisitor> VISITORS = new ResourceReloadCache<>(m -> LoweringVisitor.pruning(Set.of("head"), MATERIAL, m.sprite()));
private final RecyclingPoseStack stack = new RecyclingPoseStack();
private final InstanceTree instances;
private final InstanceTree lid;
private final Matrix4f initialPose;
private float lastProgress = Float.NaN;
@ -60,31 +54,22 @@ public class ShulkerBoxVisual extends AbstractBlockEntityVisual<ShulkerBoxBlockE
texture = Sheets.SHULKER_TEXTURE_LOCATION.get(color.getId());
}
var rotation = getDirection().getRotation();
instances = InstanceTree.create(instancerProvider(), ModelTree.of(ModelLayers.SHULKER, VISITORS.apply(texture)));
stack.setIdentity();
TransformStack.of(stack)
.translate(getVisualPosition())
.translate(0.5f)
initialPose = createInitialPose();
lid = instances.childOrThrow("lid");
}
private Matrix4f createInitialPose() {
var rotation = getDirection().getRotation();
var visualPosition = getVisualPosition();
return new Matrix4f().translate(visualPosition.getX(), visualPosition.getY(), visualPosition.getZ())
.translate(0.5f, 0.5f, 0.5f)
.scale(0.9995f)
.rotate(rotation)
.scale(1, -1, -1)
.translateY(-1);
base = createBaseInstance(texture).setTransform(stack);
base.setChanged();
lid = createLidInstance(texture).setTransform(stack);
lid.setChanged();
}
private TransformedInstance createBaseInstance(Material texture) {
return instancerProvider().instancer(InstanceTypes.TRANSFORMED, BASE_MODELS.get(texture))
.createInstance();
}
private TransformedInstance createLidInstance(Material texture) {
return instancerProvider().instancer(InstanceTypes.TRANSFORMED, LID_MODELS.get(texture))
.createInstance();
.translate(0, -1, 0);
}
private Direction getDirection() {
@ -107,33 +92,28 @@ public class ShulkerBoxVisual extends AbstractBlockEntityVisual<ShulkerBoxBlockE
}
lastProgress = progress;
Quaternionf spin = Axis.YP.rotationDegrees(270.0f * progress);
lid.yRot(1.5f * Mth.PI * progress);
lid.yPos(24f - progress * 8f);
TransformStack.of(stack)
.pushPose()
.translateY(-progress * 0.5f)
.rotate(spin);
lid.setTransform(stack)
.setChanged();
stack.popPose();
instances.updateInstancesStatic(initialPose);
}
@Override
public void updateLight(float partialTick) {
relight(base, lid);
int packedLight = computePackedLight();
instances.traverse(instance -> {
instance.light(packedLight)
.setChanged();
});
}
@Override
public void collectCrumblingInstances(Consumer<Instance> consumer) {
consumer.accept(base);
consumer.accept(lid);
instances.traverse(consumer);
}
@Override
protected void _delete() {
base.delete();
lid.delete();
instances.delete();
}
}