Add, refactor, remove

- Add DiffuseLightCalculator
- Add ModelUtil.VANILLA_RENDERER for consistent virtual rendering
- Refactor OptifineHandler
- Remove MatrixTransformStack
This commit is contained in:
PepperCode1 2022-02-08 19:26:36 -08:00
parent ae5853442b
commit f24c1fafba
12 changed files with 186 additions and 204 deletions

View file

@ -24,10 +24,10 @@ apply plugin: 'org.spongepowered.mixin'
boolean dev = System.getenv('RELEASE') == null || System.getenv('RELEASE').equalsIgnoreCase('false');
ext.buildnumber = 0
project.buildnumber = System.getenv('BUILD_NUMBER') != null ? System.getenv('BUILD_NUMBER') : 'custom'
ext.buildNumber = System.getenv('BUILD_NUMBER')
if (buildNumber == null) buildNumber = 'custom'
version = "${mc_update_version}-${mod_version}" + (dev ? ".${buildnumber}" : '')
version = "${mc_update_version}-${mod_version}" + (dev ? ".${buildNumber}" : '')
group = 'com.jozufozu.flywheel'
archivesBaseName = 'flywheel-forge'
@ -107,7 +107,7 @@ repositories {
dependencies {
minecraft "net.minecraftforge:forge:${minecraft_version}-${forge_version}"
compileOnly fg.deobf("curse.maven:starlight-526854:3559934")
compileOnly fg.deobf("curse.maven:starlight-526854:3599856")
// https://discord.com/channels/313125603924639766/725850371834118214/910619168821354497
// Prevent Mixin annotation processor from getting into IntelliJ's annotation processor settings

View file

@ -32,7 +32,7 @@ public class Backend {
* (Meshlet, MDI, GL31 Draw Instanced are planned), this will name which one is in use.
*/
public static String getBackendDescriptor() {
return engine == null ? "" : engine.getProperName();
return engine == null ? "Uninitialized" : engine.getProperName();
}
@Nullable
@ -79,7 +79,7 @@ public class Backend {
FlwEngine preferredChoice = FlwConfig.get()
.getEngine();
boolean usingShaders = OptifineHandler.usingShaders();
boolean usingShaders = OptifineHandler.isUsingShaders();
boolean canUseEngine = switch (preferredChoice) {
case OFF -> true;
case BATCHING -> !usingShaders;

View file

@ -5,21 +5,26 @@ import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.Optional;
import java.util.function.BooleanSupplier;
import com.jozufozu.flywheel.util.Lazy;
import org.apache.commons.lang3.mutable.MutableInt;
import net.minecraft.client.Minecraft;
public class OptifineHandler {
public final class OptifineHandler {
public static final String OPTIFINE_ROOT_PACKAGE = "net.optifine";
public static final String SHADER_PACKAGE = "net.optifine.shaders";
private static Package optifine;
private static OptifineHandler handler;
private static final ThreadLocal<MutableInt> FORCE_DIFFUSE = ThreadLocal.withInitial(MutableInt::new);
private static final Lazy<BooleanSupplier> isShadowPass = Lazy.of(() -> {
private static boolean isOptifineInstalled;
private static boolean isUsingShaders;
private static BooleanSupplier shadowPassSupplier;
private OptifineHandler() {
}
private static BooleanSupplier createShadowPassSupplier() {
try {
Class<?> ofShaders = Class.forName("net.optifine.shaders.Shaders");
Field field = ofShaders.getDeclaredField("isShadowPass");
@ -34,55 +39,6 @@ public class OptifineHandler {
} catch (Exception ignored) {
return () -> false;
}
});
public final boolean usingShaders;
public OptifineHandler(boolean usingShaders) {
this.usingShaders = usingShaders;
}
/**
* Get information about the current Optifine configuration.
*
* @return {@link Optional#empty()} if Optifine is not installed.
*/
public static Optional<OptifineHandler> get() {
return Optional.ofNullable(handler);
}
public static boolean optifineInstalled() {
return optifine != null;
}
public static boolean usingShaders() {
return OptifineHandler.get()
.map(OptifineHandler::isUsingShaders)
.orElse(false);
}
public static boolean isShadowPass() {
return isShadowPass.get().getAsBoolean();
}
public static void init() {
optifine = Package.getPackage(OPTIFINE_ROOT_PACKAGE);
if (optifine == null) {
Backend.LOGGER.info("Optifine not detected.");
} else {
Backend.LOGGER.info("Optifine detected.");
refresh();
}
}
public static void refresh() {
if (optifine == null) return;
boolean shadersOff = areShadersDisabledInOptifineConfigFile();
handler = new OptifineHandler(!shadersOff);
}
private static boolean areShadersDisabledInOptifineConfigFile() {
@ -109,7 +65,57 @@ public class OptifineHandler {
return shadersOff;
}
public boolean isUsingShaders() {
return usingShaders;
public static void init() {
Package optifinePackage = Package.getPackage(OPTIFINE_ROOT_PACKAGE);
isOptifineInstalled = optifinePackage != null;
if (isOptifineInstalled) {
Backend.LOGGER.info("Optifine detected.");
refresh();
} else {
Backend.LOGGER.info("Optifine not detected.");
}
shadowPassSupplier = createShadowPassSupplier();
}
public static void refresh() {
if (!isOptifineInstalled) return;
boolean shadersOff = areShadersDisabledInOptifineConfigFile();
isUsingShaders = !shadersOff;
}
public static boolean isOptifineInstalled() {
return isOptifineInstalled;
}
public static boolean isUsingShaders() {
return isUsingShaders;
}
public static boolean isShadowPass() {
return shadowPassSupplier.getAsBoolean();
}
public static void pushForceDiffuse() {
if (isOptifineInstalled) {
FORCE_DIFFUSE.get().increment();
}
}
public static void popForceDiffuse() {
if (isOptifineInstalled) {
FORCE_DIFFUSE.get().decrement();
}
}
public static boolean shouldApplyDiffuse() {
if (isOptifineInstalled) {
return !isUsingShaders || (FORCE_DIFFUSE.get().intValue() > 0);
}
return true;
}
}

View file

@ -56,7 +56,7 @@ public class BatchedMaterialGroup implements MaterialGroup {
for (BatchedMaterial<?> material : materials.values()) {
for (CPUInstancer<?> instancer : material.models.values()) {
instancer.sbb.context.outputColorDiffuse = !consumer.hasOverlay() && !OptifineHandler.usingShaders();
instancer.sbb.context.outputColorDiffuse = !consumer.hasOverlay() && OptifineHandler.shouldApplyDiffuse();
instancer.submitTasks(stack, pool, consumer);
}
}

View file

@ -1,7 +1,7 @@
package com.jozufozu.flywheel.core.model;
import com.jozufozu.flywheel.api.vertex.VertexList;
import com.jozufozu.flywheel.util.RenderMath;
import com.jozufozu.flywheel.util.DiffuseLightCalculator;
import com.jozufozu.flywheel.util.transform.Transform;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
@ -14,7 +14,6 @@ import com.mojang.math.Vector4f;
import net.minecraft.client.renderer.LightTexture;
import net.minecraft.client.renderer.texture.OverlayTexture;
import net.minecraft.util.Mth;
import net.minecraftforge.client.model.pipeline.LightUtil;
public class ModelTransformer {
@ -48,6 +47,11 @@ public class ModelTransformer {
normalMat = params.normal.copy();
}
DiffuseLightCalculator diffuseCalculator = params.diffuseCalculator;
if (diffuseCalculator == null) {
diffuseCalculator = DiffuseLightCalculator.forCurrentLevel();
}
int vertexCount = reader.getVertexCount();
for (int i = 0; i < vertexCount; i++) {
float x = reader.getX(i);
@ -68,23 +72,26 @@ public class ModelTransformer {
float ny = normal.y();
float nz = normal.z();
byte r, g, b, a;
if (params.useParamColor) {
if (context.outputColorDiffuse) {
float instanceDiffuse = LightUtil.diffuseLight(nx, ny, nz);
int colorR = transformColor(params.r, instanceDiffuse);
int colorG = transformColor(params.g, instanceDiffuse);
int colorB = transformColor(params.b, instanceDiffuse);
builder.color(colorR, colorG, colorB, params.a);
} else {
builder.color(params.r, params.g, params.b, params.a);
}
r = (byte) params.r;
g = (byte) params.g;
b = (byte) params.b;
a = (byte) params.a;
} else {
if (context.outputColorDiffuse) {
int d = RenderMath.unb(LightUtil.diffuseLight(nx, ny, nz));
builder.color(d, d, d, 0xFF);
} else {
builder.color(reader.getR(i), reader.getG(i), reader.getB(i), reader.getA(i));
}
r = reader.getR(i);
g = reader.getG(i);
b = reader.getB(i);
a = reader.getA(i);
}
if (context.outputColorDiffuse) {
float instanceDiffuse = diffuseCalculator.getDiffuse(nx, ny, nz);
int colorR = transformColor(r, instanceDiffuse);
int colorG = transformColor(g, instanceDiffuse);
int colorB = transformColor(b, instanceDiffuse);
builder.color(colorR, colorG, colorB, a);
} else {
builder.color(r, g, b, a);
}
//builder.color(Math.max(0, (int) (nx * 255)), Math.max(0, (int) (ny * 255)), Math.max(0, (int) (nz * 255)), 0xFF);
@ -118,6 +125,10 @@ public class ModelTransformer {
return "ModelTransformer[" + model + ']';
}
public static int transformColor(byte component, float scale) {
return Mth.clamp((int) (Byte.toUnsignedInt(component) * scale), 0, 255);
}
public static int transformColor(int component, float scale) {
return Mth.clamp((int) (component * scale), 0, 255);
}
@ -137,7 +148,6 @@ public class ModelTransformer {
* Do we need to bake diffuse lighting into the output colors?
*/
public boolean outputColorDiffuse = true;
}
public static class Params implements Transform<Params> {
@ -163,6 +173,9 @@ public class ModelTransformer {
public boolean useParamLight;
public int packedLightCoords;
// Diffuse
public DiffuseLightCalculator diffuseCalculator;
public Params() {
model = new Matrix4f();
normal = new Matrix3f();
@ -180,6 +193,7 @@ public class ModelTransformer {
overlay = OverlayTexture.NO_OVERLAY;
useParamLight = false;
packedLightCoords = LightTexture.FULL_BRIGHT;
diffuseCalculator = null;
}
public void load(Params from) {
@ -194,6 +208,7 @@ public class ModelTransformer {
overlay = from.overlay;
useParamLight = from.useParamLight;
packedLightCoords = from.packedLightCoords;
diffuseCalculator = from.diffuseCalculator;
}
public Params color(int r, int g, int b, int a) {

View file

@ -1,13 +1,13 @@
package com.jozufozu.flywheel.core.model;
import java.util.Arrays;
import java.lang.reflect.Field;
import java.util.Collection;
import java.util.Random;
import java.util.function.Supplier;
import com.jozufozu.flywheel.Flywheel;
import com.jozufozu.flywheel.core.virtual.VirtualEmptyBlockGetter;
import com.jozufozu.flywheel.core.virtual.VirtualEmptyModelData;
import com.jozufozu.flywheel.util.Lazy;
import com.jozufozu.flywheel.util.transform.TransformStack;
import com.mojang.blaze3d.vertex.BufferBuilder;
import com.mojang.blaze3d.vertex.DefaultVertexFormat;
@ -17,7 +17,7 @@ import com.mojang.blaze3d.vertex.VertexFormat;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.ItemBlockRenderTypes;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.block.BlockModelShaper;
import net.minecraft.client.renderer.block.BlockRenderDispatcher;
import net.minecraft.client.renderer.block.ModelBlockRenderer;
import net.minecraft.client.renderer.texture.OverlayTexture;
import net.minecraft.client.resources.model.BakedModel;
@ -29,21 +29,33 @@ import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate;
import net.minecraftforge.client.ForgeHooksClient;
import net.minecraftforge.client.model.data.EmptyModelData;
import net.minecraftforge.fml.util.ObfuscationReflectionHelper;
public class ModelUtil {
private static final Lazy<ModelBlockRenderer> MODEL_RENDERER = Lazy.of(() -> new ModelBlockRenderer(Minecraft.getInstance().getBlockColors()));
/**
* An alternative BlockRenderDispatcher that circumvents the Forge rendering pipeline to ensure consistency.
* Meant to be used for virtual rendering.
*/
public static final BlockRenderDispatcher VANILLA_RENDERER = createVanillaRenderer();
// DOWN, UP, NORTH, SOUTH, WEST, EAST, null
private static final Direction[] CULL_FACES;
static {
Direction[] directions = Direction.values();
CULL_FACES = Arrays.copyOf(directions, directions.length + 1);
private static BlockRenderDispatcher createVanillaRenderer() {
BlockRenderDispatcher defaultDispatcher = Minecraft.getInstance().getBlockRenderer();
BlockRenderDispatcher dispatcher = new BlockRenderDispatcher(null, null, null);
try {
for (Field field : BlockRenderDispatcher.class.getDeclaredFields()) {
field.setAccessible(true);
field.set(dispatcher, field.get(defaultDispatcher));
}
ObfuscationReflectionHelper.setPrivateValue(BlockRenderDispatcher.class, dispatcher, new ModelBlockRenderer(Minecraft.getInstance().getBlockColors()), "f_110900_");
} catch (Exception e) {
Flywheel.LOGGER.error("Failed to initialize vanilla BlockRenderDispatcher!", e);
return defaultDispatcher;
}
return dispatcher;
}
public static BufferBuilder getBufferBuilder(BakedModel model, BlockState referenceState, PoseStack ms) {
ModelBlockRenderer blockRenderer = Minecraft.getInstance().getBlockRenderer().getModelRenderer();
ModelBlockRenderer blockRenderer = VANILLA_RENDERER.getModelRenderer();
BufferBuilder builder = new BufferBuilder(512);
builder.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.BLOCK);
blockRenderer.tesselateBlock(VirtualEmptyBlockGetter.INSTANCE, model, referenceState, BlockPos.ZERO, ms, builder,
@ -53,8 +65,7 @@ public class ModelUtil {
}
public static BufferBuilder getBufferBuilderFromTemplate(BlockAndTintGetter renderWorld, RenderType layer, Collection<StructureTemplate.StructureBlockInfo> blocks) {
ModelBlockRenderer modelRenderer = MODEL_RENDERER.get();
BlockModelShaper blockModels = Minecraft.getInstance().getModelManager().getBlockModelShaper();
ModelBlockRenderer modelRenderer = VANILLA_RENDERER.getModelRenderer();
PoseStack ms = new PoseStack();
Random random = new Random();
@ -75,7 +86,7 @@ public class ModelUtil {
ms.pushPose();
ms.translate(pos.getX(), pos.getY(), pos.getZ());
modelRenderer.tesselateBlock(renderWorld, blockModels.getBlockModel(state), state, pos, ms, builder,
modelRenderer.tesselateBlock(renderWorld, VANILLA_RENDERER.getBlockModel(state), state, pos, ms, builder,
true, random, 42, OverlayTexture.NO_OVERLAY, EmptyModelData.INSTANCE);
ms.popPose();
}

View file

@ -23,7 +23,7 @@ public class ShaderCloseMixin {
@Inject(at = @At("HEAD"), method = "setScreen")
private void whenScreenChanges(Screen screen, CallbackInfo info) {
if (OptifineHandler.optifineInstalled() && screen instanceof VideoSettingsScreen) {
if (OptifineHandler.isOptifineInstalled() && screen instanceof VideoSettingsScreen) {
Screen old = this.screen;
if (old != null && old.getClass()
.getName()

View file

@ -0,0 +1,20 @@
package com.jozufozu.flywheel.util;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraftforge.client.model.pipeline.LightUtil;
public interface DiffuseLightCalculator {
DiffuseLightCalculator DEFAULT = LightUtil::diffuseLight;
DiffuseLightCalculator NETHER = RenderMath::diffuseLightNether;
static DiffuseLightCalculator forCurrentLevel() {
return forLevel(Minecraft.getInstance().level);
}
static DiffuseLightCalculator forLevel(ClientLevel level) {
return level.effects().constantAmbientLight() ? NETHER : DEFAULT;
}
float getDiffuse(float normalX, float normalY, float normalZ);
}

View file

@ -67,4 +67,8 @@ public class RenderMath {
target = target % 360;
return (float) (((((target - current) % 360) + 540) % 360) - 180);
}
public static float diffuseLightNether(float x, float y, float z) {
return Math.min(x * x * 0.6f + y * y * 0.9f + z * z * 0.8f, 1f);
}
}

View file

@ -1,81 +0,0 @@
package com.jozufozu.flywheel.util.transform;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.math.Matrix3f;
import com.mojang.math.Matrix4f;
import com.mojang.math.Quaternion;
public class MatrixTransformStack implements TransformStack {
private final PoseStack internal;
public MatrixTransformStack() {
this(new PoseStack());
}
public MatrixTransformStack(PoseStack internal) {
this.internal = internal;
}
public PoseStack unwrap() {
return internal;
}
public MatrixTransformStack setIdentity() {
if (internal.clear()) {
PoseStack.Pose last = internal.last();
last.normal()
.setIdentity();
last.pose()
.setIdentity();
} else {
internal.popPose();
internal.pushPose();
}
return this;
}
@Override
public TransformStack translate(double x, double y, double z) {
internal.translate(x, y, z);
return this;
}
@Override
public TransformStack multiply(Quaternion quaternion) {
internal.mulPose(quaternion);
return this;
}
@Override
public TransformStack scale(float factorX, float factorY, float factorZ) {
internal.scale(factorX, factorY, factorZ);
return this;
}
@Override
public TransformStack pushPose() {
internal.pushPose();
return this;
}
@Override
public TransformStack popPose() {
internal.popPose();
return this;
}
@Override
public TransformStack mulPose(Matrix4f pose) {
internal.last().pose().multiply(pose);
return this;
}
@Override
public TransformStack mulNormal(Matrix3f normal) {
internal.last().normal().mul(normal);
return this;
}
}

View file

@ -9,7 +9,8 @@ import com.jozufozu.flywheel.core.hardcoded.ModelPart;
import com.jozufozu.flywheel.core.materials.model.ModelData;
import com.jozufozu.flywheel.core.model.Model;
import com.jozufozu.flywheel.util.AnimationTickHolder;
import com.jozufozu.flywheel.util.transform.MatrixTransformStack;
import com.jozufozu.flywheel.util.transform.TransformStack;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.math.Vector3f;
import net.minecraft.client.renderer.RenderType;
@ -25,7 +26,7 @@ public class MinecartInstance<T extends AbstractMinecart> extends EntityInstance
private static final ResourceLocation MINECART_LOCATION = new ResourceLocation("textures/entity/minecart.png");
MatrixTransformStack stack = new MatrixTransformStack();
private final PoseStack stack = new PoseStack();
private final ModelData body;
private ModelData contents;
@ -53,11 +54,12 @@ public class MinecartInstance<T extends AbstractMinecart> extends EntityInstance
@Override
public void beginFrame() {
TransformStack tstack = TransformStack.cast(stack);
stack.setIdentity();
float pt = AnimationTickHolder.getPartialTicks();
Vec3i originCoordinate = materialManager.getOriginCoordinate();
stack.translate(
tstack.translate(
Mth.lerp(pt, entity.xOld, entity.getX()) - originCoordinate.getX(),
Mth.lerp(pt, entity.yOld, entity.getY()) - originCoordinate.getY(),
Mth.lerp(pt, entity.zOld, entity.getZ()) - originCoordinate.getZ());
@ -69,8 +71,8 @@ public class MinecartInstance<T extends AbstractMinecart> extends EntityInstance
float f = (((float)(i >> 16 & 7L) + 0.5F) / 8 - 0.5F) * 0.004F;
float f1 = (((float)(i >> 20 & 7L) + 0.5F) / 8 - 0.5F) * 0.004F;
float f2 = (((float)(i >> 24 & 7L) + 0.5F) / 8 - 0.5F) * 0.004F;
stack.translate(f, f1, f2);
stack.nudge(entity.getId());
tstack.translate(f, f1, f2);
tstack.nudge(entity.getId());
double d0 = Mth.lerp(pt, entity.xOld, entity.getX());
double d1 = Mth.lerp(pt, entity.yOld, entity.getY());
double d2 = Mth.lerp(pt, entity.zOld, entity.getZ());
@ -87,7 +89,7 @@ public class MinecartInstance<T extends AbstractMinecart> extends EntityInstance
vector3d2 = vector3d;
}
stack.translate(vector3d.x - d0, (vector3d1.y + vector3d2.y) / 2.0D - d1, vector3d.z - d2);
tstack.translate(vector3d.x - d0, (vector3d1.y + vector3d2.y) / 2.0D - d1, vector3d.z - d2);
Vec3 vector3d3 = vector3d2.add(-vector3d1.x, -vector3d1.y, -vector3d1.z);
if (vector3d3.length() != 0.0D) {
vector3d3 = vector3d3.normalize();
@ -96,9 +98,9 @@ public class MinecartInstance<T extends AbstractMinecart> extends EntityInstance
}
}
stack.translate(0.0D, 0.375D, 0.0D);
stack.multiply(Vector3f.YP.rotationDegrees(180 - yaw));
stack.multiply(Vector3f.ZP.rotationDegrees(-f3));
tstack.translate(0.0D, 0.375D, 0.0D);
tstack.multiply(Vector3f.YP.rotationDegrees(180 - yaw));
tstack.multiply(Vector3f.ZP.rotationDegrees(-f3));
float f5 = (float)entity.getHurtTime() - pt;
float f6 = entity.getDamage() - pt;
if (f6 < 0) {
@ -106,20 +108,20 @@ public class MinecartInstance<T extends AbstractMinecart> extends EntityInstance
}
if (f5 > 0) {
stack.multiply(Vector3f.XP.rotationDegrees(Mth.sin(f5) * f5 * f6 / 10 * (float)entity.getHurtDir()));
tstack.multiply(Vector3f.XP.rotationDegrees(Mth.sin(f5) * f5 * f6 / 10 * (float)entity.getHurtDir()));
}
int j = entity.getDisplayOffset();
if (contents != null) {
stack.pushPose();
stack.scale(0.75F);
stack.translate(-0.5D, (float)(j - 8) / 16, 0.5D);
stack.multiply(Vector3f.YP.rotationDegrees(90));
contents.setTransform(stack.unwrap());
stack.popPose();
tstack.pushPose();
tstack.scale(0.75F);
tstack.translate(-0.5D, (float)(j - 8) / 16, 0.5D);
tstack.multiply(Vector3f.YP.rotationDegrees(90));
contents.setTransform(stack);
tstack.popPose();
}
body.setTransform(stack.unwrap());
body.setTransform(stack);
}
@Override

View file

@ -7,7 +7,8 @@ import com.jozufozu.flywheel.core.Materials;
import com.jozufozu.flywheel.core.hardcoded.ModelPart;
import com.jozufozu.flywheel.core.materials.model.ModelData;
import com.jozufozu.flywheel.util.AnimationTickHolder;
import com.jozufozu.flywheel.util.transform.MatrixTransformStack;
import com.jozufozu.flywheel.util.transform.TransformStack;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.math.Quaternion;
import com.mojang.math.Vector3f;
@ -25,7 +26,7 @@ public class ShulkerBoxInstance extends BlockEntityInstance<ShulkerBoxBlockEntit
private final ModelData base;
private final ModelData lid;
private final MatrixTransformStack stack = new MatrixTransformStack();
private final PoseStack stack = new PoseStack();
private float lastProgress = Float.NaN;
@ -40,18 +41,20 @@ public class ShulkerBoxInstance extends BlockEntityInstance<ShulkerBoxBlockEntit
}
Quaternion rotation = getDirection().getRotation();
stack.translate(getInstancePosition())
TransformStack tstack = TransformStack.cast(stack);
tstack.translate(getInstancePosition())
.scale(0.9995f)
.translateAll(0.00025)
.centre()
.multiply(rotation)
.unCentre();
base = makeBaseInstance().setTransform(stack.unwrap());
base = makeBaseInstance().setTransform(stack);
stack.translateY(0.25);
tstack.translateY(0.25);
lid = makeLidInstance().setTransform(stack.unwrap());
lid = makeLidInstance().setTransform(stack);
}
@Override
@ -63,13 +66,15 @@ public class ShulkerBoxInstance extends BlockEntityInstance<ShulkerBoxBlockEntit
Quaternion spin = Vector3f.YP.rotationDegrees(270.0F * progress);
stack.pushPose()
TransformStack tstack = TransformStack.cast(stack);
tstack.pushPose()
.centre()
.multiply(spin)
.unCentre()
.translateY(progress * 0.5f);
lid.setTransform(stack.unwrap());
lid.setTransform(stack);
stack.popPose();
}