mirror of
https://github.com/Jozufozu/Flywheel.git
synced 2025-01-24 20:07:54 +01:00
One big happy family
- Add ModelTree - Add LoweringVisitor to traverse a MeshTree and emit ModelTree nodes and Models - Provide some default visitor creation methods - Abstract ModelCache -> ResourceReloadCache - Abstract ModelHolder -> ResourceReloadHolder - Add ModelTreeCache to hide lookup cost if it gets extreme
This commit is contained in:
parent
904933e22e
commit
31b3507d62
13 changed files with 361 additions and 141 deletions
|
@ -1,40 +1,11 @@
|
|||
package dev.engine_room.flywheel.lib.model;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
import dev.engine_room.flywheel.api.model.Model;
|
||||
import dev.engine_room.flywheel.lib.util.FlwUtil;
|
||||
|
||||
public final class ModelCache<T> {
|
||||
private static final Set<ModelCache<?>> ALL = FlwUtil.createWeakHashSet();
|
||||
private final Function<T, Model> factory;
|
||||
private final Map<T, Model> map = new ConcurrentHashMap<>();
|
||||
|
||||
public final class ModelCache<T> extends ResourceReloadCache<T, Model> {
|
||||
public ModelCache(Function<T, Model> factory) {
|
||||
this.factory = factory;
|
||||
|
||||
synchronized (ALL) {
|
||||
ALL.add(this);
|
||||
}
|
||||
}
|
||||
|
||||
public Model get(T key) {
|
||||
return map.computeIfAbsent(key, factory);
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
map.clear();
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
public static void onEndClientResourceReload() {
|
||||
for (ModelCache<?> cache : ALL) {
|
||||
cache.clear();
|
||||
}
|
||||
super(factory);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,60 +1,11 @@
|
|||
package dev.engine_room.flywheel.lib.model;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import dev.engine_room.flywheel.api.model.Model;
|
||||
import dev.engine_room.flywheel.lib.util.FlwUtil;
|
||||
|
||||
public final class ModelHolder {
|
||||
private static final Set<ModelHolder> ALL = FlwUtil.createWeakHashSet();
|
||||
private final Supplier<Model> factory;
|
||||
@Nullable
|
||||
private volatile Model model;
|
||||
|
||||
public final class ModelHolder extends ResourceReloadHolder<Model> {
|
||||
public ModelHolder(Supplier<Model> factory) {
|
||||
this.factory = factory;
|
||||
|
||||
synchronized (ALL) {
|
||||
ALL.add(this);
|
||||
}
|
||||
}
|
||||
|
||||
public Model get() {
|
||||
Model model = this.model;
|
||||
|
||||
if (model == null) {
|
||||
synchronized (this) {
|
||||
model = this.model;
|
||||
if (model == null) {
|
||||
this.model = model = factory.get();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
Model model = this.model;
|
||||
|
||||
if (model != null) {
|
||||
synchronized (this) {
|
||||
model = this.model;
|
||||
if (model != null) {
|
||||
this.model = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
public static void onEndClientResourceReload() {
|
||||
for (ModelHolder holder : ALL) {
|
||||
holder.clear();
|
||||
}
|
||||
super(factory);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
package dev.engine_room.flywheel.lib.model;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
import dev.engine_room.flywheel.lib.util.FlwUtil;
|
||||
|
||||
public class ResourceReloadCache<T, U> implements Function<T, U> {
|
||||
private static final Set<ResourceReloadCache<?, ?>> ALL = FlwUtil.createWeakHashSet();
|
||||
private final Function<T, U> factory;
|
||||
private final Map<T, U> map = new ConcurrentHashMap<>();
|
||||
|
||||
public ResourceReloadCache(Function<T, U> factory) {
|
||||
this.factory = factory;
|
||||
|
||||
synchronized (ALL) {
|
||||
ALL.add(this);
|
||||
}
|
||||
}
|
||||
|
||||
public final U get(T key) {
|
||||
return map.computeIfAbsent(key, factory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final U apply(T t) {
|
||||
return get(t);
|
||||
}
|
||||
|
||||
public final void clear() {
|
||||
map.clear();
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
public static void onEndClientResourceReload() {
|
||||
for (ResourceReloadCache<?, ?> cache : ALL) {
|
||||
cache.clear();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
package dev.engine_room.flywheel.lib.model;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import dev.engine_room.flywheel.lib.util.FlwUtil;
|
||||
|
||||
public class ResourceReloadHolder<T> implements Supplier<T> {
|
||||
private static final Set<ResourceReloadHolder<?>> ALL = FlwUtil.createWeakHashSet();
|
||||
private final Supplier<T> factory;
|
||||
@Nullable
|
||||
private volatile T obj;
|
||||
|
||||
public ResourceReloadHolder(Supplier<T> factory) {
|
||||
this.factory = factory;
|
||||
|
||||
synchronized (ALL) {
|
||||
ALL.add(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final T get() {
|
||||
T obj = this.obj;
|
||||
|
||||
if (obj == null) {
|
||||
synchronized (this) {
|
||||
obj = this.obj;
|
||||
if (obj == null) {
|
||||
this.obj = obj = factory.get();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
public final void clear() {
|
||||
T obj = this.obj;
|
||||
|
||||
if (obj != null) {
|
||||
synchronized (this) {
|
||||
obj = this.obj;
|
||||
if (obj != null) {
|
||||
this.obj = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
public static void onEndClientResourceReload() {
|
||||
for (ResourceReloadHolder<?> holder : ALL) {
|
||||
holder.clear();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,7 +1,6 @@
|
|||
package dev.engine_room.flywheel.lib.model.part;
|
||||
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.ObjIntConsumer;
|
||||
|
||||
|
@ -15,23 +14,16 @@ import org.joml.Vector3fc;
|
|||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
|
||||
import dev.engine_room.flywheel.api.instance.InstancerProvider;
|
||||
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.instance.InstanceTypes;
|
||||
import dev.engine_room.flywheel.lib.instance.TransformedInstance;
|
||||
import dev.engine_room.flywheel.lib.model.ModelCache;
|
||||
import dev.engine_room.flywheel.lib.model.SingleMeshModel;
|
||||
import dev.engine_room.flywheel.lib.transform.Affine;
|
||||
import dev.engine_room.flywheel.lib.transform.TransformStack;
|
||||
import net.minecraft.client.model.geom.ModelLayerLocation;
|
||||
import net.minecraft.client.model.geom.ModelPart;
|
||||
import net.minecraft.client.model.geom.PartPose;
|
||||
|
||||
public final class InstanceTree {
|
||||
private static final ModelCache<Model.ConfiguredMesh> MODEL_CACHE = new ModelCache<>(entry -> new SingleMeshModel(entry.mesh(), entry.material()));
|
||||
|
||||
private final MeshTree source;
|
||||
private final ModelTree source;
|
||||
@Nullable
|
||||
private final TransformedInstance instance;
|
||||
private final InstanceTree[] children;
|
||||
|
@ -54,7 +46,7 @@ public final class InstanceTree {
|
|||
|
||||
private boolean changed;
|
||||
|
||||
private InstanceTree(MeshTree source, @Nullable TransformedInstance instance, InstanceTree[] children) {
|
||||
private InstanceTree(ModelTree source, @Nullable TransformedInstance instance, InstanceTree[] children) {
|
||||
this.source = source;
|
||||
this.instance = instance;
|
||||
this.children = children;
|
||||
|
@ -68,21 +60,16 @@ public final class InstanceTree {
|
|||
resetPose();
|
||||
}
|
||||
|
||||
private static InstanceTree create(InstancerProvider provider, MeshTree meshTree, BiFunction<String, Mesh, Model.ConfiguredMesh> meshFinalizerFunc, String path) {
|
||||
public static InstanceTree create(InstancerProvider provider, ModelTree meshTree) {
|
||||
InstanceTree[] children = new InstanceTree[meshTree.childCount()];
|
||||
String pathSlash = path + "/";
|
||||
|
||||
for (int i = 0; i < meshTree.childCount(); i++) {
|
||||
var meshTreeChild = meshTree.child(i);
|
||||
String name = meshTree.childName(i);
|
||||
children[i] = create(provider, meshTreeChild, meshFinalizerFunc, pathSlash + name);
|
||||
children[i] = create(provider, meshTree.child(i));
|
||||
}
|
||||
|
||||
Mesh mesh = meshTree.mesh();
|
||||
Model model = meshTree.model();
|
||||
TransformedInstance instance;
|
||||
if (mesh != null) {
|
||||
Model.ConfiguredMesh configuredMesh = meshFinalizerFunc.apply(path, mesh);
|
||||
instance = provider.instancer(InstanceTypes.TRANSFORMED, MODEL_CACHE.get(configuredMesh))
|
||||
if (model != null) {
|
||||
instance = provider.instancer(InstanceTypes.TRANSFORMED, model)
|
||||
.createInstance();
|
||||
} else {
|
||||
instance = null;
|
||||
|
@ -91,22 +78,6 @@ public final class InstanceTree {
|
|||
return new InstanceTree(meshTree, instance, children);
|
||||
}
|
||||
|
||||
public static InstanceTree create(InstancerProvider provider, MeshTree meshTree, BiFunction<String, Mesh, Model.ConfiguredMesh> meshFinalizerFunc) {
|
||||
return create(provider, meshTree, meshFinalizerFunc, "");
|
||||
}
|
||||
|
||||
public static InstanceTree create(InstancerProvider provider, ModelLayerLocation layer, BiFunction<String, Mesh, Model.ConfiguredMesh> meshFinalizerFunc) {
|
||||
return create(provider, MeshTree.of(layer), meshFinalizerFunc);
|
||||
}
|
||||
|
||||
public static InstanceTree create(InstancerProvider provider, MeshTree meshTree, Material material) {
|
||||
return create(provider, meshTree, (path, mesh) -> new Model.ConfiguredMesh(material, mesh));
|
||||
}
|
||||
|
||||
public static InstanceTree create(InstancerProvider provider, ModelLayerLocation layer, Material material) {
|
||||
return create(provider, MeshTree.of(layer), material);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public TransformedInstance instance() {
|
||||
return instance;
|
||||
|
|
|
@ -0,0 +1,93 @@
|
|||
package dev.engine_room.flywheel.lib.model.part;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
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 materialApplyingVisitor(Material material) {
|
||||
return (path, mesh) -> new SingleMeshModel(mesh, material);
|
||||
}
|
||||
|
||||
static LoweringVisitor retexturingVisitor(Material material, TextureAtlasSprite sprite) {
|
||||
return (path, mesh) -> 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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,107 @@
|
|||
package dev.engine_room.flywheel.lib.model.part;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import dev.engine_room.flywheel.api.model.Model;
|
||||
import net.minecraft.client.model.geom.ModelLayerLocation;
|
||||
import net.minecraft.client.model.geom.PartPose;
|
||||
|
||||
public class ModelTree {
|
||||
private static final Map<ModelTreeKey, ModelTree> CACHE = new ConcurrentHashMap<>();
|
||||
|
||||
@Nullable
|
||||
private final Model model;
|
||||
private final PartPose initialPose;
|
||||
private final ModelTree[] children;
|
||||
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.
|
||||
*
|
||||
* <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 initialPose The initial pose 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) {
|
||||
if (children.length != childNames.length) {
|
||||
throw new IllegalArgumentException("children and childNames must have the same length (%s != %s)".formatted(children.length, childNames.length));
|
||||
}
|
||||
|
||||
return new ModelTree(model, initialPose, children, childNames);
|
||||
}
|
||||
|
||||
public int childCount() {
|
||||
return children.length;
|
||||
}
|
||||
|
||||
public ModelTree child(int index) {
|
||||
return children[index];
|
||||
}
|
||||
|
||||
public String childName(int index) {
|
||||
return childNames[index];
|
||||
}
|
||||
|
||||
public PartPose initialPose() {
|
||||
return initialPose;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Model model() {
|
||||
return model;
|
||||
}
|
||||
|
||||
public int childIndex(String name) {
|
||||
return Arrays.binarySearch(childNames, name);
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
public static void onEndClientResourceReload() {
|
||||
CACHE.clear();
|
||||
}
|
||||
|
||||
private record ModelTreeKey(ModelLayerLocation modelLayerLocation, LoweringVisitor loweringVisitor) {
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
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);
|
||||
}
|
||||
}
|
|
@ -7,16 +7,16 @@ 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.SimpleMaterial;
|
||||
import dev.engine_room.flywheel.lib.model.RetexturedMesh;
|
||||
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.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.blockentity.BellRenderer;
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.world.level.block.entity.BellBlockEntity;
|
||||
|
@ -26,6 +26,9 @@ public class BellVisual extends AbstractBlockEntityVisual<BellBlockEntity> imple
|
|||
.mipmap(false)
|
||||
.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 final InstanceTree instances;
|
||||
private final InstanceTree bellBody;
|
||||
|
||||
|
@ -36,10 +39,7 @@ public class BellVisual extends AbstractBlockEntityVisual<BellBlockEntity> imple
|
|||
public BellVisual(VisualizationContext ctx, BellBlockEntity blockEntity, float partialTick) {
|
||||
super(ctx, blockEntity, partialTick);
|
||||
|
||||
TextureAtlasSprite sprite = BellRenderer.BELL_RESOURCE_LOCATION.sprite();
|
||||
instances = InstanceTree.create(instancerProvider(), ModelLayers.BELL, (path, mesh) -> {
|
||||
return new Model.ConfiguredMesh(MATERIAL, new RetexturedMesh(mesh, sprite));
|
||||
});
|
||||
instances = InstanceTree.create(instancerProvider(), ModelTree.of(ModelLayers.BELL, VISITOR.get()));
|
||||
bellBody = instances.childOrThrow("bell_body");
|
||||
|
||||
BlockPos visualPos = getVisualPosition();
|
||||
|
|
|
@ -4,6 +4,7 @@ import java.util.Calendar;
|
|||
import java.util.EnumMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.joml.Matrix4f;
|
||||
|
@ -15,8 +16,11 @@ 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;
|
||||
import dev.engine_room.flywheel.lib.model.RetexturedMesh;
|
||||
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;
|
||||
|
@ -53,6 +57,8 @@ 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));
|
||||
|
||||
@Nullable
|
||||
private final InstanceTree instances;
|
||||
@Nullable
|
||||
|
@ -77,10 +83,7 @@ public class ChestVisual<T extends BlockEntity & LidBlockEntity> extends Abstrac
|
|||
if (block instanceof AbstractChestBlock<?> chestBlock) {
|
||||
ChestType chestType = blockState.hasProperty(ChestBlock.TYPE) ? blockState.getValue(ChestBlock.TYPE) : ChestType.SINGLE;
|
||||
TextureAtlasSprite sprite = Sheets.chooseMaterial(blockEntity, chestType, isChristmas()).sprite();
|
||||
|
||||
instances = InstanceTree.create(instancerProvider(), LAYER_LOCATIONS.get(chestType), (path, mesh) -> {
|
||||
return new Model.ConfiguredMesh(MATERIAL, new RetexturedMesh(mesh, sprite));
|
||||
});
|
||||
instances = InstanceTree.create(instancerProvider(), ModelTree.of(LAYER_LOCATIONS.get(chestType), VISITOR.apply(sprite)));
|
||||
lid = instances.childOrThrow("lid");
|
||||
lock = instances.childOrThrow("lock");
|
||||
|
||||
|
|
|
@ -14,6 +14,8 @@ import dev.engine_room.flywheel.lib.instance.TransformedInstance;
|
|||
import dev.engine_room.flywheel.lib.material.SimpleMaterial;
|
||||
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.LoweringVisitor;
|
||||
import dev.engine_room.flywheel.lib.model.part.ModelTree;
|
||||
import dev.engine_room.flywheel.lib.visual.ComponentEntityVisual;
|
||||
import dev.engine_room.flywheel.lib.visual.SimpleDynamicVisual;
|
||||
import dev.engine_room.flywheel.lib.visual.SimpleTickableVisual;
|
||||
|
@ -34,6 +36,7 @@ public class MinecartVisual<T extends AbstractMinecart> extends ComponentEntityV
|
|||
.texture(TEXTURE)
|
||||
.mipmap(false)
|
||||
.build();
|
||||
private static final LoweringVisitor VISITOR = LoweringVisitor.materialApplyingVisitor(MATERIAL);
|
||||
|
||||
private final InstanceTree instances;
|
||||
@Nullable
|
||||
|
@ -47,7 +50,7 @@ public class MinecartVisual<T extends AbstractMinecart> extends ComponentEntityV
|
|||
public MinecartVisual(VisualizationContext ctx, T entity, float partialTick, ModelLayerLocation layerLocation) {
|
||||
super(ctx, entity, partialTick);
|
||||
|
||||
instances = InstanceTree.create(instancerProvider(), layerLocation, MATERIAL);
|
||||
instances = InstanceTree.create(instancerProvider(), ModelTree.of(layerLocation, VISITOR));
|
||||
blockState = entity.getDisplayBlockState();
|
||||
contents = createContentsInstance();
|
||||
|
||||
|
|
|
@ -9,10 +9,11 @@ import dev.engine_room.flywheel.backend.LightSmoothnessArgument;
|
|||
import dev.engine_room.flywheel.backend.compile.FlwProgramsReloader;
|
||||
import dev.engine_room.flywheel.backend.engine.uniform.Uniforms;
|
||||
import dev.engine_room.flywheel.impl.visualization.VisualizationEventHandler;
|
||||
import dev.engine_room.flywheel.lib.model.ModelCache;
|
||||
import dev.engine_room.flywheel.lib.model.ModelHolder;
|
||||
import dev.engine_room.flywheel.lib.model.ResourceReloadCache;
|
||||
import dev.engine_room.flywheel.lib.model.ResourceReloadHolder;
|
||||
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.fabric.api.client.command.v2.ClientCommandRegistrationCallback;
|
||||
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientEntityEvents;
|
||||
|
@ -68,12 +69,11 @@ public final class FlywheelFabric implements ClientModInitializer {
|
|||
}
|
||||
|
||||
private static void setupLib() {
|
||||
EndClientResourceReloadCallback.EVENT.register((minecraft, resourceManager, initialReload, error) ->
|
||||
ModelCache.onEndClientResourceReload());
|
||||
EndClientResourceReloadCallback.EVENT.register((minecraft, resourceManager, initialReload, error) ->
|
||||
ModelHolder.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) ->
|
||||
MeshTree.onEndClientResourceReload());
|
||||
EndClientResourceReloadCallback.EVENT.register((minecraft, resourceManager, initialReload, error) -> ModelTree.onEndClientResourceReload());
|
||||
|
||||
ModelLoadingPlugin.register(ctx -> {
|
||||
ctx.addModels(PartialModelEventHandler.onRegisterAdditional());
|
||||
|
|
|
@ -10,10 +10,11 @@ import dev.engine_room.flywheel.backend.LightSmoothnessArgument;
|
|||
import dev.engine_room.flywheel.backend.compile.FlwProgramsReloader;
|
||||
import dev.engine_room.flywheel.backend.engine.uniform.Uniforms;
|
||||
import dev.engine_room.flywheel.impl.visualization.VisualizationEventHandler;
|
||||
import dev.engine_room.flywheel.lib.model.ModelCache;
|
||||
import dev.engine_room.flywheel.lib.model.ModelHolder;
|
||||
import dev.engine_room.flywheel.lib.model.ResourceReloadCache;
|
||||
import dev.engine_room.flywheel.lib.model.ResourceReloadHolder;
|
||||
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 net.minecraft.client.Minecraft;
|
||||
import net.minecraft.commands.synchronization.ArgumentTypeInfos;
|
||||
|
@ -110,9 +111,10 @@ public final class FlywheelForge {
|
|||
private static void registerLibEventListeners(IEventBus forgeEventBus, IEventBus modEventBus) {
|
||||
forgeEventBus.addListener((LevelEvent.Unload e) -> LevelAttached.invalidateLevel(e.getLevel()));
|
||||
|
||||
modEventBus.addListener((EndClientResourceReloadEvent e) -> ModelCache.onEndClientResourceReload());
|
||||
modEventBus.addListener((EndClientResourceReloadEvent e) -> ModelHolder.onEndClientResourceReload());
|
||||
modEventBus.addListener((EndClientResourceReloadEvent e) -> ResourceReloadCache.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::onBakingCompleted);
|
||||
|
|
Loading…
Reference in a new issue