From 0e058e284e1c4f9fa50ef8861a5d7931cbf83a40 Mon Sep 17 00:00:00 2001 From: DekinDev Date: Thu, 1 Jan 2026 05:42:00 +0100 Subject: [PATCH] 1.21.8 --- .../smoothdoors/client/anim/SddAnimator.java | 340 ++++++++++-------- .../client/ui/SddConfigScreen.java | 2 +- 2 files changed, 197 insertions(+), 145 deletions(-) diff --git a/src/client/java/com/straice/smoothdoors/client/anim/SddAnimator.java b/src/client/java/com/straice/smoothdoors/client/anim/SddAnimator.java index d60bb6d..c2d49c2 100644 --- a/src/client/java/com/straice/smoothdoors/client/anim/SddAnimator.java +++ b/src/client/java/com/straice/smoothdoors/client/anim/SddAnimator.java @@ -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 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 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> 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; } } diff --git a/src/client/java/com/straice/smoothdoors/client/ui/SddConfigScreen.java b/src/client/java/com/straice/smoothdoors/client/ui/SddConfigScreen.java index 12a5831..820e05f 100644 --- a/src/client/java/com/straice/smoothdoors/client/ui/SddConfigScreen.java +++ b/src/client/java/com/straice/smoothdoors/client/ui/SddConfigScreen.java @@ -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());