1.21.8
This commit is contained in:
@@ -7,121 +7,126 @@ import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.DoorBlock;
|
||||
import net.minecraft.block.FenceGateBlock;
|
||||
import net.minecraft.block.TrapdoorBlock;
|
||||
import net.minecraft.block.enums.BlockHalf;
|
||||
import net.minecraft.block.enums.DoorHinge;
|
||||
import net.minecraft.block.enums.DoubleBlockHalf;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.render.OverlayTexture;
|
||||
import net.minecraft.client.render.VertexConsumerProvider;
|
||||
import net.minecraft.client.render.WorldRenderer;
|
||||
import net.minecraft.client.render.block.BlockRenderManager;
|
||||
import net.minecraft.client.util.math.MatrixStack;
|
||||
import net.minecraft.state.property.Properties;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Direction;
|
||||
import net.minecraft.util.math.RotationAxis;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public final class SddAnimator {
|
||||
|
||||
public enum Kind {
|
||||
DOOR,
|
||||
TRAPDOOR,
|
||||
FENCE_GATE
|
||||
}
|
||||
|
||||
private static final Map<BlockPos, Anim> ANIMS = new ConcurrentHashMap<>();
|
||||
private static long lastNs = -1L;
|
||||
|
||||
private SddAnimator() {}
|
||||
|
||||
private enum Kind { DOOR, TRAPDOOR, FENCE_GATE }
|
||||
private static final class Anim {
|
||||
final BlockPos pos;
|
||||
final Kind kind;
|
||||
final boolean toOpen;
|
||||
float time; // seconds
|
||||
final BlockState baseClosed; // state renderizado (cerrado) al que aplicamos la rotación
|
||||
|
||||
private static final Map<BlockPos, Anim> ANIMS = new HashMap<>();
|
||||
private static long lastNs = -1;
|
||||
|
||||
private static float frameDtSeconds() {
|
||||
long now = System.nanoTime();
|
||||
if (lastNs < 0) {
|
||||
lastNs = now;
|
||||
return 0f;
|
||||
Anim(BlockPos pos, Kind kind, boolean toOpen, BlockState baseClosed) {
|
||||
this.pos = pos;
|
||||
this.kind = kind;
|
||||
this.toOpen = toOpen;
|
||||
this.baseClosed = baseClosed;
|
||||
this.time = 0f;
|
||||
}
|
||||
long d = now - lastNs;
|
||||
lastNs = now;
|
||||
|
||||
float dt = d / 1_000_000_000f;
|
||||
if (dt > 0.1f) dt = 0.1f;
|
||||
if (dt < 0f) dt = 0f;
|
||||
return dt;
|
||||
}
|
||||
|
||||
private static boolean isDoor(BlockState s) { return s.getBlock() instanceof DoorBlock; }
|
||||
private static boolean isTrapdoor(BlockState s) { return s.getBlock() instanceof TrapdoorBlock; }
|
||||
private static boolean isFenceGate(BlockState s) { return s.getBlock() instanceof FenceGateBlock; }
|
||||
|
||||
private static Kind kindOf(BlockState s) {
|
||||
if (isDoor(s)) return Kind.DOOR;
|
||||
if (isTrapdoor(s)) return Kind.TRAPDOOR;
|
||||
if (isFenceGate(s)) return Kind.FENCE_GATE;
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean getOpen(BlockState s) {
|
||||
return s.contains(Properties.OPEN) && s.get(Properties.OPEN);
|
||||
}
|
||||
|
||||
private static BlockState withOpen(BlockState s, boolean open) {
|
||||
return s.contains(Properties.OPEN) ? s.with(Properties.OPEN, open) : s;
|
||||
}
|
||||
|
||||
private static float getSpeed(SddConfig cfg, Kind kind) {
|
||||
return switch (kind) {
|
||||
case DOOR -> cfg.doorSpeed;
|
||||
case TRAPDOOR -> cfg.trapdoorSpeed;
|
||||
case FENCE_GATE -> cfg.fenceGateSpeed;
|
||||
};
|
||||
}
|
||||
|
||||
private static boolean isEnabled(SddConfig cfg, Kind kind) {
|
||||
return switch (kind) {
|
||||
case DOOR -> cfg.animateDoors;
|
||||
case TRAPDOOR -> cfg.animateTrapdoors;
|
||||
case FENCE_GATE -> cfg.animateFenceGates;
|
||||
};
|
||||
}
|
||||
// ========= API (llamada desde mixins) =========
|
||||
|
||||
public static void onBlockUpdate(BlockPos pos, BlockState oldState, BlockState newState) {
|
||||
SddConfig cfg = SddConfigManager.get();
|
||||
Kind kind = kindOf(newState);
|
||||
if (kind == null) return;
|
||||
if (!isEnabled(cfg, kind)) return;
|
||||
Kind kNew = kindOf(newState);
|
||||
if (kNew == null) {
|
||||
ANIMS.remove(pos);
|
||||
return;
|
||||
}
|
||||
if (!newState.contains(Properties.OPEN)) return;
|
||||
|
||||
Block oldB = oldState.getBlock();
|
||||
Block newB = newState.getBlock();
|
||||
if (oldB != newB) return;
|
||||
|
||||
boolean oldOpen = getOpen(oldState);
|
||||
boolean newOpen = getOpen(newState);
|
||||
boolean oldOpen = oldState.contains(Properties.OPEN) && oldState.get(Properties.OPEN);
|
||||
boolean newOpen = newState.get(Properties.OPEN);
|
||||
if (oldOpen == newOpen) return;
|
||||
|
||||
BlockState baseClosed = withOpen(newState, false);
|
||||
ANIMS.put(pos.toImmutable(), new Anim(kind, pos.toImmutable(), baseClosed, newOpen));
|
||||
MinecraftClient client = MinecraftClient.getInstance();
|
||||
if (client.world == null) return;
|
||||
|
||||
// Door: animar lower+upper
|
||||
if (kNew == Kind.DOOR && newState.getBlock() instanceof DoorBlock && newState.contains(Properties.DOUBLE_BLOCK_HALF)) {
|
||||
DoubleBlockHalf half = newState.get(Properties.DOUBLE_BLOCK_HALF);
|
||||
BlockPos basePos = (half == DoubleBlockHalf.UPPER) ? pos.down() : pos;
|
||||
|
||||
BlockState lower = client.world.getBlockState(basePos);
|
||||
BlockState upper = client.world.getBlockState(basePos.up());
|
||||
|
||||
BlockState lowerClosed = (lower.getBlock() instanceof DoorBlock && lower.contains(Properties.OPEN))
|
||||
? lower.with(Properties.OPEN, false)
|
||||
: newState.with(Properties.OPEN, false).with(Properties.DOUBLE_BLOCK_HALF, DoubleBlockHalf.LOWER);
|
||||
|
||||
BlockState upperClosed = (upper.getBlock() instanceof DoorBlock && upper.contains(Properties.OPEN))
|
||||
? upper.with(Properties.OPEN, false)
|
||||
: newState.with(Properties.OPEN, false).with(Properties.DOUBLE_BLOCK_HALF, DoubleBlockHalf.UPPER);
|
||||
|
||||
startAnim(basePos, Kind.DOOR, newOpen, lowerClosed);
|
||||
startAnim(basePos.up(), Kind.DOOR, newOpen, upperClosed);
|
||||
return;
|
||||
}
|
||||
|
||||
// Trapdoor / Fence gate
|
||||
startAnim(pos, kNew, newOpen, newState.with(Properties.OPEN, false));
|
||||
}
|
||||
|
||||
/**
|
||||
* Si devuelve true, el mixin que renderiza chunks debe "ocultar" el bloque real para que
|
||||
* sólo se vea el animado.
|
||||
*/
|
||||
public static boolean shouldHideInChunk(BlockPos pos, BlockState state) {
|
||||
Anim a = ANIMS.get(pos);
|
||||
if (a == null) return false;
|
||||
if (kindOf(state) != a.kind) return false;
|
||||
|
||||
SddConfig cfg = SddConfigManager.get();
|
||||
if (!isEnabled(cfg, a.kind)) return false;
|
||||
|
||||
return kindOf(state) == a.kind;
|
||||
return isEnabled(cfg, a.kind);
|
||||
}
|
||||
|
||||
/**
|
||||
* Llamar cada frame desde WorldRendererMixin, pasando camera.getPos().
|
||||
*/
|
||||
public static void renderAll(Vec3d camPos) {
|
||||
MinecraftClient client = MinecraftClient.getInstance();
|
||||
if (client.world == null) {
|
||||
ANIMS.clear();
|
||||
lastNs = -1;
|
||||
lastNs = -1L;
|
||||
return;
|
||||
}
|
||||
|
||||
float dt = frameDtSeconds();
|
||||
if (dt <= 0f && ANIMS.isEmpty()) return;
|
||||
if (ANIMS.isEmpty()) return;
|
||||
|
||||
SddConfig cfg = SddConfigManager.get();
|
||||
var consumers = client.getBufferBuilders().getEntityVertexConsumers();
|
||||
BlockRenderManager brm = client.getBlockRenderManager();
|
||||
VertexConsumerProvider.Immediate consumers = client.getBufferBuilders().getEntityVertexConsumers();
|
||||
|
||||
Iterator<Map.Entry<BlockPos, Anim>> it = ANIMS.entrySet().iterator();
|
||||
while (it.hasNext()) {
|
||||
@@ -133,6 +138,7 @@ public final class SddAnimator {
|
||||
}
|
||||
|
||||
float speed = getSpeed(cfg, a.kind);
|
||||
|
||||
float baseDuration = switch (a.kind) {
|
||||
case DOOR -> 0.18f;
|
||||
case TRAPDOOR -> 0.16f;
|
||||
@@ -155,13 +161,11 @@ public final class SddAnimator {
|
||||
consumers.draw();
|
||||
}
|
||||
|
||||
private static float smoothStep(float t) {
|
||||
return t * t * (3f - 2f * t);
|
||||
}
|
||||
// ========= Render =========
|
||||
|
||||
private static void renderOne(MinecraftClient client,
|
||||
BlockRenderManager brm,
|
||||
net.minecraft.client.render.VertexConsumerProvider.Immediate consumers,
|
||||
VertexConsumerProvider.Immediate consumers,
|
||||
Vec3d camPos,
|
||||
Anim a,
|
||||
float open01) {
|
||||
@@ -171,10 +175,12 @@ public final class SddAnimator {
|
||||
|
||||
MatrixStack matrices = new MatrixStack();
|
||||
|
||||
double rx = a.pos.getX() - camPos.x;
|
||||
double ry = a.pos.getY() - camPos.y;
|
||||
double rz = a.pos.getZ() - camPos.z;
|
||||
matrices.translate(rx, ry, rz);
|
||||
// CLAVE: coordenadas relativas a cámara
|
||||
matrices.translate(
|
||||
a.pos.getX() - camPos.x,
|
||||
a.pos.getY() - camPos.y,
|
||||
a.pos.getZ() - camPos.z
|
||||
);
|
||||
|
||||
int light = WorldRenderer.getLightmapCoordinates(client.world, a.pos);
|
||||
|
||||
@@ -187,102 +193,148 @@ public final class SddAnimator {
|
||||
brm.renderBlockAsEntity(a.baseClosed, matrices, consumers, light, OverlayTexture.DEFAULT_UV);
|
||||
}
|
||||
|
||||
private static void applyDoorTransform(MatrixStack matrices, BlockState worldState, float open01) {
|
||||
float angle = 90f * open01;
|
||||
// ========= Transforms =========
|
||||
|
||||
var facing = worldState.get(Properties.HORIZONTAL_FACING);
|
||||
var hinge = worldState.get(Properties.DOOR_HINGE);
|
||||
private static void applyDoorTransform(MatrixStack m, BlockState worldState, float open01) {
|
||||
if (!(worldState.getBlock() instanceof DoorBlock)) return;
|
||||
|
||||
float dir = (hinge == net.minecraft.block.enums.DoorHinge.LEFT) ? 1f : -1f;
|
||||
Direction facing = worldState.get(Properties.HORIZONTAL_FACING);
|
||||
DoorHinge hinge = worldState.get(Properties.DOOR_HINGE);
|
||||
|
||||
float pivotX = 0f;
|
||||
float pivotZ = 0f;
|
||||
float pivotX = 0.5f;
|
||||
float pivotZ = 0.5f;
|
||||
|
||||
// FIX: switch completo para Direction (incluye UP/DOWN)
|
||||
switch (facing) {
|
||||
case NORTH -> pivotX = (hinge == net.minecraft.block.enums.DoorHinge.LEFT) ? 0f : 1f;
|
||||
case SOUTH -> pivotX = (hinge == net.minecraft.block.enums.DoorHinge.LEFT) ? 1f : 0f;
|
||||
case EAST -> pivotZ = (hinge == net.minecraft.block.enums.DoorHinge.LEFT) ? 0f : 1f;
|
||||
case WEST -> pivotZ = (hinge == net.minecraft.block.enums.DoorHinge.LEFT) ? 1f : 0f;
|
||||
case UP, DOWN -> {
|
||||
// No debería ocurrir en puertas, pero evita warning del compilador
|
||||
}
|
||||
case NORTH -> pivotX = (hinge == DoorHinge.LEFT) ? 0.0f : 1.0f;
|
||||
case SOUTH -> pivotX = (hinge == DoorHinge.LEFT) ? 1.0f : 0.0f;
|
||||
case EAST -> pivotZ = (hinge == DoorHinge.LEFT) ? 0.0f : 1.0f;
|
||||
case WEST -> pivotZ = (hinge == DoorHinge.LEFT) ? 1.0f : 0.0f;
|
||||
default -> {}
|
||||
}
|
||||
|
||||
matrices.translate(pivotX, 0.0, pivotZ);
|
||||
matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(dir * angle));
|
||||
matrices.translate(-pivotX, 0.0, -pivotZ);
|
||||
float sign;
|
||||
switch (facing) {
|
||||
case NORTH -> sign = (hinge == DoorHinge.RIGHT) ? -1f : 1f;
|
||||
case SOUTH -> sign = (hinge == DoorHinge.RIGHT) ? 1f : -1f;
|
||||
case EAST -> sign = (hinge == DoorHinge.RIGHT) ? 1f : -1f;
|
||||
case WEST -> sign = (hinge == DoorHinge.RIGHT) ? -1f : 1f;
|
||||
default -> sign = 1f;
|
||||
}
|
||||
|
||||
float yawDeg = open01 * 90.0f * sign;
|
||||
|
||||
m.translate(pivotX, 0.0f, pivotZ);
|
||||
m.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(yawDeg));
|
||||
m.translate(-pivotX, 0.0f, -pivotZ);
|
||||
}
|
||||
|
||||
private static void applyTrapdoorTransform(MatrixStack matrices, BlockState worldState, float open01) {
|
||||
float angle = 90f * open01;
|
||||
private static void applyTrapdoorTransform(MatrixStack m, BlockState worldState, float open01) {
|
||||
if (!(worldState.getBlock() instanceof TrapdoorBlock)) return;
|
||||
|
||||
var facing = worldState.get(Properties.HORIZONTAL_FACING);
|
||||
var half = worldState.get(Properties.BLOCK_HALF); // TOP/BOTTOM
|
||||
Direction facing = worldState.get(Properties.HORIZONTAL_FACING);
|
||||
BlockHalf half = worldState.get(Properties.BLOCK_HALF);
|
||||
|
||||
float sign = (half == net.minecraft.block.enums.BlockHalf.BOTTOM) ? -1f : 1f;
|
||||
float pivotY = (half == net.minecraft.block.enums.BlockHalf.BOTTOM) ? 0f : 1f;
|
||||
float sign = (half == BlockHalf.TOP) ? -1f : 1f;
|
||||
float angle = open01 * 90.0f * sign;
|
||||
|
||||
float pivotX = 0.5f;
|
||||
float pivotY = 0.5f;
|
||||
float pivotZ = 0.5f;
|
||||
|
||||
// Ya estaba completo, lo dejo igual
|
||||
switch (facing) {
|
||||
case NORTH -> {
|
||||
float pivotZ = 0f;
|
||||
matrices.translate(0, pivotY, pivotZ);
|
||||
matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(sign * angle));
|
||||
matrices.translate(0, -pivotY, -pivotZ);
|
||||
pivotZ = 0.0f;
|
||||
m.translate(pivotX, pivotY, pivotZ);
|
||||
m.multiply(RotationAxis.POSITIVE_X.rotationDegrees(angle));
|
||||
m.translate(-pivotX, -pivotY, -pivotZ);
|
||||
}
|
||||
case SOUTH -> {
|
||||
float pivotZ = 1f;
|
||||
matrices.translate(0, pivotY, pivotZ);
|
||||
matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(-sign * angle));
|
||||
matrices.translate(0, -pivotY, -pivotZ);
|
||||
pivotZ = 1.0f;
|
||||
m.translate(pivotX, pivotY, pivotZ);
|
||||
m.multiply(RotationAxis.NEGATIVE_X.rotationDegrees(angle));
|
||||
m.translate(-pivotX, -pivotY, -pivotZ);
|
||||
}
|
||||
case WEST -> {
|
||||
float pivotX = 0f;
|
||||
matrices.translate(pivotX, pivotY, 0);
|
||||
matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(-sign * angle));
|
||||
matrices.translate(-pivotX, -pivotY, 0);
|
||||
pivotX = 0.0f;
|
||||
m.translate(pivotX, pivotY, pivotZ);
|
||||
m.multiply(RotationAxis.NEGATIVE_Z.rotationDegrees(angle));
|
||||
m.translate(-pivotX, -pivotY, -pivotZ);
|
||||
}
|
||||
case EAST -> {
|
||||
float pivotX = 1f;
|
||||
matrices.translate(pivotX, pivotY, 0);
|
||||
matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(sign * angle));
|
||||
matrices.translate(-pivotX, -pivotY, 0);
|
||||
}
|
||||
case UP, DOWN -> {
|
||||
// No aplicable
|
||||
pivotX = 1.0f;
|
||||
m.translate(pivotX, pivotY, pivotZ);
|
||||
m.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(angle));
|
||||
m.translate(-pivotX, -pivotY, -pivotZ);
|
||||
}
|
||||
default -> {}
|
||||
}
|
||||
}
|
||||
|
||||
private static void applyFenceGateTransform(MatrixStack matrices, BlockState worldState, float open01) {
|
||||
float angle = 90f * open01;
|
||||
var facing = worldState.get(Properties.HORIZONTAL_FACING);
|
||||
private static void applyFenceGateTransform(MatrixStack m, BlockState worldState, float open01) {
|
||||
if (!(worldState.getBlock() instanceof FenceGateBlock)) return;
|
||||
|
||||
// FIX: switch completo para Direction (incluye UP/DOWN)
|
||||
float dir = switch (facing) {
|
||||
case NORTH, EAST -> 1f;
|
||||
case SOUTH, WEST -> -1f;
|
||||
case UP, DOWN -> 1f; // no debería ocurrir
|
||||
Direction facing = worldState.get(Properties.HORIZONTAL_FACING);
|
||||
|
||||
float sign;
|
||||
switch (facing) {
|
||||
case NORTH, WEST -> sign = 1f;
|
||||
case SOUTH, EAST -> sign = -1f;
|
||||
default -> sign = 1f;
|
||||
}
|
||||
|
||||
float yawDeg = open01 * 90.0f * sign;
|
||||
|
||||
m.translate(0.5f, 0.0f, 0.5f);
|
||||
m.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(yawDeg));
|
||||
m.translate(-0.5f, 0.0f, -0.5f);
|
||||
}
|
||||
|
||||
// ========= Helpers =========
|
||||
|
||||
private static Kind kindOf(BlockState s) {
|
||||
Block b = s.getBlock();
|
||||
if (b instanceof DoorBlock) return Kind.DOOR;
|
||||
if (b instanceof TrapdoorBlock) return Kind.TRAPDOOR;
|
||||
if (b instanceof FenceGateBlock) return Kind.FENCE_GATE;
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void startAnim(BlockPos pos, Kind kind, boolean toOpen, BlockState baseClosed) {
|
||||
ANIMS.put(pos.toImmutable(), new Anim(pos.toImmutable(), kind, toOpen, baseClosed));
|
||||
}
|
||||
|
||||
private static boolean isEnabled(SddConfig cfg, Kind kind) {
|
||||
return switch (kind) {
|
||||
case DOOR -> cfg.animateDoors;
|
||||
case TRAPDOOR -> cfg.animateTrapdoors;
|
||||
case FENCE_GATE -> cfg.animateFenceGates;
|
||||
};
|
||||
|
||||
matrices.translate(0.5, 0.0, 0.5);
|
||||
matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(dir * angle));
|
||||
matrices.translate(-0.5, 0.0, -0.5);
|
||||
}
|
||||
|
||||
private static final class Anim {
|
||||
final Kind kind;
|
||||
final BlockPos pos;
|
||||
final BlockState baseClosed;
|
||||
final boolean toOpen;
|
||||
float time = 0f;
|
||||
private static float getSpeed(SddConfig cfg, Kind kind) {
|
||||
return switch (kind) {
|
||||
case DOOR -> cfg.doorSpeed;
|
||||
case TRAPDOOR -> cfg.trapdoorSpeed;
|
||||
case FENCE_GATE -> cfg.fenceGateSpeed;
|
||||
};
|
||||
}
|
||||
|
||||
Anim(Kind kind, BlockPos pos, BlockState baseClosed, boolean toOpen) {
|
||||
this.kind = kind;
|
||||
this.pos = pos;
|
||||
this.baseClosed = baseClosed;
|
||||
this.toOpen = toOpen;
|
||||
private static float smoothStep(float t) {
|
||||
return t * t * (3f - 2f * t);
|
||||
}
|
||||
|
||||
private static float frameDtSeconds() {
|
||||
long now = System.nanoTime();
|
||||
if (lastNs < 0L) {
|
||||
lastNs = now;
|
||||
return 0f;
|
||||
}
|
||||
long d = now - lastNs;
|
||||
lastNs = now;
|
||||
|
||||
double sec = d / 1_000_000_000.0;
|
||||
if (sec < 0) sec = 0;
|
||||
if (sec > 0.1) sec = 0.1;
|
||||
return (float) sec;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,7 +104,7 @@ public class SddConfigScreen extends Screen {
|
||||
}));
|
||||
y += 40;
|
||||
|
||||
addDrawableChild(ButtonWidget.builder(Text.literal("Save and close"), btn -> {
|
||||
addDrawableChild(ButtonWidget.builder(Text.literal("Done"), btn -> {
|
||||
SddConfigManager.save();
|
||||
if (this.client != null) this.client.setScreen(parent);
|
||||
}).dimensions(centerX - 120, y, 240, h).build());
|
||||
|
||||
Reference in New Issue
Block a user