From 53806b305caf0acf2de3251e630702938dd4a591 Mon Sep 17 00:00:00 2001 From: rtm516 Date: Thu, 4 Apr 2024 10:30:18 +0100 Subject: [PATCH] Add mostly working block collision rotation --- .../hydraulic/block/BlockPackModule.java | 131 +++++++++++++++++- .../org/geysermc/hydraulic/util/MathUtil.java | 21 +++ 2 files changed, 148 insertions(+), 4 deletions(-) create mode 100644 shared/src/main/java/org/geysermc/hydraulic/util/MathUtil.java diff --git a/shared/src/main/java/org/geysermc/hydraulic/block/BlockPackModule.java b/shared/src/main/java/org/geysermc/hydraulic/block/BlockPackModule.java index 2141148..9672bc8 100644 --- a/shared/src/main/java/org/geysermc/hydraulic/block/BlockPackModule.java +++ b/shared/src/main/java/org/geysermc/hydraulic/block/BlockPackModule.java @@ -44,6 +44,7 @@ import org.geysermc.hydraulic.pack.context.PackPreProcessContext; import org.geysermc.hydraulic.storage.ModStorage; import org.geysermc.hydraulic.util.Constants; +import org.geysermc.hydraulic.util.MathUtil; import org.geysermc.hydraulic.util.PackUtil; import org.geysermc.hydraulic.util.SingletonBlockGetter; import org.geysermc.pack.bedrock.resource.BedrockResourcePack; @@ -181,10 +182,13 @@ private void onDefineCustomBlocks(PackEventContext getFaceMapping(Key parent) { return mapping; } + /** + * Create a box component with the given shape. + * + * @param shape The shape to create the box from + * @return The box component + */ private static BoxComponent createBoxComponent(VoxelShape shape) { if (shape.isEmpty()) { return BoxComponent.emptyBox(); @@ -560,4 +581,106 @@ private static BoxComponent createBoxComponent(VoxelShape shape) { 16 * (maxZ - minZ) ); } + + /** + * Create a box component with the given shape and rotation. + * + * @param shape The shape to create the box from + * @param rotationX The X rotation + * @param rotationY The Y rotation + * @return The box component + */ + private static BoxComponent createBoxComponent(VoxelShape shape, float rotationX, float rotationY) { + return rotateBox(createBoxComponent(shape), Math.toRadians(rotationX), Math.toRadians(rotationY)); + } + + /** + * Rotate the given box component by the given angles. + * + * @param boxComponent The box component to rotate + * @param angleX The X rotation + * @param angleY The Y rotation + * @return The rotated box component + */ + private static BoxComponent rotateBox(BoxComponent boxComponent, double angleX, double angleY) { + if (angleX == 0 && angleY == 0) { + return boxComponent; + } + + // Y offset to fix the box position as pivot is at the bottom + int offsetY = 8; + + Vec3 position = new Vec3(boxComponent.originX(), boxComponent.originY() - offsetY, boxComponent.originZ()); + Vec3 size = new Vec3(boxComponent.sizeX(), boxComponent.sizeY(), boxComponent.sizeZ()); + + // Rotate the box around Y if needed + if (angleY != 0) { + position = rotatePoint(position, new Vec3(0, 1, 0), angleY); + size = rotatePoint(size, new Vec3(0, 1, 0), angleY); + + // Fix the position/size + if (size.x() < 0) position = new Vec3(position.x() + size.x(), position.y(), position.z()); + if (size.y() < 0) position = new Vec3(position.x(), position.y() + size.y(), position.z()); + if (size.z() < 0) position = new Vec3(position.x(), position.y(), position.z() + size.z()); + size = new Vec3(Math.abs(size.x()), Math.abs(size.y()), Math.abs(size.z())); + } + + // Rotate the box around X if needed + if (angleX != 0) { + position = rotatePoint(position, new Vec3(1, 0, 0), angleX); + size = rotatePoint(size, new Vec3(1, 0, 0), angleX); + + // Fix the position/size + if (size.x() < 0) position = new Vec3(position.x() + size.x(), position.y(), position.z()); + if (size.y() < 0) position = new Vec3(position.x(), position.y() + size.y(), position.z()); + if (size.z() < 0) position = new Vec3(position.x(), position.y(), position.z() + size.z()); + size = new Vec3(Math.abs(size.x()), Math.abs(size.y()), Math.abs(size.z())); + } + + // Add back the Y offset + position = new Vec3(position.x(), position.y() + offsetY, position.z()); + + // Round the values to 3 decimal places and return the new box component + return new BoxComponent( + MathUtil.round((float) position.x(), 3), + MathUtil.round((float) position.y(), 3), + MathUtil.round((float) position.z(), 3), + MathUtil.round((float) size.x(), 3), + MathUtil.round((float) size.y(), 3), + MathUtil.round((float) size.z(), 3) + ); + } + + /** + * Rotate the given point around the given axis by the given angle. + * + * @param point The point to rotate + * @param axis The axis to rotate around + * @param angle The angle to rotate by + * @return The rotated point + */ + private static Vec3 rotatePoint(Vec3 point, Vec3 axis, double angle) { + double x = point.x(); + double y = point.y(); + double z = point.z(); + + double u = axis.x(); + double v = axis.y(); + double w = axis.z(); + + double cosAngle = Math.cos(angle); + double sinAngle = Math.sin(angle); + + double newX = (u * (u * x + v * y + w * z) * (1 - cosAngle) + + x * cosAngle + + (-w * y + v * z) * sinAngle); + double newY = (v * (u * x + v * y + w * z) * (1 - cosAngle) + + y * cosAngle + + (w * x - u * z) * sinAngle); + double newZ = (w * (u * x + v * y + w * z) * (1 - cosAngle) + + z * cosAngle + + (-v * x + u * y) * sinAngle); + + return new Vec3(newX, newY, newZ); + } } diff --git a/shared/src/main/java/org/geysermc/hydraulic/util/MathUtil.java b/shared/src/main/java/org/geysermc/hydraulic/util/MathUtil.java new file mode 100644 index 0000000..7527376 --- /dev/null +++ b/shared/src/main/java/org/geysermc/hydraulic/util/MathUtil.java @@ -0,0 +1,21 @@ +package org.geysermc.hydraulic.util; + +public class MathUtil { + /** + * Rounds a float to a specified number of decimal places. + * + * @param value the value to round + * @param places the number of decimal places to round to + * @return the rounded value + */ + public static float round(float value, int places) { + if (places < 0) { + throw new IllegalArgumentException(); + } + + long factor = (long) Math.pow(10, places); + value = value * factor; + long tmp = Math.round(value); + return (float) tmp / factor; + } +}