mirror of
https://github.com/Jozufozu/Flywheel.git
synced 2025-01-24 20:07:54 +01:00
Home alone
- Remove LoweringVisitor - Move functionality of four main static methods in LoweringVisitor to new ModelTrees class - Return ModelTree directly - Accept Material instead of TextureAtlasSprite for efficiency, so visuals don't need to look up the sprite to get the ModelTree - Use ResourceReloadCache for MeshTree.CACHE
This commit is contained in:
parent
5a75fe972f
commit
62a0954381
11 changed files with 146 additions and 267 deletions
|
@ -1,128 +0,0 @@
|
||||||
package dev.engine_room.flywheel.lib.model.part;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import dev.engine_room.flywheel.api.material.Material;
|
|
||||||
import dev.engine_room.flywheel.api.model.Mesh;
|
|
||||||
import dev.engine_room.flywheel.api.model.Model;
|
|
||||||
import dev.engine_room.flywheel.lib.model.RetexturedMesh;
|
|
||||||
import dev.engine_room.flywheel.lib.model.SingleMeshModel;
|
|
||||||
import net.minecraft.client.model.geom.PartPose;
|
|
||||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A tree walking visitor that lowers a MeshTree to a ModelTree.
|
|
||||||
*/
|
|
||||||
public interface LoweringVisitor {
|
|
||||||
static ModelTree leaf(Model model) {
|
|
||||||
return leaf(model, PartPose.ZERO);
|
|
||||||
}
|
|
||||||
|
|
||||||
static ModelTree leaf(Model model, PartPose initialPose) {
|
|
||||||
return ModelTree.create(model, initialPose, new ModelTree[0], new String[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
static LoweringVisitor create(Material material) {
|
|
||||||
return (path, mesh) -> new SingleMeshModel(mesh, material);
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
return path + "/" + child;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Walk the given MeshTree, lowering its Mesh to a Model, and lowering all children using the given visitor.
|
|
||||||
*
|
|
||||||
* @param path The absolute path to the MeshTree node.
|
|
||||||
* @param meshTree The MeshTree to walk.
|
|
||||||
* @param loweringVisitor The visitor to use to lower the Mesh and MeshTree nodes.
|
|
||||||
* @return The lowered ModelTree.
|
|
||||||
*/
|
|
||||||
static ModelTree walk(String path, MeshTree meshTree, LoweringVisitor loweringVisitor) {
|
|
||||||
Model out = null;
|
|
||||||
|
|
||||||
if (meshTree.mesh() != null) {
|
|
||||||
out = loweringVisitor.visit(path, meshTree.mesh());
|
|
||||||
}
|
|
||||||
|
|
||||||
ArrayList<ModelTree> children = new ArrayList<>();
|
|
||||||
ArrayList<String> childNames = new ArrayList<>();
|
|
||||||
|
|
||||||
for (int i = 0; i < meshTree.childCount(); i++) {
|
|
||||||
var child = loweringVisitor.visit(append(path, meshTree.childName(i)), meshTree.child(i));
|
|
||||||
|
|
||||||
if (child != null) {
|
|
||||||
children.add(child);
|
|
||||||
childNames.add(meshTree.childName(i));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ModelTree.create(out, meshTree.initialPose(), children.toArray(new ModelTree[0]), childNames.toArray(new String[0]));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Visit the given Mesh, converting it to a Model.
|
|
||||||
*
|
|
||||||
* @param path The absolute path to the MeshTree node containing the Mesh.
|
|
||||||
* @param mesh The Mesh to lower.
|
|
||||||
* @return The lowered Model, or null if the Model should be omitted.
|
|
||||||
*/
|
|
||||||
@Nullable Model visit(String path, Mesh mesh);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Visit the given MeshTree, converting it to a ModelTree.
|
|
||||||
*
|
|
||||||
* @param path The absolute path to the MeshTree node.
|
|
||||||
* @param meshTree The MeshTree to lower.
|
|
||||||
* @return The lowered ModelTree, or null if the ModelTree should be omitted.
|
|
||||||
*/
|
|
||||||
@Nullable
|
|
||||||
default ModelTree visit(String path, MeshTree meshTree) {
|
|
||||||
return walk(path, meshTree, this);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,12 +1,8 @@
|
||||||
package dev.engine_room.flywheel.lib.model.part;
|
package dev.engine_room.flywheel.lib.model.part;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.NoSuchElementException;
|
import java.util.NoSuchElementException;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
import java.util.function.Consumer;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.ApiStatus;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import com.mojang.blaze3d.vertex.PoseStack;
|
import com.mojang.blaze3d.vertex.PoseStack;
|
||||||
|
@ -14,6 +10,7 @@ import com.mojang.blaze3d.vertex.PoseStack;
|
||||||
import dev.engine_room.flywheel.api.model.Mesh;
|
import dev.engine_room.flywheel.api.model.Mesh;
|
||||||
import dev.engine_room.flywheel.lib.internal.FlwLibLink;
|
import dev.engine_room.flywheel.lib.internal.FlwLibLink;
|
||||||
import dev.engine_room.flywheel.lib.memory.MemoryBlock;
|
import dev.engine_room.flywheel.lib.memory.MemoryBlock;
|
||||||
|
import dev.engine_room.flywheel.lib.model.ResourceReloadCache;
|
||||||
import dev.engine_room.flywheel.lib.model.SimpleQuadMesh;
|
import dev.engine_room.flywheel.lib.model.SimpleQuadMesh;
|
||||||
import dev.engine_room.flywheel.lib.vertex.PosTexNormalVertexView;
|
import dev.engine_room.flywheel.lib.vertex.PosTexNormalVertexView;
|
||||||
import dev.engine_room.flywheel.lib.vertex.VertexView;
|
import dev.engine_room.flywheel.lib.vertex.VertexView;
|
||||||
|
@ -28,7 +25,7 @@ import net.minecraft.client.renderer.texture.OverlayTexture;
|
||||||
public final class MeshTree {
|
public final class MeshTree {
|
||||||
private static final ThreadLocal<ThreadLocalObjects> THREAD_LOCAL_OBJECTS = ThreadLocal.withInitial(ThreadLocalObjects::new);
|
private static final ThreadLocal<ThreadLocalObjects> THREAD_LOCAL_OBJECTS = ThreadLocal.withInitial(ThreadLocalObjects::new);
|
||||||
private static final PoseStack.Pose IDENTITY_POSE = new PoseStack().last();
|
private static final PoseStack.Pose IDENTITY_POSE = new PoseStack().last();
|
||||||
private static final Map<ModelLayerLocation, MeshTree> CACHE = new ConcurrentHashMap<>();
|
private static final ResourceReloadCache<ModelLayerLocation, MeshTree> CACHE = new ResourceReloadCache<>(MeshTree::convert);
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private final Mesh mesh;
|
private final Mesh mesh;
|
||||||
|
@ -44,7 +41,7 @@ public final class MeshTree {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static MeshTree of(ModelLayerLocation layer) {
|
public static MeshTree of(ModelLayerLocation layer) {
|
||||||
return CACHE.computeIfAbsent(layer, MeshTree::convert);
|
return CACHE.get(layer);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static MeshTree convert(ModelLayerLocation layer) {
|
private static MeshTree convert(ModelLayerLocation layer) {
|
||||||
|
@ -58,9 +55,9 @@ public final class MeshTree {
|
||||||
private static MeshTree convert(ModelPart modelPart, ThreadLocalObjects objects) {
|
private static MeshTree convert(ModelPart modelPart, ThreadLocalObjects objects) {
|
||||||
var modelPartChildren = FlwLibLink.INSTANCE.getModelPartChildren(modelPart);
|
var modelPartChildren = FlwLibLink.INSTANCE.getModelPartChildren(modelPart);
|
||||||
|
|
||||||
// Freeze the ordering here. Maybe we want to sort this?
|
|
||||||
String[] childNames = modelPartChildren.keySet()
|
String[] childNames = modelPartChildren.keySet()
|
||||||
.toArray(String[]::new);
|
.toArray(String[]::new);
|
||||||
|
Arrays.sort(childNames);
|
||||||
|
|
||||||
MeshTree[] children = new MeshTree[childNames.length];
|
MeshTree[] children = new MeshTree[childNames.length];
|
||||||
for (int i = 0; i < childNames.length; i++) {
|
for (int i = 0; i < childNames.length; i++) {
|
||||||
|
@ -135,20 +132,6 @@ public final class MeshTree {
|
||||||
return child;
|
return child;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void traverse(Consumer<Mesh> consumer) {
|
|
||||||
if (mesh != null) {
|
|
||||||
consumer.accept(mesh);
|
|
||||||
}
|
|
||||||
for (MeshTree child : children) {
|
|
||||||
child.traverse(consumer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@ApiStatus.Internal
|
|
||||||
public static void onEndClientResourceReload() {
|
|
||||||
CACHE.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class ThreadLocalObjects {
|
private static class ThreadLocalObjects {
|
||||||
public final VertexWriter vertexWriter = new VertexWriter();
|
public final VertexWriter vertexWriter = new VertexWriter();
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,74 +2,50 @@ package dev.engine_room.flywheel.lib.model.part;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.NoSuchElementException;
|
||||||
|
|
||||||
import org.jetbrains.annotations.ApiStatus;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import dev.engine_room.flywheel.api.model.Model;
|
import dev.engine_room.flywheel.api.model.Model;
|
||||||
import net.minecraft.client.model.geom.ModelLayerLocation;
|
|
||||||
import net.minecraft.client.model.geom.PartPose;
|
import net.minecraft.client.model.geom.PartPose;
|
||||||
|
|
||||||
public class ModelTree {
|
public final class ModelTree {
|
||||||
private static final Map<ModelTreeKey, ModelTree> CACHE = new ConcurrentHashMap<>();
|
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private final Model model;
|
private final Model model;
|
||||||
private final PartPose initialPose;
|
private final PartPose initialPose;
|
||||||
private final ModelTree[] children;
|
private final ModelTree[] children;
|
||||||
private final String[] childNames;
|
private final String[] childNames;
|
||||||
|
|
||||||
private ModelTree(@Nullable Model model, PartPose initialPose, ModelTree[] children, String[] childNames) {
|
|
||||||
this.model = model;
|
|
||||||
this.initialPose = initialPose;
|
|
||||||
this.children = children;
|
|
||||||
this.childNames = childNames;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create and memoize a ModelTree root.
|
|
||||||
*
|
|
||||||
* <p>This method is intended for use by Visuals.
|
|
||||||
*
|
|
||||||
* @param modelLayerLocation The model location to lower.
|
|
||||||
* @param loweringVisitor The visitor to use to lower the model.
|
|
||||||
* @return The cached ModelTree root.
|
|
||||||
*/
|
|
||||||
public static ModelTree of(ModelLayerLocation modelLayerLocation, LoweringVisitor loweringVisitor) {
|
|
||||||
return CACHE.computeIfAbsent(new ModelTreeKey(modelLayerLocation, loweringVisitor), k -> {
|
|
||||||
var meshTree = MeshTree.of(k.modelLayerLocation());
|
|
||||||
|
|
||||||
var out = k.loweringVisitor()
|
|
||||||
.visit("", meshTree);
|
|
||||||
|
|
||||||
if (out == null) {
|
|
||||||
// Should this be an error, or a missing model?
|
|
||||||
return ModelTree.create(null, PartPose.ZERO, new ModelTree[0], new String[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return out;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new ModelTree node.
|
* Create a new ModelTree node.
|
||||||
*
|
*
|
||||||
* <p>This method is intended for use by {@link LoweringVisitor} implementations.
|
|
||||||
*
|
|
||||||
* @param model The model to associate with this node, or null if this node does not render.
|
* @param model The model to associate with this node, or null if this node does not render.
|
||||||
* @param initialPose The initial pose of this node.
|
* @param initialPose The initial pose of this node.
|
||||||
* @param children The children of this node.
|
* @param children The children of this node.
|
||||||
* @param childNames The names of the children of this node.
|
|
||||||
* @return A new ModelTree node.
|
|
||||||
* @throws IllegalArgumentException if children and childNames have different lengths.
|
|
||||||
*/
|
*/
|
||||||
public static ModelTree create(@Nullable Model model, PartPose initialPose, ModelTree[] children, String[] childNames) {
|
public ModelTree(@Nullable Model model, PartPose initialPose, Map<String, ModelTree> children) {
|
||||||
if (children.length != childNames.length) {
|
this.model = model;
|
||||||
throw new IllegalArgumentException("children and childNames must have the same length (%s != %s)".formatted(children.length, childNames.length));
|
this.initialPose = initialPose;
|
||||||
|
|
||||||
|
String[] childNames = children.keySet().toArray(String[]::new);
|
||||||
|
Arrays.sort(childNames);
|
||||||
|
|
||||||
|
ModelTree[] childArray = new ModelTree[childNames.length];
|
||||||
|
for (int i = 0; i < childNames.length; i++) {
|
||||||
|
childArray[i] = children.get(childNames[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new ModelTree(model, initialPose, children, childNames);
|
this.children = childArray;
|
||||||
|
this.childNames = childNames;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public Model model() {
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PartPose initialPose() {
|
||||||
|
return initialPose;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int childCount() {
|
public int childCount() {
|
||||||
|
@ -84,24 +60,32 @@ public class ModelTree {
|
||||||
return childNames[index];
|
return childNames[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
public PartPose initialPose() {
|
|
||||||
return initialPose;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
public Model model() {
|
|
||||||
return model;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int childIndex(String name) {
|
public int childIndex(String name) {
|
||||||
return Arrays.binarySearch(childNames, name);
|
return Arrays.binarySearch(childNames, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ApiStatus.Internal
|
public boolean hasChild(String name) {
|
||||||
public static void onEndClientResourceReload() {
|
return childIndex(name) >= 0;
|
||||||
CACHE.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private record ModelTreeKey(ModelLayerLocation modelLayerLocation, LoweringVisitor loweringVisitor) {
|
@Nullable
|
||||||
|
public ModelTree child(String name) {
|
||||||
|
int index = childIndex(name);
|
||||||
|
|
||||||
|
if (index < 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return child(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ModelTree childOrThrow(String name) {
|
||||||
|
ModelTree child = child(name);
|
||||||
|
|
||||||
|
if (child == null) {
|
||||||
|
throw new NoSuchElementException("Can't find part " + name);
|
||||||
|
}
|
||||||
|
|
||||||
|
return child;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +0,0 @@
|
||||||
package dev.engine_room.flywheel.lib.model.part;
|
|
||||||
|
|
||||||
import java.util.function.Function;
|
|
||||||
|
|
||||||
import dev.engine_room.flywheel.lib.model.ResourceReloadCache;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If the lookup to create your model tree directly through {@link ModelTree#of} is particularly expensive,
|
|
||||||
* you can memoize the arguments here to hide the cost.
|
|
||||||
*/
|
|
||||||
public class ModelTreeCache<T> extends ResourceReloadCache<T, ModelTree> {
|
|
||||||
public ModelTreeCache(Function<T, ModelTree> factory) {
|
|
||||||
super(factory);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,83 @@
|
||||||
|
package dev.engine_room.flywheel.lib.model.part;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import dev.engine_room.flywheel.api.material.Material;
|
||||||
|
import dev.engine_room.flywheel.api.model.Mesh;
|
||||||
|
import dev.engine_room.flywheel.api.model.Model;
|
||||||
|
import dev.engine_room.flywheel.lib.model.ResourceReloadCache;
|
||||||
|
import dev.engine_room.flywheel.lib.model.RetexturedMesh;
|
||||||
|
import dev.engine_room.flywheel.lib.model.SingleMeshModel;
|
||||||
|
import net.minecraft.client.model.geom.ModelLayerLocation;
|
||||||
|
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||||
|
|
||||||
|
public final class ModelTrees {
|
||||||
|
private static final ResourceReloadCache<ModelTreeKey, ModelTree> CACHE = new ResourceReloadCache<>(k -> {
|
||||||
|
ModelTree tree = convert("", MeshTree.of(k.layer), k.pathsToPrune, k.texture != null ? k.texture.sprite() : null, k.material);
|
||||||
|
|
||||||
|
if (tree == null) {
|
||||||
|
throw new IllegalArgumentException("Cannot prune root node!");
|
||||||
|
}
|
||||||
|
|
||||||
|
return tree;
|
||||||
|
});
|
||||||
|
|
||||||
|
private ModelTrees() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ModelTree of(ModelLayerLocation layer, Material material) {
|
||||||
|
return CACHE.get(new ModelTreeKey(layer, Collections.emptySet(), null, material));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ModelTree of(ModelLayerLocation layer, net.minecraft.client.resources.model.Material texture, Material material) {
|
||||||
|
return CACHE.get(new ModelTreeKey(layer, Collections.emptySet(), texture, material));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ModelTree of(ModelLayerLocation layer, Set<String> pathsToPrune, Material material) {
|
||||||
|
return CACHE.get(new ModelTreeKey(layer, Set.copyOf(pathsToPrune), null, material));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ModelTree of(ModelLayerLocation layer, Set<String> pathsToPrune, net.minecraft.client.resources.model.Material texture, Material material) {
|
||||||
|
return CACHE.get(new ModelTreeKey(layer, Set.copyOf(pathsToPrune), texture, material));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private static ModelTree convert(String path, MeshTree meshTree, Set<String> pathsToPrune, @Nullable TextureAtlasSprite sprite, Material material) {
|
||||||
|
if (pathsToPrune.contains(path)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Model model = null;
|
||||||
|
Mesh mesh = meshTree.mesh();
|
||||||
|
|
||||||
|
if (mesh != null) {
|
||||||
|
if (sprite != null) {
|
||||||
|
mesh = new RetexturedMesh(mesh, sprite);
|
||||||
|
}
|
||||||
|
|
||||||
|
model = new SingleMeshModel(mesh, material);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, ModelTree> children = new HashMap<>();
|
||||||
|
String pathSlash = path + "/";
|
||||||
|
|
||||||
|
for (int i = 0; i < meshTree.childCount(); i++) {
|
||||||
|
String childName = meshTree.childName(i);
|
||||||
|
var child = convert(pathSlash + childName, meshTree.child(i), pathsToPrune, sprite, material);
|
||||||
|
|
||||||
|
if (child != null) {
|
||||||
|
children.put(childName, child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ModelTree(model, meshTree.initialPose(), children);
|
||||||
|
}
|
||||||
|
|
||||||
|
private record ModelTreeKey(ModelLayerLocation layer, Set<String> pathsToPrune, @Nullable net.minecraft.client.resources.model.Material texture, Material material) {
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,10 +9,8 @@ import dev.engine_room.flywheel.api.instance.Instance;
|
||||||
import dev.engine_room.flywheel.api.material.Material;
|
import dev.engine_room.flywheel.api.material.Material;
|
||||||
import dev.engine_room.flywheel.api.visualization.VisualizationContext;
|
import dev.engine_room.flywheel.api.visualization.VisualizationContext;
|
||||||
import dev.engine_room.flywheel.lib.material.SimpleMaterial;
|
import dev.engine_room.flywheel.lib.material.SimpleMaterial;
|
||||||
import dev.engine_room.flywheel.lib.model.ResourceReloadHolder;
|
|
||||||
import dev.engine_room.flywheel.lib.model.part.InstanceTree;
|
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.ModelTrees;
|
||||||
import dev.engine_room.flywheel.lib.model.part.ModelTree;
|
|
||||||
import dev.engine_room.flywheel.lib.visual.AbstractBlockEntityVisual;
|
import dev.engine_room.flywheel.lib.visual.AbstractBlockEntityVisual;
|
||||||
import dev.engine_room.flywheel.lib.visual.SimpleDynamicVisual;
|
import dev.engine_room.flywheel.lib.visual.SimpleDynamicVisual;
|
||||||
import net.minecraft.client.model.geom.ModelLayers;
|
import net.minecraft.client.model.geom.ModelLayers;
|
||||||
|
@ -26,9 +24,6 @@ public class BellVisual extends AbstractBlockEntityVisual<BellBlockEntity> imple
|
||||||
.mipmap(false)
|
.mipmap(false)
|
||||||
.build();
|
.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.create(MATERIAL, BellRenderer.BELL_RESOURCE_LOCATION.sprite()));
|
|
||||||
|
|
||||||
private final InstanceTree instances;
|
private final InstanceTree instances;
|
||||||
private final InstanceTree bellBody;
|
private final InstanceTree bellBody;
|
||||||
|
|
||||||
|
@ -39,7 +34,7 @@ public class BellVisual extends AbstractBlockEntityVisual<BellBlockEntity> imple
|
||||||
public BellVisual(VisualizationContext ctx, BellBlockEntity blockEntity, float partialTick) {
|
public BellVisual(VisualizationContext ctx, BellBlockEntity blockEntity, float partialTick) {
|
||||||
super(ctx, blockEntity, partialTick);
|
super(ctx, blockEntity, partialTick);
|
||||||
|
|
||||||
instances = InstanceTree.create(instancerProvider(), ModelTree.of(ModelLayers.BELL, VISITOR.get()));
|
instances = InstanceTree.create(instancerProvider(), ModelTrees.of(ModelLayers.BELL, BellRenderer.BELL_RESOURCE_LOCATION, MATERIAL));
|
||||||
bellBody = instances.childOrThrow("bell_body");
|
bellBody = instances.childOrThrow("bell_body");
|
||||||
|
|
||||||
BlockPos visualPos = getVisualPosition();
|
BlockPos visualPos = getVisualPosition();
|
||||||
|
|
|
@ -4,7 +4,6 @@ import java.util.Calendar;
|
||||||
import java.util.EnumMap;
|
import java.util.EnumMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Function;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.joml.Matrix4f;
|
import org.joml.Matrix4f;
|
||||||
|
@ -15,10 +14,8 @@ import dev.engine_room.flywheel.api.material.Material;
|
||||||
import dev.engine_room.flywheel.api.visualization.VisualizationContext;
|
import dev.engine_room.flywheel.api.visualization.VisualizationContext;
|
||||||
import dev.engine_room.flywheel.lib.material.CutoutShaders;
|
import dev.engine_room.flywheel.lib.material.CutoutShaders;
|
||||||
import dev.engine_room.flywheel.lib.material.SimpleMaterial;
|
import dev.engine_room.flywheel.lib.material.SimpleMaterial;
|
||||||
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.InstanceTree;
|
||||||
import dev.engine_room.flywheel.lib.model.part.LoweringVisitor;
|
import dev.engine_room.flywheel.lib.model.part.ModelTrees;
|
||||||
import dev.engine_room.flywheel.lib.model.part.ModelTree;
|
|
||||||
import dev.engine_room.flywheel.lib.visual.AbstractBlockEntityVisual;
|
import dev.engine_room.flywheel.lib.visual.AbstractBlockEntityVisual;
|
||||||
import dev.engine_room.flywheel.lib.visual.SimpleDynamicVisual;
|
import dev.engine_room.flywheel.lib.visual.SimpleDynamicVisual;
|
||||||
import it.unimi.dsi.fastutil.floats.Float2FloatFunction;
|
import it.unimi.dsi.fastutil.floats.Float2FloatFunction;
|
||||||
|
@ -28,7 +25,6 @@ import net.minecraft.client.model.geom.ModelLayers;
|
||||||
import net.minecraft.client.renderer.LevelRenderer;
|
import net.minecraft.client.renderer.LevelRenderer;
|
||||||
import net.minecraft.client.renderer.LightTexture;
|
import net.minecraft.client.renderer.LightTexture;
|
||||||
import net.minecraft.client.renderer.Sheets;
|
import net.minecraft.client.renderer.Sheets;
|
||||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.core.SectionPos;
|
import net.minecraft.core.SectionPos;
|
||||||
import net.minecraft.util.Mth;
|
import net.minecraft.util.Mth;
|
||||||
|
@ -55,8 +51,6 @@ public class ChestVisual<T extends BlockEntity & LidBlockEntity> extends Abstrac
|
||||||
LAYER_LOCATIONS.put(ChestType.RIGHT, ModelLayers.DOUBLE_CHEST_RIGHT);
|
LAYER_LOCATIONS.put(ChestType.RIGHT, ModelLayers.DOUBLE_CHEST_RIGHT);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final Function<TextureAtlasSprite, LoweringVisitor> VISITOR = new ResourceReloadCache<>(s -> LoweringVisitor.create(MATERIAL, s));
|
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private final InstanceTree instances;
|
private final InstanceTree instances;
|
||||||
@Nullable
|
@Nullable
|
||||||
|
@ -80,8 +74,8 @@ public class ChestVisual<T extends BlockEntity & LidBlockEntity> extends Abstrac
|
||||||
Block block = blockState.getBlock();
|
Block block = blockState.getBlock();
|
||||||
if (block instanceof AbstractChestBlock<?> chestBlock) {
|
if (block instanceof AbstractChestBlock<?> chestBlock) {
|
||||||
ChestType chestType = blockState.hasProperty(ChestBlock.TYPE) ? blockState.getValue(ChestBlock.TYPE) : ChestType.SINGLE;
|
ChestType chestType = blockState.hasProperty(ChestBlock.TYPE) ? blockState.getValue(ChestBlock.TYPE) : ChestType.SINGLE;
|
||||||
TextureAtlasSprite sprite = Sheets.chooseMaterial(blockEntity, chestType, isChristmas()).sprite();
|
net.minecraft.client.resources.model.Material texture = Sheets.chooseMaterial(blockEntity, chestType, isChristmas());
|
||||||
instances = InstanceTree.create(instancerProvider(), ModelTree.of(LAYER_LOCATIONS.get(chestType), VISITOR.apply(sprite)));
|
instances = InstanceTree.create(instancerProvider(), ModelTrees.of(LAYER_LOCATIONS.get(chestType), texture, MATERIAL));
|
||||||
lid = instances.childOrThrow("lid");
|
lid = instances.childOrThrow("lid");
|
||||||
lock = instances.childOrThrow("lock");
|
lock = instances.childOrThrow("lock");
|
||||||
|
|
||||||
|
|
|
@ -14,8 +14,7 @@ import dev.engine_room.flywheel.lib.instance.TransformedInstance;
|
||||||
import dev.engine_room.flywheel.lib.material.SimpleMaterial;
|
import dev.engine_room.flywheel.lib.material.SimpleMaterial;
|
||||||
import dev.engine_room.flywheel.lib.model.Models;
|
import dev.engine_room.flywheel.lib.model.Models;
|
||||||
import dev.engine_room.flywheel.lib.model.part.InstanceTree;
|
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.ModelTrees;
|
||||||
import dev.engine_room.flywheel.lib.model.part.ModelTree;
|
|
||||||
import dev.engine_room.flywheel.lib.visual.ComponentEntityVisual;
|
import dev.engine_room.flywheel.lib.visual.ComponentEntityVisual;
|
||||||
import dev.engine_room.flywheel.lib.visual.SimpleDynamicVisual;
|
import dev.engine_room.flywheel.lib.visual.SimpleDynamicVisual;
|
||||||
import dev.engine_room.flywheel.lib.visual.SimpleTickableVisual;
|
import dev.engine_room.flywheel.lib.visual.SimpleTickableVisual;
|
||||||
|
@ -36,7 +35,6 @@ public class MinecartVisual<T extends AbstractMinecart> extends ComponentEntityV
|
||||||
.texture(TEXTURE)
|
.texture(TEXTURE)
|
||||||
.mipmap(false)
|
.mipmap(false)
|
||||||
.build();
|
.build();
|
||||||
private static final LoweringVisitor VISITOR = LoweringVisitor.create(MATERIAL);
|
|
||||||
|
|
||||||
private final InstanceTree instances;
|
private final InstanceTree instances;
|
||||||
@Nullable
|
@Nullable
|
||||||
|
@ -50,7 +48,7 @@ public class MinecartVisual<T extends AbstractMinecart> extends ComponentEntityV
|
||||||
public MinecartVisual(VisualizationContext ctx, T entity, float partialTick, ModelLayerLocation layerLocation) {
|
public MinecartVisual(VisualizationContext ctx, T entity, float partialTick, ModelLayerLocation layerLocation) {
|
||||||
super(ctx, entity, partialTick);
|
super(ctx, entity, partialTick);
|
||||||
|
|
||||||
instances = InstanceTree.create(instancerProvider(), ModelTree.of(layerLocation, VISITOR));
|
instances = InstanceTree.create(instancerProvider(), ModelTrees.of(layerLocation, MATERIAL));
|
||||||
blockState = entity.getDisplayBlockState();
|
blockState = entity.getDisplayBlockState();
|
||||||
contents = createContentsInstance();
|
contents = createContentsInstance();
|
||||||
|
|
||||||
|
|
|
@ -2,23 +2,20 @@ package dev.engine_room.flywheel.vanilla;
|
||||||
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Function;
|
|
||||||
|
|
||||||
import org.joml.Matrix4f;
|
import org.joml.Matrix4f;
|
||||||
|
|
||||||
import dev.engine_room.flywheel.api.instance.Instance;
|
import dev.engine_room.flywheel.api.instance.Instance;
|
||||||
|
import dev.engine_room.flywheel.api.material.Material;
|
||||||
import dev.engine_room.flywheel.api.visualization.VisualizationContext;
|
import dev.engine_room.flywheel.api.visualization.VisualizationContext;
|
||||||
import dev.engine_room.flywheel.lib.material.CutoutShaders;
|
import dev.engine_room.flywheel.lib.material.CutoutShaders;
|
||||||
import dev.engine_room.flywheel.lib.material.SimpleMaterial;
|
import dev.engine_room.flywheel.lib.material.SimpleMaterial;
|
||||||
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.InstanceTree;
|
||||||
import dev.engine_room.flywheel.lib.model.part.LoweringVisitor;
|
import dev.engine_room.flywheel.lib.model.part.ModelTrees;
|
||||||
import dev.engine_room.flywheel.lib.model.part.ModelTree;
|
|
||||||
import dev.engine_room.flywheel.lib.visual.AbstractBlockEntityVisual;
|
import dev.engine_room.flywheel.lib.visual.AbstractBlockEntityVisual;
|
||||||
import dev.engine_room.flywheel.lib.visual.SimpleDynamicVisual;
|
import dev.engine_room.flywheel.lib.visual.SimpleDynamicVisual;
|
||||||
import net.minecraft.client.model.geom.ModelLayers;
|
import net.minecraft.client.model.geom.ModelLayers;
|
||||||
import net.minecraft.client.renderer.Sheets;
|
import net.minecraft.client.renderer.Sheets;
|
||||||
import net.minecraft.client.resources.model.Material;
|
|
||||||
import net.minecraft.core.Direction;
|
import net.minecraft.core.Direction;
|
||||||
import net.minecraft.util.Mth;
|
import net.minecraft.util.Mth;
|
||||||
import net.minecraft.world.item.DyeColor;
|
import net.minecraft.world.item.DyeColor;
|
||||||
|
@ -26,15 +23,13 @@ import net.minecraft.world.level.block.ShulkerBoxBlock;
|
||||||
import net.minecraft.world.level.block.entity.ShulkerBoxBlockEntity;
|
import net.minecraft.world.level.block.entity.ShulkerBoxBlockEntity;
|
||||||
|
|
||||||
public class ShulkerBoxVisual extends AbstractBlockEntityVisual<ShulkerBoxBlockEntity> implements SimpleDynamicVisual {
|
public class ShulkerBoxVisual extends AbstractBlockEntityVisual<ShulkerBoxBlockEntity> implements SimpleDynamicVisual {
|
||||||
private static final dev.engine_room.flywheel.api.material.Material MATERIAL = SimpleMaterial.builder()
|
private static final Material MATERIAL = SimpleMaterial.builder()
|
||||||
.cutout(CutoutShaders.ONE_TENTH)
|
.cutout(CutoutShaders.ONE_TENTH)
|
||||||
.texture(Sheets.SHULKER_SHEET)
|
.texture(Sheets.SHULKER_SHEET)
|
||||||
.mipmap(false)
|
.mipmap(false)
|
||||||
.backfaceCulling(false)
|
.backfaceCulling(false)
|
||||||
.build();
|
.build();
|
||||||
|
private static final Set<String> PATHS_TO_PRUNE = Set.of("/head");
|
||||||
|
|
||||||
private static final Function<Material, LoweringVisitor> VISITORS = new ResourceReloadCache<>(m -> LoweringVisitor.pruning(Set.of("head"), MATERIAL, m.sprite()));
|
|
||||||
|
|
||||||
private final InstanceTree instances;
|
private final InstanceTree instances;
|
||||||
private final InstanceTree lid;
|
private final InstanceTree lid;
|
||||||
|
@ -47,23 +42,22 @@ public class ShulkerBoxVisual extends AbstractBlockEntityVisual<ShulkerBoxBlockE
|
||||||
super(ctx, blockEntity, partialTick);
|
super(ctx, blockEntity, partialTick);
|
||||||
|
|
||||||
DyeColor color = blockEntity.getColor();
|
DyeColor color = blockEntity.getColor();
|
||||||
Material texture;
|
net.minecraft.client.resources.model.Material texture;
|
||||||
if (color == null) {
|
if (color == null) {
|
||||||
texture = Sheets.DEFAULT_SHULKER_TEXTURE_LOCATION;
|
texture = Sheets.DEFAULT_SHULKER_TEXTURE_LOCATION;
|
||||||
} else {
|
} else {
|
||||||
texture = Sheets.SHULKER_TEXTURE_LOCATION.get(color.getId());
|
texture = Sheets.SHULKER_TEXTURE_LOCATION.get(color.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
instances = InstanceTree.create(instancerProvider(), ModelTree.of(ModelLayers.SHULKER, VISITORS.apply(texture)));
|
instances = InstanceTree.create(instancerProvider(), ModelTrees.of(ModelLayers.SHULKER, PATHS_TO_PRUNE, texture, MATERIAL));
|
||||||
|
lid = instances.childOrThrow("lid");
|
||||||
|
|
||||||
initialPose = createInitialPose();
|
initialPose = createInitialPose();
|
||||||
|
|
||||||
lid = instances.childOrThrow("lid");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Matrix4f createInitialPose() {
|
private Matrix4f createInitialPose() {
|
||||||
var rotation = getDirection().getRotation();
|
|
||||||
var visualPosition = getVisualPosition();
|
var visualPosition = getVisualPosition();
|
||||||
|
var rotation = getDirection().getRotation();
|
||||||
return new Matrix4f().translate(visualPosition.getX(), visualPosition.getY(), visualPosition.getZ())
|
return new Matrix4f().translate(visualPosition.getX(), visualPosition.getY(), visualPosition.getZ())
|
||||||
.translate(0.5f, 0.5f, 0.5f)
|
.translate(0.5f, 0.5f, 0.5f)
|
||||||
.scale(0.9995f)
|
.scale(0.9995f)
|
||||||
|
|
|
@ -12,8 +12,6 @@ import dev.engine_room.flywheel.impl.visualization.VisualizationEventHandler;
|
||||||
import dev.engine_room.flywheel.lib.model.ResourceReloadCache;
|
import dev.engine_room.flywheel.lib.model.ResourceReloadCache;
|
||||||
import dev.engine_room.flywheel.lib.model.ResourceReloadHolder;
|
import dev.engine_room.flywheel.lib.model.ResourceReloadHolder;
|
||||||
import dev.engine_room.flywheel.lib.model.baked.PartialModelEventHandler;
|
import dev.engine_room.flywheel.lib.model.baked.PartialModelEventHandler;
|
||||||
import dev.engine_room.flywheel.lib.model.part.MeshTree;
|
|
||||||
import dev.engine_room.flywheel.lib.model.part.ModelTree;
|
|
||||||
import net.fabricmc.api.ClientModInitializer;
|
import net.fabricmc.api.ClientModInitializer;
|
||||||
import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback;
|
import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback;
|
||||||
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientEntityEvents;
|
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientEntityEvents;
|
||||||
|
@ -71,9 +69,6 @@ public final class FlywheelFabric implements ClientModInitializer {
|
||||||
private static void setupLib() {
|
private static void setupLib() {
|
||||||
EndClientResourceReloadCallback.EVENT.register((minecraft, resourceManager, initialReload, error) -> ResourceReloadCache.onEndClientResourceReload());
|
EndClientResourceReloadCallback.EVENT.register((minecraft, resourceManager, initialReload, error) -> ResourceReloadCache.onEndClientResourceReload());
|
||||||
EndClientResourceReloadCallback.EVENT.register((minecraft, resourceManager, initialReload, error) -> ResourceReloadHolder.onEndClientResourceReload());
|
EndClientResourceReloadCallback.EVENT.register((minecraft, resourceManager, initialReload, error) -> ResourceReloadHolder.onEndClientResourceReload());
|
||||||
EndClientResourceReloadCallback.EVENT.register((minecraft, resourceManager, initialReload, error) ->
|
|
||||||
MeshTree.onEndClientResourceReload());
|
|
||||||
EndClientResourceReloadCallback.EVENT.register((minecraft, resourceManager, initialReload, error) -> ModelTree.onEndClientResourceReload());
|
|
||||||
|
|
||||||
ModelLoadingPlugin.register(ctx -> {
|
ModelLoadingPlugin.register(ctx -> {
|
||||||
ctx.addModels(PartialModelEventHandler.onRegisterAdditional());
|
ctx.addModels(PartialModelEventHandler.onRegisterAdditional());
|
||||||
|
|
|
@ -13,8 +13,6 @@ import dev.engine_room.flywheel.impl.visualization.VisualizationEventHandler;
|
||||||
import dev.engine_room.flywheel.lib.model.ResourceReloadCache;
|
import dev.engine_room.flywheel.lib.model.ResourceReloadCache;
|
||||||
import dev.engine_room.flywheel.lib.model.ResourceReloadHolder;
|
import dev.engine_room.flywheel.lib.model.ResourceReloadHolder;
|
||||||
import dev.engine_room.flywheel.lib.model.baked.PartialModelEventHandler;
|
import dev.engine_room.flywheel.lib.model.baked.PartialModelEventHandler;
|
||||||
import dev.engine_room.flywheel.lib.model.part.MeshTree;
|
|
||||||
import dev.engine_room.flywheel.lib.model.part.ModelTree;
|
|
||||||
import dev.engine_room.flywheel.lib.util.LevelAttached;
|
import dev.engine_room.flywheel.lib.util.LevelAttached;
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.commands.synchronization.ArgumentTypeInfos;
|
import net.minecraft.commands.synchronization.ArgumentTypeInfos;
|
||||||
|
@ -113,8 +111,6 @@ public final class FlywheelForge {
|
||||||
|
|
||||||
modEventBus.addListener((EndClientResourceReloadEvent e) -> ResourceReloadCache.onEndClientResourceReload());
|
modEventBus.addListener((EndClientResourceReloadEvent e) -> ResourceReloadCache.onEndClientResourceReload());
|
||||||
modEventBus.addListener((EndClientResourceReloadEvent e) -> ResourceReloadHolder.onEndClientResourceReload());
|
modEventBus.addListener((EndClientResourceReloadEvent e) -> ResourceReloadHolder.onEndClientResourceReload());
|
||||||
modEventBus.addListener((EndClientResourceReloadEvent e) -> MeshTree.onEndClientResourceReload());
|
|
||||||
modEventBus.addListener((EndClientResourceReloadEvent e) -> ModelTree.onEndClientResourceReload());
|
|
||||||
|
|
||||||
modEventBus.addListener(PartialModelEventHandler::onRegisterAdditional);
|
modEventBus.addListener(PartialModelEventHandler::onRegisterAdditional);
|
||||||
modEventBus.addListener(PartialModelEventHandler::onBakingCompleted);
|
modEventBus.addListener(PartialModelEventHandler::onBakingCompleted);
|
||||||
|
|
Loading…
Reference in a new issue