Treefactors

- Add MeshTree and InstanceTree
- Deprecate ModelPartConverter for removal
- Refactor ChestVisual to use InstanceTree
- Combine double chest light in ChestVisual
This commit is contained in:
PepperCode1 2024-09-14 15:00:27 -07:00
parent ddb0450105
commit 295ebc7573
13 changed files with 668 additions and 83 deletions

View file

@ -1,11 +1,15 @@
package dev.engine_room.flywheel.lib.internal;
import java.util.Map;
import org.slf4j.Logger;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import dev.engine_room.flywheel.api.internal.DependencyInjection;
import dev.engine_room.flywheel.lib.transform.PoseTransformStack;
import net.minecraft.client.model.geom.ModelPart;
public interface FlwLibLink {
FlwLibLink INSTANCE = DependencyInjection.load(FlwLibLink.class, "dev.engine_room.flywheel.impl.FlwLibLinkImpl");
@ -13,4 +17,8 @@ public interface FlwLibLink {
Logger getLogger();
PoseTransformStack getPoseTransformStackOf(PoseStack stack);
Map<String, ModelPart> getModelPartChildren(ModelPart part);
void compileModelPart(ModelPart part, PoseStack.Pose pose, VertexConsumer consumer, int light, int overlay, float red, float green, float blue, float alpha);
}

View file

@ -0,0 +1,38 @@
package dev.engine_room.flywheel.lib.model;
import org.joml.Vector4fc;
import dev.engine_room.flywheel.api.model.IndexSequence;
import dev.engine_room.flywheel.api.model.Mesh;
import dev.engine_room.flywheel.api.vertex.MutableVertexList;
import dev.engine_room.flywheel.lib.vertex.VertexTransformations;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
public record RetexturedMesh(Mesh mesh, TextureAtlasSprite sprite) implements Mesh {
@Override
public int vertexCount() {
return mesh.vertexCount();
}
@Override
public void write(MutableVertexList vertexList) {
mesh.write(vertexList);
VertexTransformations.retexture(vertexList, sprite);
}
@Override
public IndexSequence indexSequence() {
return mesh.indexSequence();
}
@Override
public int indexCount() {
return mesh.indexCount();
}
@Override
public Vector4fc boundingSphere() {
return mesh.boundingSphere();
}
}

View file

@ -0,0 +1,295 @@
package dev.engine_room.flywheel.lib.model.part;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.ObjIntConsumer;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.Unmodifiable;
import org.joml.Quaternionf;
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 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()));
@Nullable
private final TransformedInstance instance;
private final PartPose initialPose;
@Unmodifiable
private final Map<String, InstanceTree> children;
private final Quaternionf rotation = new Quaternionf();
public float x;
public float y;
public float z;
public float xRot;
public float yRot;
public float zRot;
public float xScale;
public float yScale;
public float zScale;
public boolean visible = true;
public boolean skipDraw;
private InstanceTree(@Nullable TransformedInstance instance, PartPose initialPose, @Unmodifiable Map<String, InstanceTree> children) {
this.instance = instance;
this.initialPose = initialPose;
this.children = children;
resetPose();
}
private static InstanceTree create(InstancerProvider provider, MeshTree meshTree, BiFunction<String, Mesh, Model.ConfiguredMesh> meshFinalizerFunc, String path) {
Map<String, InstanceTree> children = new HashMap<>();
String pathSlash = path + "/";
meshTree.children().forEach((name, meshTreeChild) -> {
children.put(name, InstanceTree.create(provider, meshTreeChild, meshFinalizerFunc, pathSlash + name));
});
Mesh mesh = meshTree.mesh();
TransformedInstance instance;
if (mesh != null) {
Model.ConfiguredMesh configuredMesh = meshFinalizerFunc.apply(path, mesh);
instance = provider.instancer(InstanceTypes.TRANSFORMED, MODEL_CACHE.get(configuredMesh))
.createInstance();
} else {
instance = null;
}
return new InstanceTree(instance, meshTree.initialPose(), Collections.unmodifiableMap(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;
}
public PartPose initialPose() {
return initialPose;
}
@Unmodifiable
public Map<String, InstanceTree> children() {
return children;
}
public boolean hasChild(String name) {
return children.containsKey(name);
}
@Nullable
public InstanceTree child(String name) {
return children.get(name);
}
public InstanceTree childOrThrow(String name) {
InstanceTree child = child(name);
if (child == null) {
throw new NoSuchElementException("Can't find part " + name);
}
return child;
}
public void traverse(Consumer<? super TransformedInstance> consumer) {
if (instance != null) {
consumer.accept(instance);
}
for (InstanceTree child : children.values()) {
child.traverse(consumer);
}
}
@ApiStatus.Experimental
public void traverse(int i, ObjIntConsumer<? super TransformedInstance> consumer) {
if (instance != null) {
consumer.accept(instance, i);
}
for (InstanceTree child : children.values()) {
child.traverse(i, consumer);
}
}
@ApiStatus.Experimental
public void traverse(int i, int j, ObjIntIntConsumer<? super TransformedInstance> consumer) {
if (instance != null) {
consumer.accept(instance, i, j);
}
for (InstanceTree child : children.values()) {
child.traverse(i, j, consumer);
}
}
public void translateAndRotate(PoseStack poseStack) {
poseStack.translate(x / 16.0F, y / 16.0F, z / 16.0F);
if (xRot != 0.0F || yRot != 0.0F || zRot != 0.0F) {
poseStack.mulPose(rotation.rotationZYX(zRot, yRot, xRot));
}
if (xScale != 1.0F || yScale != 1.0F || zScale != 1.0F) {
poseStack.scale(xScale, yScale, zScale);
}
}
public void updateInstances(PoseStack poseStack) {
if (visible) {
poseStack.pushPose();
translateAndRotate(poseStack);
if (instance != null && !skipDraw) {
instance.setTransform(poseStack.last())
.setChanged();
}
for (InstanceTree child : children.values()) {
child.updateInstances(poseStack);
}
poseStack.popPose();
}
}
public void pos(float x, float y, float z) {
this.x = x;
this.y = y;
this.z = z;
}
public void rotation(float xRot, float yRot, float zRot) {
this.xRot = xRot;
this.yRot = yRot;
this.zRot = zRot;
}
public void scale(float xScale, float yScale, float zScale) {
this.xScale = xScale;
this.yScale = yScale;
this.zScale = zScale;
}
public void offsetPos(float xOffset, float yOffset, float zOffset) {
x += xOffset;
y += yOffset;
z += zOffset;
}
public void offsetRotation(float xOffset, float yOffset, float zOffset) {
xRot += xOffset;
yRot += yOffset;
zRot += zOffset;
}
public void offsetScale(float xOffset, float yOffset, float zOffset) {
xScale += xOffset;
yScale += yOffset;
zScale += zOffset;
}
public void offsetPos(Vector3fc offset) {
offsetPos(offset.x(), offset.y(), offset.z());
}
public void offsetRotation(Vector3fc offset) {
offsetRotation(offset.x(), offset.y(), offset.z());
}
public void offsetScale(Vector3fc offset) {
offsetScale(offset.x(), offset.y(), offset.z());
}
public PartPose storePose() {
return PartPose.offsetAndRotation(x, y, z, xRot, yRot, zRot);
}
public void loadPose(PartPose pose) {
x = pose.x;
y = pose.y;
z = pose.z;
xRot = pose.xRot;
yRot = pose.yRot;
zRot = pose.zRot;
xScale = ModelPart.DEFAULT_SCALE;
yScale = ModelPart.DEFAULT_SCALE;
zScale = ModelPart.DEFAULT_SCALE;
}
public void resetPose() {
loadPose(initialPose);
}
public void copyTransform(InstanceTree tree) {
x = tree.x;
y = tree.y;
z = tree.z;
xRot = tree.xRot;
yRot = tree.yRot;
zRot = tree.zRot;
xScale = tree.xScale;
yScale = tree.yScale;
zScale = tree.zScale;
}
public void copyTransform(ModelPart modelPart) {
x = modelPart.x;
y = modelPart.y;
z = modelPart.z;
xRot = modelPart.xRot;
yRot = modelPart.yRot;
zRot = modelPart.zRot;
xScale = modelPart.xScale;
yScale = modelPart.yScale;
zScale = modelPart.zScale;
}
public void delete() {
if (instance != null) {
instance.delete();
}
children.values()
.forEach(InstanceTree::delete);
}
@ApiStatus.Experimental
@FunctionalInterface
public interface ObjIntIntConsumer<T> {
void accept(T t, int i, int j);
}
}

View file

@ -0,0 +1,136 @@
package dev.engine_room.flywheel.lib.model.part;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
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.Unmodifiable;
import com.mojang.blaze3d.vertex.PoseStack;
import dev.engine_room.flywheel.api.model.Mesh;
import dev.engine_room.flywheel.lib.internal.FlwLibLink;
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.model.geom.PartPose;
import net.minecraft.client.renderer.LightTexture;
import net.minecraft.client.renderer.texture.OverlayTexture;
public final class MeshTree {
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 Map<ModelLayerLocation, MeshTree> CACHE = new ConcurrentHashMap<>();
@Nullable
private final Mesh mesh;
private final PartPose initialPose;
@Unmodifiable
private final Map<String, MeshTree> children;
private MeshTree(@Nullable Mesh mesh, PartPose initialPose, @Unmodifiable Map<String, MeshTree> children) {
this.mesh = mesh;
this.initialPose = initialPose;
this.children = children;
}
public static MeshTree of(ModelLayerLocation layer) {
return CACHE.computeIfAbsent(layer, MeshTree::convert);
}
private static MeshTree convert(ModelLayerLocation layer) {
EntityModelSet entityModels = Minecraft.getInstance()
.getEntityModels();
ModelPart modelPart = entityModels.bakeLayer(layer);
return convert(modelPart, THREAD_LOCAL_OBJECTS.get());
}
private static MeshTree convert(ModelPart modelPart, ThreadLocalObjects objects) {
var modelPartChildren = FlwLibLink.INSTANCE.getModelPartChildren(modelPart);
Map<String, MeshTree> children = new HashMap<>();
modelPartChildren.forEach((name, modelPartChild) -> {
children.put(name, convert(modelPartChild, objects));
});
return new MeshTree(compile(modelPart, objects), modelPart.getInitialPose(), Collections.unmodifiableMap(children));
}
@Nullable
private static Mesh compile(ModelPart modelPart, ThreadLocalObjects objects) {
if (modelPart.isEmpty()) {
return null;
}
VertexWriter vertexWriter = objects.vertexWriter;
FlwLibLink.INSTANCE.compileModelPart(modelPart, IDENTITY_POSE, vertexWriter, LightTexture.FULL_BRIGHT, OverlayTexture.NO_OVERLAY, 1.0F, 1.0F, 1.0F, 1.0F);
MemoryBlock data = vertexWriter.copyDataAndReset();
VertexView vertexView = new PosTexNormalVertexView();
vertexView.load(data);
return new SimpleQuadMesh(vertexView, "source=MeshTree");
}
@Nullable
public Mesh mesh() {
return mesh;
}
public PartPose initialPose() {
return initialPose;
}
@Unmodifiable
public Map<String, MeshTree> children() {
return children;
}
public boolean hasChild(String name) {
return children.containsKey(name);
}
@Nullable
public MeshTree child(String name) {
return children.get(name);
}
public MeshTree childOrThrow(String name) {
MeshTree child = child(name);
if (child == null) {
throw new NoSuchElementException("Can't find part " + name);
}
return child;
}
public void traverse(Consumer<Mesh> consumer) {
if (mesh != null) {
consumer.accept(mesh);
}
for (MeshTree child : children.values()) {
child.traverse(consumer);
}
}
@ApiStatus.Internal
public static void onEndClientResourceReload() {
CACHE.clear();
}
private static class ThreadLocalObjects {
public final VertexWriter vertexWriter = new VertexWriter();
}
}

View file

@ -18,6 +18,7 @@ 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);

View file

@ -0,0 +1,20 @@
package dev.engine_room.flywheel.lib.vertex;
import dev.engine_room.flywheel.api.vertex.MutableVertexList;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
public final class VertexTransformations {
private VertexTransformations() {
}
public static void retexture(MutableVertexList vertexList, int index, TextureAtlasSprite sprite) {
vertexList.u(index, sprite.getU(vertexList.u(index) * 16));
vertexList.v(index, sprite.getV(vertexList.v(index) * 16));
}
public static void retexture(MutableVertexList vertexList, TextureAtlasSprite sprite) {
for (int i = 0; i < vertexList.vertexCount(); i++) {
retexture(vertexList, i, sprite);
}
}
}

View file

@ -1,12 +1,17 @@
package dev.engine_room.flywheel.impl;
import java.util.Map;
import org.slf4j.Logger;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import dev.engine_room.flywheel.impl.extension.PoseStackExtension;
import dev.engine_room.flywheel.impl.mixin.ModelPartAccessor;
import dev.engine_room.flywheel.lib.internal.FlwLibLink;
import dev.engine_room.flywheel.lib.transform.PoseTransformStack;
import net.minecraft.client.model.geom.ModelPart;
public class FlwLibLinkImpl implements FlwLibLink {
@Override
@ -18,4 +23,14 @@ public class FlwLibLinkImpl implements FlwLibLink {
public PoseTransformStack getPoseTransformStackOf(PoseStack stack) {
return ((PoseStackExtension) stack).flywheel$transformStack();
}
@Override
public Map<String, ModelPart> getModelPartChildren(ModelPart part) {
return ((ModelPartAccessor) (Object) part).flywheel$children();
}
@Override
public void compileModelPart(ModelPart part, PoseStack.Pose pose, VertexConsumer consumer, int light, int overlay, float red, float green, float blue, float alpha) {
((ModelPartAccessor) (Object) part).flywheel$compile(pose, consumer, light, overlay, red, green, blue, alpha);
}
}

View file

@ -0,0 +1,21 @@
package dev.engine_room.flywheel.impl.mixin;
import java.util.Map;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
import org.spongepowered.asm.mixin.gen.Invoker;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import net.minecraft.client.model.geom.ModelPart;
@Mixin(ModelPart.class)
public interface ModelPartAccessor {
@Accessor("children")
Map<String, ModelPart> flywheel$children();
@Invoker("compile")
void flywheel$compile(PoseStack.Pose pose, VertexConsumer vertexConsumer, int packedLight, int packedOverlay, float red, float green, float blue, float alpha);
}

View file

@ -5,26 +5,30 @@ import java.util.EnumMap;
import java.util.Map;
import java.util.function.Consumer;
import org.joml.Quaternionf;
import org.jetbrains.annotations.Nullable;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.math.Axis;
import dev.engine_room.flywheel.api.instance.Instance;
import dev.engine_room.flywheel.api.model.Model;
import dev.engine_room.flywheel.api.visualization.VisualizationContext;
import dev.engine_room.flywheel.lib.instance.InstanceTypes;
import dev.engine_room.flywheel.lib.instance.OrientedInstance;
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.util.Pair;
import dev.engine_room.flywheel.lib.model.RetexturedMesh;
import dev.engine_room.flywheel.lib.model.part.InstanceTree;
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;
import it.unimi.dsi.fastutil.longs.LongSet;
import net.minecraft.client.model.geom.ModelLayerLocation;
import net.minecraft.client.model.geom.ModelLayers;
import net.minecraft.client.renderer.LevelRenderer;
import net.minecraft.client.renderer.LightTexture;
import net.minecraft.client.renderer.Sheets;
import net.minecraft.client.resources.model.Material;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.core.SectionPos;
import net.minecraft.world.level.block.AbstractChestBlock;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.ChestBlock;
@ -35,7 +39,7 @@ import net.minecraft.world.level.block.entity.LidBlockEntity;
import net.minecraft.world.level.block.state.properties.ChestType;
public class ChestVisual<T extends BlockEntity & LidBlockEntity> extends AbstractBlockEntityVisual<T> implements SimpleDynamicVisual {
public static final dev.engine_room.flywheel.api.material.Material MATERIAL = SimpleMaterial.builder()
private static final dev.engine_room.flywheel.api.material.Material MATERIAL = SimpleMaterial.builder()
.cutout(CutoutShaders.ONE_TENTH)
.texture(Sheets.CHEST_SHEET)
.mipmap(false)
@ -48,68 +52,55 @@ public class ChestVisual<T extends BlockEntity & LidBlockEntity> extends Abstrac
LAYER_LOCATIONS.put(ChestType.RIGHT, ModelLayers.DOUBLE_CHEST_RIGHT);
}
private static final ModelCache<Pair<ChestType, Material>> BOTTOM_MODELS = new ModelCache<>(key -> {
return new SingleMeshModel(ModelPartConverter.convert(LAYER_LOCATIONS.get(key.first()), key.second().sprite(), "bottom"), MATERIAL);
});
private static final ModelCache<Pair<ChestType, Material>> LID_MODELS = new ModelCache<>(key -> {
return new SingleMeshModel(ModelPartConverter.convert(LAYER_LOCATIONS.get(key.first()), key.second().sprite(), "lid"), MATERIAL);
});
private static final ModelCache<Pair<ChestType, Material>> LOCK_MODELS = new ModelCache<>(key -> {
return new SingleMeshModel(ModelPartConverter.convert(LAYER_LOCATIONS.get(key.first()), key.second().sprite(), "lock"), MATERIAL);
});
@Nullable
private final InstanceTree instances;
@Nullable
private final InstanceTree lid;
@Nullable
private final InstanceTree lock;
private final OrientedInstance bottom;
private final TransformedInstance lid;
private final TransformedInstance lock;
private final ChestType chestType;
private final PoseStack poseStack = new PoseStack();
private final BrightnessCombiner brightnessCombiner = new BrightnessCombiner();
@Nullable
private final DoubleBlockCombiner.NeighborCombineResult<? extends ChestBlockEntity> neighborCombineResult;
@Nullable
private final Float2FloatFunction lidProgress;
private final Quaternionf baseRotation = new Quaternionf();
private float lastProgress = Float.NaN;
public ChestVisual(VisualizationContext ctx, T blockEntity, float partialTick) {
super(ctx, blockEntity, partialTick);
chestType = blockState.hasProperty(ChestBlock.TYPE) ? blockState.getValue(ChestBlock.TYPE) : ChestType.SINGLE;
Material texture = Sheets.chooseMaterial(blockEntity, chestType, isChristmas());
bottom = createBottomInstance(texture).position(getVisualPosition());
lid = createLidInstance(texture);
lock = createLockInstance(texture);
Block block = blockState.getBlock();
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));
});
lid = instances.childOrThrow("lid");
lock = instances.childOrThrow("lock");
poseStack.pushPose();
TransformStack.of(poseStack).translate(getVisualPosition());
float horizontalAngle = blockState.getValue(ChestBlock.FACING).toYRot();
baseRotation.setAngleAxis(Math.toRadians(-horizontalAngle), 0, 1, 0);
poseStack.translate(0.5F, 0.5F, 0.5F);
poseStack.mulPose(Axis.YP.rotationDegrees(-horizontalAngle));
poseStack.translate(-0.5F, -0.5F, -0.5F);
DoubleBlockCombiner.NeighborCombineResult<? extends ChestBlockEntity> wrapper = chestBlock.combine(blockState, level, pos, true);
lidProgress = wrapper.apply(ChestBlock.opennessCombiner(blockEntity));
neighborCombineResult = chestBlock.combine(blockState, level, pos, true);
lidProgress = neighborCombineResult.apply(ChestBlock.opennessCombiner(blockEntity));
lastProgress = lidProgress.get(partialTick);
applyLidTransform(lastProgress);
} else {
baseRotation.identity();
lidProgress = $ -> 0f;
instances = null;
lid = null;
lock = null;
neighborCombineResult = null;
lidProgress = null;
}
bottom.rotation(baseRotation);
bottom.setChanged();
applyLidTransform(lidProgress.get(partialTick));
}
private OrientedInstance createBottomInstance(Material texture) {
return instancerProvider.instancer(InstanceTypes.ORIENTED, BOTTOM_MODELS.get(Pair.of(chestType, texture)))
.createInstance();
}
private TransformedInstance createLidInstance(Material texture) {
return instancerProvider.instancer(InstanceTypes.TRANSFORMED, LID_MODELS.get(Pair.of(chestType, texture)))
.createInstance();
}
private TransformedInstance createLockInstance(Material texture) {
return instancerProvider.instancer(InstanceTypes.TRANSFORMED, LOCK_MODELS.get(Pair.of(chestType, texture)))
.createInstance();
}
private static boolean isChristmas() {
@ -117,8 +108,23 @@ public class ChestVisual<T extends BlockEntity & LidBlockEntity> extends Abstrac
return calendar.get(Calendar.MONTH) + 1 == 12 && calendar.get(Calendar.DATE) >= 24 && calendar.get(Calendar.DATE) <= 26;
}
@Override
public void setSectionCollector(SectionCollector sectionCollector) {
this.lightSections = sectionCollector;
if (neighborCombineResult != null) {
lightSections.sections(neighborCombineResult.apply(new SectionPosCombiner()));
} else {
lightSections.sections(LongSet.of(SectionPos.asLong(pos)));
}
}
@Override
public void beginFrame(Context context) {
if (instances == null) {
return;
}
if (doDistanceLimitThisFrame(context) || !isVisible(context.frustum())) {
return;
}
@ -136,41 +142,80 @@ public class ChestVisual<T extends BlockEntity & LidBlockEntity> extends Abstrac
progress = 1.0F - progress;
progress = 1.0F - progress * progress * progress;
float angleX = -(progress * ((float) Math.PI / 2F));
lid.setIdentityTransform()
.translate(getVisualPosition())
.rotateCentered(baseRotation)
.translate(0, 9f / 16f, 1f / 16f)
.rotateX(angleX)
.translate(0, -9f / 16f, -1f / 16f)
.setChanged();
lock.setIdentityTransform()
.translate(getVisualPosition())
.rotateCentered(baseRotation)
.translate(0, 8f / 16f, 0)
.rotateX(angleX)
.translate(0, -8f / 16f, 0)
.setChanged();
lid.xRot = -(progress * ((float) Math.PI / 2F));
lock.xRot = lid.xRot;
instances.updateInstances(poseStack);
}
@Override
public void updateLight(float partialTick) {
relight(bottom, lid, lock);
if (instances != null) {
int packedLight = neighborCombineResult.apply(brightnessCombiner);
instances.traverse(instance -> {
instance.light(packedLight)
.setChanged();
});
}
}
@Override
public void collectCrumblingInstances(Consumer<Instance> consumer) {
consumer.accept(bottom);
consumer.accept(lid);
consumer.accept(lock);
if (instances != null) {
instances.traverse(consumer);
}
}
@Override
protected void _delete() {
bottom.delete();
lid.delete();
lock.delete();
if (instances != null) {
instances.delete();
}
}
private class SectionPosCombiner implements DoubleBlockCombiner.Combiner<BlockEntity, LongSet> {
@Override
public LongSet acceptDouble(BlockEntity first, BlockEntity second) {
long firstSection = SectionPos.asLong(first.getBlockPos());
long secondSection = SectionPos.asLong(second.getBlockPos());
if (firstSection == secondSection) {
return LongSet.of(firstSection);
} else {
return LongSet.of(firstSection, secondSection);
}
}
@Override
public LongSet acceptSingle(BlockEntity single) {
return LongSet.of(SectionPos.asLong(single.getBlockPos()));
}
@Override
public LongSet acceptNone() {
return LongSet.of(SectionPos.asLong(pos));
}
}
private class BrightnessCombiner implements DoubleBlockCombiner.Combiner<BlockEntity, Integer> {
@Override
public Integer acceptDouble(BlockEntity first, BlockEntity second) {
int firstLight = LevelRenderer.getLightColor(first.getLevel(), first.getBlockPos());
int secondLight = LevelRenderer.getLightColor(second.getLevel(), second.getBlockPos());
int firstBlockLight = LightTexture.block(firstLight);
int secondBlockLight = LightTexture.block(secondLight);
int firstSkyLight = LightTexture.sky(firstLight);
int secondSkyLight = LightTexture.sky(secondLight);
return LightTexture.pack(Math.max(firstBlockLight, secondBlockLight), Math.max(firstSkyLight, secondSkyLight));
}
@Override
public Integer acceptSingle(BlockEntity single) {
return LevelRenderer.getLightColor(single.getLevel(), single.getBlockPos());
}
@Override
public Integer acceptNone() {
return LevelRenderer.getLightColor(level, pos);
}
}
}

View file

@ -12,6 +12,7 @@
"LevelMixin",
"LevelRendererMixin",
"MinecraftMixin",
"ModelPartAccessor",
"PoseStackMixin",
"fix.FixFabulousDepthMixin",
"fix.FixNormalScalingMixin",

View file

@ -12,6 +12,7 @@ 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.baked.PartialModelEventHandler;
import dev.engine_room.flywheel.lib.model.part.MeshTree;
import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientEntityEvents;
@ -71,6 +72,8 @@ public final class FlywheelFabric implements ClientModInitializer {
ModelCache.onEndClientResourceReload());
EndClientResourceReloadCallback.EVENT.register((minecraft, resourceManager, initialReload, error) ->
ModelHolder.onEndClientResourceReload());
EndClientResourceReloadCallback.EVENT.register((minecraft, resourceManager, initialReload, error) ->
MeshTree.onEndClientResourceReload());
ModelLoadingPlugin.register(ctx -> {
ctx.addModels(PartialModelEventHandler.onRegisterAdditional());

View file

@ -13,6 +13,7 @@ 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.baked.PartialModelEventHandler;
import dev.engine_room.flywheel.lib.model.part.MeshTree;
import dev.engine_room.flywheel.lib.util.LevelAttached;
import net.minecraft.client.Minecraft;
import net.minecraft.commands.synchronization.ArgumentTypeInfos;
@ -111,6 +112,7 @@ public final class FlywheelForge {
modEventBus.addListener((EndClientResourceReloadEvent e) -> ModelCache.onEndClientResourceReload());
modEventBus.addListener((EndClientResourceReloadEvent e) -> ModelHolder.onEndClientResourceReload());
modEventBus.addListener((EndClientResourceReloadEvent e) -> MeshTree.onEndClientResourceReload());
modEventBus.addListener(PartialModelEventHandler::onRegisterAdditional);
modEventBus.addListener(PartialModelEventHandler::onBakingCompleted);

View file

@ -19,7 +19,7 @@ forge_version_range = [47.0.0,)
# General build dependency versions
java_version = 17
arch_loom_version=1.7.412
arch_loom_version = 1.7.412
cursegradle_version = 1.4.0
parchment_version = 2023.09.03