更新内容

1. 優化方法
This commit is contained in:
叁玖领域 2025-12-30 01:04:24 +08:00
parent c0a25f9172
commit b54cd4acea
3 changed files with 491 additions and 415 deletions

View File

@ -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

View File

@ -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.

View File

@ -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));
// }
//}