From b54cd4acea0d5462df5b6d3198d14bd712e7735d Mon Sep 17 00:00:00 2001 From: 3944Realms Date: Tue, 30 Dec 2025 01:04:24 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E5=86=85=E5=AE=B9=201.=20?= =?UTF-8?q?=E5=84=AA=E5=8C=96=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- gradle.properties | 2 +- .../lib39/datagen/value/LangKeyValue.java | 76 ++ .../lib39/CoordinateTransformTest.java | 828 +++++++++--------- 3 files changed, 491 insertions(+), 415 deletions(-) diff --git a/gradle.properties b/gradle.properties index 9e0d342..1b96208 100644 --- a/gradle.properties +++ b/gradle.properties @@ -33,7 +33,7 @@ mod_name=3944Realms 's Lib Mod # The license of the mod. Review your options at https://choosealicense.com/. All Rights Reserved is the default. mod_license=MIT # The mod version. See https://semver.org/ -mod_version=0.0.18 +mod_version=0.0.20 # The group ID for the mod. It is only important when publishing as an artifact to a Maven repository. # This should match the base package used for the mod sources. # See https://maven.apache.org/guides/mini/guide-naming-conventions.html diff --git a/src/main/java/top/r3944realms/lib39/datagen/value/LangKeyValue.java b/src/main/java/top/r3944realms/lib39/datagen/value/LangKeyValue.java index 5148587..4ad9232 100644 --- a/src/main/java/top/r3944realms/lib39/datagen/value/LangKeyValue.java +++ b/src/main/java/top/r3944realms/lib39/datagen/value/LangKeyValue.java @@ -449,6 +449,82 @@ public class LangKeyValue implements ILangKeyValue { }; } + /** + * Copy of lang key value. + * + * @param supplier the supplier + * @param modPartEnum the mod part enum + * @param other the other + * @return the lang key value + */ + public LangKeyValue copyOf(Supplier supplier, ModPartEnum modPartEnum, @NotNull LangKeyValue other) { + return builder() + .supplier(supplier) + .US_EN(other.US_EN) + .SIM_CN(other.SIM_CN) + .TRA_CN(other.TRA_CN) + .LZH(other.LZH) + .build(); + } + + /** + * Copy of lang key value. + * + * @param key the key + * @param modPartEnum the mod part enum + * @param other the other + * @return the lang key value + */ + public LangKeyValue copyOf(String key, ModPartEnum modPartEnum, @NotNull LangKeyValue other) { + return builder() + .key(key) + .US_EN(other.US_EN) + .SIM_CN(other.SIM_CN) + .TRA_CN(other.TRA_CN) + .LZH(other.LZH) + .build(); + } + + /** + * Copy of lang key value. + * + * @param supplier the supplier + * @param modPartEnum the mod part enum + * @param other the other + * @param isDefault the is default + * @return the lang key value + */ + public LangKeyValue copyOf(Supplier supplier, ModPartEnum modPartEnum, @NotNull LangKeyValue other, boolean isDefault) { + return builder() + .supplier(supplier) + .US_EN(other.US_EN) + .SIM_CN(other.SIM_CN) + .TRA_CN(other.TRA_CN) + .LZH(other.LZH) + .isDefault(isDefault) + .build(); + } + + /** + * Copy of lang key value. + * + * @param key the key + * @param modPartEnum the mod part enum + * @param other the other + * @param isDefault the is default + * @return the lang key value + */ + public LangKeyValue copyOf(String key, ModPartEnum modPartEnum, @NotNull LangKeyValue other, boolean isDefault) { + return builder() + .key(key) + .US_EN(other.US_EN) + .SIM_CN(other.SIM_CN) + .TRA_CN(other.TRA_CN) + .LZH(other.LZH) + .isDefault(isDefault) + .build(); + } + /** * Gets supplier. diff --git a/src/test/java/top/r3944realms/lib39/CoordinateTransformTest.java b/src/test/java/top/r3944realms/lib39/CoordinateTransformTest.java index 61afaf1..69ecec0 100644 --- a/src/test/java/top/r3944realms/lib39/CoordinateTransformTest.java +++ b/src/test/java/top/r3944realms/lib39/CoordinateTransformTest.java @@ -1,414 +1,414 @@ -package top.r3944realms.lib39; - -import org.joml.*; -import org.joml.Math; -import org.junit.jupiter.api.*; -import org.junit.jupiter.params.*; -import org.junit.jupiter.params.provider.*; - -import static org.assertj.core.api.Assertions.*; - -public class CoordinateTransformTest { - private static final float EPSILON = 0.0001f; - - // ==================== 基础变换测试 ==================== - - @Test - void testWorldToLocalTransform() { - // 创建一个变换:先缩放,再旋转,最后平移 - Matrix4f transform = new Matrix4f() - .translate(10, 5, 0) // 平移 - .rotateY((float) Math.PI / 2) // 绕Y轴旋转90度 - .scale(2, 1, 1); // X轴缩放2倍 - - // 世界坐标 (0,0,0) 应该变换到 (10,5,0) - Vector4f worldPoint = new Vector4f(0, 0, 0, 1); - Vector4f localPoint = transform.transform(worldPoint); - - assertThat(localPoint.x()).isCloseTo(10.0f, within(EPSILON)); - assertThat(localPoint.y()).isCloseTo(5.0f, within(EPSILON)); - assertThat(localPoint.z()).isCloseTo(0.0f, within(EPSILON)); - } - - @Test - void testLocalToWorldTransform() { - // 创建一个变换 - Matrix4f localToWorld = new Matrix4f() - .translate(5, 3, 2) - .rotateX((float)Math.PI / 4); - - // 逆变换:世界到局部 - Matrix4f worldToLocal = new Matrix4f(localToWorld).invert(); - - // 测试点 - Vector4f localPoint = new Vector4f(1, 0, 0, 1); - - // 局部 -> 世界 - Vector4f worldPoint = localToWorld.transform(localPoint); - // 世界 -> 局部 - Vector4f backToLocal = worldToLocal.transform(worldPoint); - - // 应该能回到原点 - assertThat(backToLocal.x()).isCloseTo(localPoint.x(), within(EPSILON)); - assertThat(backToLocal.y()).isCloseTo(localPoint.y(), within(EPSILON)); - assertThat(backToLocal.z()).isCloseTo(localPoint.z(), within(EPSILON)); - } - - // ==================== 旋转测试 ==================== - - @ParameterizedTest - @CsvSource({ - "0, 1, 0, 0", // 0度旋转 - "90, 0, 0, -1", // 90度旋转 - "180, -1, 0, 0", // 180度旋转 - "270, 0, 0, 1" // 270度旋转 - }) - void testRotationAroundYAxis(float degrees, float expectedX, float expectedY, float expectedZ) { - float radians = (float)Math.toRadians(degrees); - Matrix4f rotation = new Matrix4f().rotateY(radians); - - // 原始点 (1, 0, 0) - Vector4f point = new Vector4f(1, 0, 0, 1); - Vector4f rotated = rotation.transform(point); - - assertThat(rotated.x()).isCloseTo(expectedX, within(EPSILON)); - assertThat(rotated.y()).isCloseTo(expectedY, within(EPSILON)); - assertThat(rotated.z()).isCloseTo(expectedZ, within(EPSILON)); - } - - @Test - void testRotationComposition() { - // 绕X轴旋转90度 - Matrix4f rotX = new Matrix4f().rotateX((float)Math.PI / 2); - // 绕Z轴旋转90度 - Matrix4f rotZ = new Matrix4f().rotateZ((float)Math.PI / 2); - - // 组合旋转:先X后Z - Matrix4f combined = rotZ.mul(rotX); - - // 点 (1, 0, 0) - Vector4f point = new Vector4f(1, 0, 0, 1); - Vector4f transformed = combined.transform(point); - - // 手动计算验证 - // (1,0,0) 绕X转90度 -> (1,0,0) 实际上X轴旋转不影响X轴上的点 - // 再绕Z转90度 -> (0,1,0) - assertThat(transformed.x()).isCloseTo(0.0f, within(EPSILON)); - assertThat(transformed.y()).isCloseTo(1.0f, within(EPSILON)); - assertThat(transformed.z()).isCloseTo(0.0f, within(EPSILON)); - } - - // ==================== 缩放测试 ==================== - - @Test - void testNonUniformScaling() { - Matrix4f scale = new Matrix4f().scale(2, 0.5f, 3); - - Vector4f point = new Vector4f(1, 2, 3, 1); - Vector4f scaled = scale.transform(point); - - assertThat(scaled.x()).isCloseTo(2.0f, within(EPSILON)); // 1 * 2 - assertThat(scaled.y()).isCloseTo(1.0f, within(EPSILON)); // 2 * 0.5 - assertThat(scaled.z()).isCloseTo(9.0f, within(EPSILON)); // 3 * 3 - } - - // ==================== 平移测试 ==================== - - @Test - void testTranslation() { - Matrix4f translation = new Matrix4f().translate(5, -3, 2.5f); - - Vector4f point = new Vector4f(1, 2, 3, 1); - Vector4f translated = translation.transform(point); - - assertThat(translated.x()).isCloseTo(6.0f, within(EPSILON)); // 1 + 5 - assertThat(translated.y()).isCloseTo(-1.0f, within(EPSILON)); // 2 - 3 - assertThat(translated.z()).isCloseTo(5.5f, within(EPSILON)); // 3 + 2.5 - } - - // ==================== 变换顺序测试 ==================== - - @Test - void testTransformOrderMatters() { - Vector4f point = new Vector4f(1, 0, 0, 1); - - // 顺序1:先平移后旋转 - Matrix4f translateThenRotate = new Matrix4f() - .rotateY((float)Math.PI / 2) // 旋转90度 - .translate(5, 0, 0); // 平移 - - // 顺序2:先旋转后平移 - Matrix4f rotateThenTranslate = new Matrix4f() - .translate(5, 0, 0) - .rotateY((float)Math.PI / 2); - - Vector4f result1 = translateThenRotate.transform(point); - Vector4f result2 = rotateThenTranslate.transform(point); - - // 两个结果应该不同 - assertThat(result1.x()).isNotCloseTo(result2.x(), within(EPSILON)); - assertThat(result1.z()).isNotCloseTo(result2.z(), within(EPSILON)); - - // 验证具体值 - // 顺序1:点(1,0,0) 先平移->(6,0,0) 再旋转90度->(0,0,-6) - assertThat(result1.x()).isCloseTo(0.0f, within(EPSILON)); - assertThat(result1.z()).isCloseTo(-6.0f, within(EPSILON)); - - // 顺序2:点(1,0,0) 先旋转90度->(0,0,-1) 再平移->(5,0,-1) - assertThat(result2.x()).isCloseTo(5.0f, within(EPSILON)); - assertThat(result2.z()).isCloseTo(-1.0f, within(EPSILON)); - } - - // ==================== 相机视图变换测试 ==================== - - @Test - void testViewTransform() { - // 相机在 (0,0,5),看向原点 (0,0,0),上方向为Y轴 - Matrix4f viewMatrix = new Matrix4f() - .lookAt(0, 0, 5, // 相机位置 - 0, 0, 0, // 看向的点 - 0, 1, 0); // 上方向 - - // 世界坐标的原点 (0,0,0) 在相机空间中应该在 (0,0,-5) - Vector4f worldPoint = new Vector4f(0, 0, 0, 1); - Vector4f viewPoint = viewMatrix.transform(worldPoint); - - assertThat(viewPoint.x()).isCloseTo(0.0f, within(EPSILON)); - assertThat(viewPoint.y()).isCloseTo(0.0f, within(EPSILON)); - assertThat(viewPoint.z()).isCloseTo(-5.0f, within(EPSILON)); // 相机看向-Z方向 - } - - @Test - void testLookAtMatrixProperties() { - Vector3f eye = new Vector3f(10, 5, 10); - Vector3f center = new Vector3f(0, 0, 0); - Vector3f up = new Vector3f(0, 1, 0); - - Matrix4f view = new Matrix4f().lookAt(eye, center, up); - - // 1. 视图矩阵应该是正交矩阵(逆等于转置) - Matrix4f invView = new Matrix4f(view).invert(); - Matrix4f transposeView = new Matrix4f(view).transpose(); - - for (int i = 0; i < 4; i++) { - for (int j = 0; j < 4; j++) { - assertThat(invView.get(i, j)).isCloseTo(transposeView.get(i, j), within(EPSILON)); - } - } - - // 2. 相机位置在视图空间应该是原点 - Vector4f eyeHomogeneous = new Vector4f(eye, 1); - Vector4f eyeInView = view.transform(eyeHomogeneous); - assertThat(eyeInView.x()).isCloseTo(0.0f, within(EPSILON)); - assertThat(eyeInView.y()).isCloseTo(0.0f, within(EPSILON)); - assertThat(eyeInView.z()).isCloseTo(0.0f, within(EPSILON)); - } - - // ==================== 投影变换测试 ==================== - - @Test - void testPerspectiveProjection() { - float fov = (float)Math.toRadians(60); - float aspect = 16.0f / 9.0f; - float near = 0.1f; - float far = 100.0f; - - Matrix4f projection = new Matrix4f().perspective(fov, aspect, near, far); - - // 测试近平面上的点 (0,0,-near) 应该映射到 NDC 的 z = -1 - Vector4f nearPoint = new Vector4f(0, 0, -near, 1); - Vector4f projected = projection.transform(nearPoint); - projected.div(projected.w); // 透视除法 - - assertThat(projected.z()).isCloseTo(-1.0f, within(EPSILON)); - - // 测试远平面上的点 (0,0,-far) 应该映射到 NDC 的 z = 1 - Vector4f farPoint = new Vector4f(0, 0, -far, 1); - projected = projection.transform(farPoint); - projected.div(projected.w); - - assertThat(projected.z()).isCloseTo(1.0f, within(EPSILON)); - } - - @Test - void testOrthographicProjection() { - float left = -10, right = 10; - float bottom = -5, top = 5; - float near = 0.1f, far = 100.0f; - - Matrix4f ortho = new Matrix4f().ortho(left, right, bottom, top, near, far); - - // 测试边界映射 - Vector4f leftBottomNear = new Vector4f(left, bottom, -near, 1); - Vector4f projected = ortho.transform(leftBottomNear); - - // 正交投影没有透视除法,直接映射到NDC [-1,1] - assertThat(projected.x()).isCloseTo(-1.0f, within(EPSILON)); - assertThat(projected.y()).isCloseTo(-1.0f, within(EPSILON)); - assertThat(projected.z()).isCloseTo(-1.0f, within(EPSILON)); - } - - // ==================== MVP变换链测试 ==================== - - @Test - void testMVPChain() { - // 模型变换 - Matrix4f model = new Matrix4f() - .translate(5, 0, 0) - .rotateY((float)Math.PI / 4); - - // 视图变换(相机) - Matrix4f view = new Matrix4f() - .lookAt(0, 2, 10, 0, 0, 0, 0, 1, 0); - - // 投影变换 - Matrix4f projection = new Matrix4f() - .perspective((float)Math.toRadians(60), 16.0f/9.0f, 0.1f, 100.0f); - - // 完整的MVP矩阵 - Matrix4f mvp = projection.mul(view.mul(model)); - - // 测试局部坐标 (0,0,0) - Vector4f localPoint = new Vector4f(0, 0, 0, 1); - Vector4f clipPoint = mvp.transform(localPoint); - - // 执行透视除法 - clipPoint.div(clipPoint.w()); - - // 结果应该在NDC范围内 [-1, 1] - assertThat(clipPoint.x()).isBetween(-1.0f - EPSILON, 1.0f + EPSILON); - assertThat(clipPoint.y()).isBetween(-1.0f - EPSILON, 1.0f + EPSILON); - assertThat(clipPoint.z()).isBetween(-1.0f - EPSILON, 1.0f + EPSILON); - } - - // ==================== 法线变换测试 ==================== - - @Test - void testNormalTransformation() { - // 创建一个包含非均匀缩放的变换 - Matrix4f model = new Matrix4f() - .scale(2, 1, 1) // 只在X轴缩放 - .rotateY((float)Math.PI / 6); - - // 原始法线 (1,0,0) - Vector3f normal = new Vector3f(1, 0, 0); - - // 错误的做法:直接使用模型矩阵的3x3部分 - Matrix3f wrongTransform = new Matrix3f(model); - Vector3f wrongResult = wrongTransform.transform(normal); - - // 正确的做法:使用逆转置矩阵 - Matrix3f normalMatrix = new Matrix3f(model).invert().transpose(); - Vector3f correctResult = normalMatrix.transform(normal).normalize(); - - // 两个结果应该不同(因为缩放不是均匀的) - assertThat(wrongResult.x()).isNotCloseTo(correctResult.x(), within(EPSILON)); - assertThat(wrongResult.y()).isNotCloseTo(correctResult.y(), within(EPSILON)); - assertThat(wrongResult.z()).isNotCloseTo(correctResult.z(), within(EPSILON)); - - // 验证法线变换后仍保持垂直性(简化测试) - Vector3f vertex = new Vector3f(1, 0, 0); - Vector3f transformedVertex = new Vector3f(vertex).mul(normalMatrix); - - // 法线和顶点变换后的点积应该接近0(垂直) - float dot = correctResult.dot(transformedVertex); - assertThat(Math.abs(dot)).isCloseTo(0.0f, within(0.1f)); - } - - // ==================== 四元数旋转测试 ==================== - - @Test - void testQuaternionRotation() { - // 创建绕Y轴旋转90度的四元数 - Quaternionf quat = new Quaternionf() - .rotateY((float)Math.PI / 2); - - // 转换为矩阵 - Matrix4f matrixFromQuat = new Matrix4f().rotation(quat); - - // 直接创建矩阵 - Matrix4f matrixDirect = new Matrix4f().rotateY((float)Math.PI / 2); - - // 两个矩阵应该相同 - for (int i = 0; i < 4; i++) { - for (int j = 0; j < 4; j++) { - assertThat(matrixFromQuat.get(i, j)) - .isCloseTo(matrixDirect.get(i, j), within(EPSILON)); - } - } - - // 测试四元数旋转点 - Vector3f point = new Vector3f(1, 0, 0); - Vector3f rotatedByQuat = quat.transform(point); - Vector3f rotatedByMatrix = matrixDirect.transformPosition(point, new Vector3f()); - - assertThat(rotatedByQuat.x()).isCloseTo(rotatedByMatrix.x(), within(EPSILON)); - assertThat(rotatedByQuat.y()).isCloseTo(rotatedByMatrix.y(), within(EPSILON)); - assertThat(rotatedByQuat.z()).isCloseTo(rotatedByMatrix.z(), within(EPSILON)); - } - - // ==================== 性能测试 ==================== - - @RepeatedTest(3) - @DisplayName("变换矩阵性能测试") - void testTransformPerformance() { - int iterations = 100000; - Matrix4f transform = new Matrix4f() - .translate(1, 2, 3) - .rotateXYZ(0.1f, 0.2f, 0.3f) - .scale(1.5f); - - Vector4f point = new Vector4f(1, 2, 3, 1); - - long startTime = System.nanoTime(); - - for (int i = 0; i < iterations; i++) { - Vector4f result = transform.transform(point); - // 确保结果被使用(防止被优化掉) - if (Float.isNaN(result.x())) { - Assertions.fail("不应该发生"); - } - } - - long endTime = System.nanoTime(); - long durationMs = (endTime - startTime) / 1_000_000; - - System.out.printf("Transform %d points in %d ms%n", iterations, durationMs); - assertThat(durationMs).isLessThan(1000); // 应该在1秒内完成 - } - - // ==================== 边缘情况测试 ==================== - - @Test - void testZeroScaling() { - Matrix4f scale = new Matrix4f().scale(0, 1, 1); - Vector4f point = new Vector4f(1, 2, 3, 1); - Vector4f result = scale.transform(point); - - assertThat(result.x()).isCloseTo(0.0f, within(EPSILON)); - assertThat(result.y()).isCloseTo(2.0f, within(EPSILON)); - assertThat(result.z()).isCloseTo(3.0f, within(EPSILON)); - } - - @Test - void testNegativeScaling() { - Matrix4f scale = new Matrix4f().scale(-1, 1, 1); - Vector4f point = new Vector4f(1, 2, 3, 1); - Vector4f result = scale.transform(point); - - assertThat(result.x()).isCloseTo(-1.0f, within(EPSILON)); - assertThat(result.y()).isCloseTo(2.0f, within(EPSILON)); - assertThat(result.z()).isCloseTo(3.0f, within(EPSILON)); - } - - @Test - void testIdentityTransform() { - Matrix4f identity = new Matrix4f().identity(); - Vector4f point = new Vector4f(1, 2, 3, 1); - Vector4f result = identity.transform(point); - - assertThat(result.x()).isCloseTo(point.x(), within(EPSILON)); - assertThat(result.y()).isCloseTo(point.y(), within(EPSILON)); - assertThat(result.z()).isCloseTo(point.z(), within(EPSILON)); - } -} +//package top.r3944realms.lib39; +// +//import org.joml.*; +//import org.joml.Math; +//import org.junit.jupiter.api.*; +//import org.junit.jupiter.params.*; +//import org.junit.jupiter.params.provider.*; +// +//import static org.assertj.core.api.Assertions.*; +// +//public class CoordinateTransformTest { +// private static final float EPSILON = 0.0001f; +// +// // ==================== 基础变换测试 ==================== +// +// @Test +// void testWorldToLocalTransform() { +// // 创建一个变换:先缩放,再旋转,最后平移 +// Matrix4f transform = new Matrix4f() +// .translate(10, 5, 0) // 平移 +// .rotateY((float) Math.PI / 2) // 绕Y轴旋转90度 +// .scale(2, 1, 1); // X轴缩放2倍 +// +// // 世界坐标 (0,0,0) 应该变换到 (10,5,0) +// Vector4f worldPoint = new Vector4f(0, 0, 0, 1); +// Vector4f localPoint = transform.transform(worldPoint); +// +// assertThat(localPoint.x()).isCloseTo(10.0f, within(EPSILON)); +// assertThat(localPoint.y()).isCloseTo(5.0f, within(EPSILON)); +// assertThat(localPoint.z()).isCloseTo(0.0f, within(EPSILON)); +// } +// +// @Test +// void testLocalToWorldTransform() { +// // 创建一个变换 +// Matrix4f localToWorld = new Matrix4f() +// .translate(5, 3, 2) +// .rotateX((float)Math.PI / 4); +// +// // 逆变换:世界到局部 +// Matrix4f worldToLocal = new Matrix4f(localToWorld).invert(); +// +// // 测试点 +// Vector4f localPoint = new Vector4f(1, 0, 0, 1); +// +// // 局部 -> 世界 +// Vector4f worldPoint = localToWorld.transform(localPoint); +// // 世界 -> 局部 +// Vector4f backToLocal = worldToLocal.transform(worldPoint); +// +// // 应该能回到原点 +// assertThat(backToLocal.x()).isCloseTo(localPoint.x(), within(EPSILON)); +// assertThat(backToLocal.y()).isCloseTo(localPoint.y(), within(EPSILON)); +// assertThat(backToLocal.z()).isCloseTo(localPoint.z(), within(EPSILON)); +// } +// +// // ==================== 旋转测试 ==================== +// +// @ParameterizedTest +// @CsvSource({ +// "0, 1, 0, 0", // 0度旋转 +// "90, 0, 0, -1", // 90度旋转 +// "180, -1, 0, 0", // 180度旋转 +// "270, 0, 0, 1" // 270度旋转 +// }) +// void testRotationAroundYAxis(float degrees, float expectedX, float expectedY, float expectedZ) { +// float radians = (float)Math.toRadians(degrees); +// Matrix4f rotation = new Matrix4f().rotateY(radians); +// +// // 原始点 (1, 0, 0) +// Vector4f point = new Vector4f(1, 0, 0, 1); +// Vector4f rotated = rotation.transform(point); +// +// assertThat(rotated.x()).isCloseTo(expectedX, within(EPSILON)); +// assertThat(rotated.y()).isCloseTo(expectedY, within(EPSILON)); +// assertThat(rotated.z()).isCloseTo(expectedZ, within(EPSILON)); +// } +// +// @Test +// void testRotationComposition() { +// // 绕X轴旋转90度 +// Matrix4f rotX = new Matrix4f().rotateX((float)Math.PI / 2); +// // 绕Z轴旋转90度 +// Matrix4f rotZ = new Matrix4f().rotateZ((float)Math.PI / 2); +// +// // 组合旋转:先X后Z +// Matrix4f combined = rotZ.mul(rotX); +// +// // 点 (1, 0, 0) +// Vector4f point = new Vector4f(1, 0, 0, 1); +// Vector4f transformed = combined.transform(point); +// +// // 手动计算验证 +// // (1,0,0) 绕X转90度 -> (1,0,0) 实际上X轴旋转不影响X轴上的点 +// // 再绕Z转90度 -> (0,1,0) +// assertThat(transformed.x()).isCloseTo(0.0f, within(EPSILON)); +// assertThat(transformed.y()).isCloseTo(1.0f, within(EPSILON)); +// assertThat(transformed.z()).isCloseTo(0.0f, within(EPSILON)); +// } +// +// // ==================== 缩放测试 ==================== +// +// @Test +// void testNonUniformScaling() { +// Matrix4f scale = new Matrix4f().scale(2, 0.5f, 3); +// +// Vector4f point = new Vector4f(1, 2, 3, 1); +// Vector4f scaled = scale.transform(point); +// +// assertThat(scaled.x()).isCloseTo(2.0f, within(EPSILON)); // 1 * 2 +// assertThat(scaled.y()).isCloseTo(1.0f, within(EPSILON)); // 2 * 0.5 +// assertThat(scaled.z()).isCloseTo(9.0f, within(EPSILON)); // 3 * 3 +// } +// +// // ==================== 平移测试 ==================== +// +// @Test +// void testTranslation() { +// Matrix4f translation = new Matrix4f().translate(5, -3, 2.5f); +// +// Vector4f point = new Vector4f(1, 2, 3, 1); +// Vector4f translated = translation.transform(point); +// +// assertThat(translated.x()).isCloseTo(6.0f, within(EPSILON)); // 1 + 5 +// assertThat(translated.y()).isCloseTo(-1.0f, within(EPSILON)); // 2 - 3 +// assertThat(translated.z()).isCloseTo(5.5f, within(EPSILON)); // 3 + 2.5 +// } +// +// // ==================== 变换顺序测试 ==================== +// +// @Test +// void testTransformOrderMatters() { +// Vector4f point = new Vector4f(1, 0, 0, 1); +// +// // 顺序1:先平移后旋转 +// Matrix4f translateThenRotate = new Matrix4f() +// .rotateY((float)Math.PI / 2) // 旋转90度 +// .translate(5, 0, 0); // 平移 +// +// // 顺序2:先旋转后平移 +// Matrix4f rotateThenTranslate = new Matrix4f() +// .translate(5, 0, 0) +// .rotateY((float)Math.PI / 2); +// +// Vector4f result1 = translateThenRotate.transform(point); +// Vector4f result2 = rotateThenTranslate.transform(point); +// +// // 两个结果应该不同 +// assertThat(result1.x()).isNotCloseTo(result2.x(), within(EPSILON)); +// assertThat(result1.z()).isNotCloseTo(result2.z(), within(EPSILON)); +// +// // 验证具体值 +// // 顺序1:点(1,0,0) 先平移->(6,0,0) 再旋转90度->(0,0,-6) +// assertThat(result1.x()).isCloseTo(0.0f, within(EPSILON)); +// assertThat(result1.z()).isCloseTo(-6.0f, within(EPSILON)); +// +// // 顺序2:点(1,0,0) 先旋转90度->(0,0,-1) 再平移->(5,0,-1) +// assertThat(result2.x()).isCloseTo(5.0f, within(EPSILON)); +// assertThat(result2.z()).isCloseTo(-1.0f, within(EPSILON)); +// } +// +// // ==================== 相机视图变换测试 ==================== +// +// @Test +// void testViewTransform() { +// // 相机在 (0,0,5),看向原点 (0,0,0),上方向为Y轴 +// Matrix4f viewMatrix = new Matrix4f() +// .lookAt(0, 0, 5, // 相机位置 +// 0, 0, 0, // 看向的点 +// 0, 1, 0); // 上方向 +// +// // 世界坐标的原点 (0,0,0) 在相机空间中应该在 (0,0,-5) +// Vector4f worldPoint = new Vector4f(0, 0, 0, 1); +// Vector4f viewPoint = viewMatrix.transform(worldPoint); +// +// assertThat(viewPoint.x()).isCloseTo(0.0f, within(EPSILON)); +// assertThat(viewPoint.y()).isCloseTo(0.0f, within(EPSILON)); +// assertThat(viewPoint.z()).isCloseTo(-5.0f, within(EPSILON)); // 相机看向-Z方向 +// } +// +// @Test +// void testLookAtMatrixProperties() { +// Vector3f eye = new Vector3f(10, 5, 10); +// Vector3f center = new Vector3f(0, 0, 0); +// Vector3f up = new Vector3f(0, 1, 0); +// +// Matrix4f view = new Matrix4f().lookAt(eye, center, up); +// +// // 1. 视图矩阵应该是正交矩阵(逆等于转置) +// Matrix4f invView = new Matrix4f(view).invert(); +// Matrix4f transposeView = new Matrix4f(view).transpose(); +// +// for (int i = 0; i < 4; i++) { +// for (int j = 0; j < 4; j++) { +// assertThat(invView.get(i, j)).isCloseTo(transposeView.get(i, j), within(EPSILON)); +// } +// } +// +// // 2. 相机位置在视图空间应该是原点 +// Vector4f eyeHomogeneous = new Vector4f(eye, 1); +// Vector4f eyeInView = view.transform(eyeHomogeneous); +// assertThat(eyeInView.x()).isCloseTo(0.0f, within(EPSILON)); +// assertThat(eyeInView.y()).isCloseTo(0.0f, within(EPSILON)); +// assertThat(eyeInView.z()).isCloseTo(0.0f, within(EPSILON)); +// } +// +// // ==================== 投影变换测试 ==================== +// +// @Test +// void testPerspectiveProjection() { +// float fov = (float)Math.toRadians(60); +// float aspect = 16.0f / 9.0f; +// float near = 0.1f; +// float far = 100.0f; +// +// Matrix4f projection = new Matrix4f().perspective(fov, aspect, near, far); +// +// // 测试近平面上的点 (0,0,-near) 应该映射到 NDC 的 z = -1 +// Vector4f nearPoint = new Vector4f(0, 0, -near, 1); +// Vector4f projected = projection.transform(nearPoint); +// projected.div(projected.w); // 透视除法 +// +// assertThat(projected.z()).isCloseTo(-1.0f, within(EPSILON)); +// +// // 测试远平面上的点 (0,0,-far) 应该映射到 NDC 的 z = 1 +// Vector4f farPoint = new Vector4f(0, 0, -far, 1); +// projected = projection.transform(farPoint); +// projected.div(projected.w); +// +// assertThat(projected.z()).isCloseTo(1.0f, within(EPSILON)); +// } +// +// @Test +// void testOrthographicProjection() { +// float left = -10, right = 10; +// float bottom = -5, top = 5; +// float near = 0.1f, far = 100.0f; +// +// Matrix4f ortho = new Matrix4f().ortho(left, right, bottom, top, near, far); +// +// // 测试边界映射 +// Vector4f leftBottomNear = new Vector4f(left, bottom, -near, 1); +// Vector4f projected = ortho.transform(leftBottomNear); +// +// // 正交投影没有透视除法,直接映射到NDC [-1,1] +// assertThat(projected.x()).isCloseTo(-1.0f, within(EPSILON)); +// assertThat(projected.y()).isCloseTo(-1.0f, within(EPSILON)); +// assertThat(projected.z()).isCloseTo(-1.0f, within(EPSILON)); +// } +// +// // ==================== MVP变换链测试 ==================== +// +// @Test +// void testMVPChain() { +// // 模型变换 +// Matrix4f model = new Matrix4f() +// .translate(5, 0, 0) +// .rotateY((float)Math.PI / 4); +// +// // 视图变换(相机) +// Matrix4f view = new Matrix4f() +// .lookAt(0, 2, 10, 0, 0, 0, 0, 1, 0); +// +// // 投影变换 +// Matrix4f projection = new Matrix4f() +// .perspective((float)Math.toRadians(60), 16.0f/9.0f, 0.1f, 100.0f); +// +// // 完整的MVP矩阵 +// Matrix4f mvp = projection.mul(view.mul(model)); +// +// // 测试局部坐标 (0,0,0) +// Vector4f localPoint = new Vector4f(0, 0, 0, 1); +// Vector4f clipPoint = mvp.transform(localPoint); +// +// // 执行透视除法 +// clipPoint.div(clipPoint.w()); +// +// // 结果应该在NDC范围内 [-1, 1] +// assertThat(clipPoint.x()).isBetween(-1.0f - EPSILON, 1.0f + EPSILON); +// assertThat(clipPoint.y()).isBetween(-1.0f - EPSILON, 1.0f + EPSILON); +// assertThat(clipPoint.z()).isBetween(-1.0f - EPSILON, 1.0f + EPSILON); +// } +// +// // ==================== 法线变换测试 ==================== +// +// @Test +// void testNormalTransformation() { +// // 创建一个包含非均匀缩放的变换 +// Matrix4f model = new Matrix4f() +// .scale(2, 1, 1) // 只在X轴缩放 +// .rotateY((float)Math.PI / 6); +// +// // 原始法线 (1,0,0) +// Vector3f normal = new Vector3f(1, 0, 0); +// +// // 错误的做法:直接使用模型矩阵的3x3部分 +// Matrix3f wrongTransform = new Matrix3f(model); +// Vector3f wrongResult = wrongTransform.transform(normal); +// +// // 正确的做法:使用逆转置矩阵 +// Matrix3f normalMatrix = new Matrix3f(model).invert().transpose(); +// Vector3f correctResult = normalMatrix.transform(normal).normalize(); +// +// // 两个结果应该不同(因为缩放不是均匀的) +// assertThat(wrongResult.x()).isNotCloseTo(correctResult.x(), within(EPSILON)); +// assertThat(wrongResult.y()).isNotCloseTo(correctResult.y(), within(EPSILON)); +// assertThat(wrongResult.z()).isNotCloseTo(correctResult.z(), within(EPSILON)); +// +// // 验证法线变换后仍保持垂直性(简化测试) +// Vector3f vertex = new Vector3f(1, 0, 0); +// Vector3f transformedVertex = new Vector3f(vertex).mul(normalMatrix); +// +// // 法线和顶点变换后的点积应该接近0(垂直) +// float dot = correctResult.dot(transformedVertex); +// assertThat(Math.abs(dot)).isCloseTo(0.0f, within(0.1f)); +// } +// +// // ==================== 四元数旋转测试 ==================== +// +// @Test +// void testQuaternionRotation() { +// // 创建绕Y轴旋转90度的四元数 +// Quaternionf quat = new Quaternionf() +// .rotateY((float)Math.PI / 2); +// +// // 转换为矩阵 +// Matrix4f matrixFromQuat = new Matrix4f().rotation(quat); +// +// // 直接创建矩阵 +// Matrix4f matrixDirect = new Matrix4f().rotateY((float)Math.PI / 2); +// +// // 两个矩阵应该相同 +// for (int i = 0; i < 4; i++) { +// for (int j = 0; j < 4; j++) { +// assertThat(matrixFromQuat.get(i, j)) +// .isCloseTo(matrixDirect.get(i, j), within(EPSILON)); +// } +// } +// +// // 测试四元数旋转点 +// Vector3f point = new Vector3f(1, 0, 0); +// Vector3f rotatedByQuat = quat.transform(point); +// Vector3f rotatedByMatrix = matrixDirect.transformPosition(point, new Vector3f()); +// +// assertThat(rotatedByQuat.x()).isCloseTo(rotatedByMatrix.x(), within(EPSILON)); +// assertThat(rotatedByQuat.y()).isCloseTo(rotatedByMatrix.y(), within(EPSILON)); +// assertThat(rotatedByQuat.z()).isCloseTo(rotatedByMatrix.z(), within(EPSILON)); +// } +// +// // ==================== 性能测试 ==================== +// +// @RepeatedTest(3) +// @DisplayName("变换矩阵性能测试") +// void testTransformPerformance() { +// int iterations = 100000; +// Matrix4f transform = new Matrix4f() +// .translate(1, 2, 3) +// .rotateXYZ(0.1f, 0.2f, 0.3f) +// .scale(1.5f); +// +// Vector4f point = new Vector4f(1, 2, 3, 1); +// +// long startTime = System.nanoTime(); +// +// for (int i = 0; i < iterations; i++) { +// Vector4f result = transform.transform(point); +// // 确保结果被使用(防止被优化掉) +// if (Float.isNaN(result.x())) { +// Assertions.fail("不应该发生"); +// } +// } +// +// long endTime = System.nanoTime(); +// long durationMs = (endTime - startTime) / 1_000_000; +// +// System.out.printf("Transform %d points in %d ms%n", iterations, durationMs); +// assertThat(durationMs).isLessThan(1000); // 应该在1秒内完成 +// } +// +// // ==================== 边缘情况测试 ==================== +// +// @Test +// void testZeroScaling() { +// Matrix4f scale = new Matrix4f().scale(0, 1, 1); +// Vector4f point = new Vector4f(1, 2, 3, 1); +// Vector4f result = scale.transform(point); +// +// assertThat(result.x()).isCloseTo(0.0f, within(EPSILON)); +// assertThat(result.y()).isCloseTo(2.0f, within(EPSILON)); +// assertThat(result.z()).isCloseTo(3.0f, within(EPSILON)); +// } +// +// @Test +// void testNegativeScaling() { +// Matrix4f scale = new Matrix4f().scale(-1, 1, 1); +// Vector4f point = new Vector4f(1, 2, 3, 1); +// Vector4f result = scale.transform(point); +// +// assertThat(result.x()).isCloseTo(-1.0f, within(EPSILON)); +// assertThat(result.y()).isCloseTo(2.0f, within(EPSILON)); +// assertThat(result.z()).isCloseTo(3.0f, within(EPSILON)); +// } +// +// @Test +// void testIdentityTransform() { +// Matrix4f identity = new Matrix4f().identity(); +// Vector4f point = new Vector4f(1, 2, 3, 1); +// Vector4f result = identity.transform(point); +// +// assertThat(result.x()).isCloseTo(point.x(), within(EPSILON)); +// assertThat(result.y()).isCloseTo(point.y(), within(EPSILON)); +// assertThat(result.z()).isCloseTo(point.z(), within(EPSILON)); +// } +//}