更新内容

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. # The license of the mod. Review your options at https://choosealicense.com/. All Rights Reserved is the default.
mod_license=MIT mod_license=MIT
# The mod version. See https://semver.org/ # 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. # 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. # This should match the base package used for the mod sources.
# See https://maven.apache.org/guides/mini/guide-naming-conventions.html # 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. * Gets supplier.

View File

@ -1,414 +1,414 @@
package top.r3944realms.lib39; //package top.r3944realms.lib39;
//
import org.joml.*; //import org.joml.*;
import org.joml.Math; //import org.joml.Math;
import org.junit.jupiter.api.*; //import org.junit.jupiter.api.*;
import org.junit.jupiter.params.*; //import org.junit.jupiter.params.*;
import org.junit.jupiter.params.provider.*; //import org.junit.jupiter.params.provider.*;
//
import static org.assertj.core.api.Assertions.*; //import static org.assertj.core.api.Assertions.*;
//
public class CoordinateTransformTest { //public class CoordinateTransformTest {
private static final float EPSILON = 0.0001f; // private static final float EPSILON = 0.0001f;
//
// ==================== 基础变换测试 ==================== // // ==================== 基础变换测试 ====================
//
@Test // @Test
void testWorldToLocalTransform() { // void testWorldToLocalTransform() {
// 创建一个变换先缩放再旋转最后平移 // // 创建一个变换先缩放再旋转最后平移
Matrix4f transform = new Matrix4f() // Matrix4f transform = new Matrix4f()
.translate(10, 5, 0) // 平移 // .translate(10, 5, 0) // 平移
.rotateY((float) Math.PI / 2) // 绕Y轴旋转90度 // .rotateY((float) Math.PI / 2) // 绕Y轴旋转90度
.scale(2, 1, 1); // X轴缩放2倍 // .scale(2, 1, 1); // X轴缩放2倍
//
// 世界坐标 (0,0,0) 应该变换到 (10,5,0) // // 世界坐标 (0,0,0) 应该变换到 (10,5,0)
Vector4f worldPoint = new Vector4f(0, 0, 0, 1); // Vector4f worldPoint = new Vector4f(0, 0, 0, 1);
Vector4f localPoint = transform.transform(worldPoint); // Vector4f localPoint = transform.transform(worldPoint);
//
assertThat(localPoint.x()).isCloseTo(10.0f, within(EPSILON)); // assertThat(localPoint.x()).isCloseTo(10.0f, within(EPSILON));
assertThat(localPoint.y()).isCloseTo(5.0f, within(EPSILON)); // assertThat(localPoint.y()).isCloseTo(5.0f, within(EPSILON));
assertThat(localPoint.z()).isCloseTo(0.0f, within(EPSILON)); // assertThat(localPoint.z()).isCloseTo(0.0f, within(EPSILON));
} // }
//
@Test // @Test
void testLocalToWorldTransform() { // void testLocalToWorldTransform() {
// 创建一个变换 // // 创建一个变换
Matrix4f localToWorld = new Matrix4f() // Matrix4f localToWorld = new Matrix4f()
.translate(5, 3, 2) // .translate(5, 3, 2)
.rotateX((float)Math.PI / 4); // .rotateX((float)Math.PI / 4);
//
// 逆变换世界到局部 // // 逆变换世界到局部
Matrix4f worldToLocal = new Matrix4f(localToWorld).invert(); // Matrix4f worldToLocal = new Matrix4f(localToWorld).invert();
//
// 测试点 // // 测试点
Vector4f localPoint = new Vector4f(1, 0, 0, 1); // Vector4f localPoint = new Vector4f(1, 0, 0, 1);
//
// 局部 -> 世界 // // 局部 -> 世界
Vector4f worldPoint = localToWorld.transform(localPoint); // Vector4f worldPoint = localToWorld.transform(localPoint);
// 世界 -> 局部 // // 世界 -> 局部
Vector4f backToLocal = worldToLocal.transform(worldPoint); // Vector4f backToLocal = worldToLocal.transform(worldPoint);
//
// 应该能回到原点 // // 应该能回到原点
assertThat(backToLocal.x()).isCloseTo(localPoint.x(), within(EPSILON)); // assertThat(backToLocal.x()).isCloseTo(localPoint.x(), within(EPSILON));
assertThat(backToLocal.y()).isCloseTo(localPoint.y(), within(EPSILON)); // assertThat(backToLocal.y()).isCloseTo(localPoint.y(), within(EPSILON));
assertThat(backToLocal.z()).isCloseTo(localPoint.z(), within(EPSILON)); // assertThat(backToLocal.z()).isCloseTo(localPoint.z(), within(EPSILON));
} // }
//
// ==================== 旋转测试 ==================== // // ==================== 旋转测试 ====================
//
@ParameterizedTest // @ParameterizedTest
@CsvSource({ // @CsvSource({
"0, 1, 0, 0", // 0度旋转 // "0, 1, 0, 0", // 0度旋转
"90, 0, 0, -1", // 90度旋转 // "90, 0, 0, -1", // 90度旋转
"180, -1, 0, 0", // 180度旋转 // "180, -1, 0, 0", // 180度旋转
"270, 0, 0, 1" // 270度旋转 // "270, 0, 0, 1" // 270度旋转
}) // })
void testRotationAroundYAxis(float degrees, float expectedX, float expectedY, float expectedZ) { // void testRotationAroundYAxis(float degrees, float expectedX, float expectedY, float expectedZ) {
float radians = (float)Math.toRadians(degrees); // float radians = (float)Math.toRadians(degrees);
Matrix4f rotation = new Matrix4f().rotateY(radians); // Matrix4f rotation = new Matrix4f().rotateY(radians);
//
// 原始点 (1, 0, 0) // // 原始点 (1, 0, 0)
Vector4f point = new Vector4f(1, 0, 0, 1); // Vector4f point = new Vector4f(1, 0, 0, 1);
Vector4f rotated = rotation.transform(point); // Vector4f rotated = rotation.transform(point);
//
assertThat(rotated.x()).isCloseTo(expectedX, within(EPSILON)); // assertThat(rotated.x()).isCloseTo(expectedX, within(EPSILON));
assertThat(rotated.y()).isCloseTo(expectedY, within(EPSILON)); // assertThat(rotated.y()).isCloseTo(expectedY, within(EPSILON));
assertThat(rotated.z()).isCloseTo(expectedZ, within(EPSILON)); // assertThat(rotated.z()).isCloseTo(expectedZ, within(EPSILON));
} // }
//
@Test // @Test
void testRotationComposition() { // void testRotationComposition() {
// 绕X轴旋转90度 // // 绕X轴旋转90度
Matrix4f rotX = new Matrix4f().rotateX((float)Math.PI / 2); // Matrix4f rotX = new Matrix4f().rotateX((float)Math.PI / 2);
// 绕Z轴旋转90度 // // 绕Z轴旋转90度
Matrix4f rotZ = new Matrix4f().rotateZ((float)Math.PI / 2); // Matrix4f rotZ = new Matrix4f().rotateZ((float)Math.PI / 2);
//
// 组合旋转先X后Z // // 组合旋转先X后Z
Matrix4f combined = rotZ.mul(rotX); // Matrix4f combined = rotZ.mul(rotX);
//
// (1, 0, 0) // // (1, 0, 0)
Vector4f point = new Vector4f(1, 0, 0, 1); // Vector4f point = new Vector4f(1, 0, 0, 1);
Vector4f transformed = combined.transform(point); // Vector4f transformed = combined.transform(point);
//
// 手动计算验证 // // 手动计算验证
// (1,0,0) 绕X转90度 -> (1,0,0) 实际上X轴旋转不影响X轴上的点 // // (1,0,0) 绕X转90度 -> (1,0,0) 实际上X轴旋转不影响X轴上的点
// 再绕Z转90度 -> (0,1,0) // // 再绕Z转90度 -> (0,1,0)
assertThat(transformed.x()).isCloseTo(0.0f, within(EPSILON)); // assertThat(transformed.x()).isCloseTo(0.0f, within(EPSILON));
assertThat(transformed.y()).isCloseTo(1.0f, within(EPSILON)); // assertThat(transformed.y()).isCloseTo(1.0f, within(EPSILON));
assertThat(transformed.z()).isCloseTo(0.0f, within(EPSILON)); // assertThat(transformed.z()).isCloseTo(0.0f, within(EPSILON));
} // }
//
// ==================== 缩放测试 ==================== // // ==================== 缩放测试 ====================
//
@Test // @Test
void testNonUniformScaling() { // void testNonUniformScaling() {
Matrix4f scale = new Matrix4f().scale(2, 0.5f, 3); // Matrix4f scale = new Matrix4f().scale(2, 0.5f, 3);
//
Vector4f point = new Vector4f(1, 2, 3, 1); // Vector4f point = new Vector4f(1, 2, 3, 1);
Vector4f scaled = scale.transform(point); // Vector4f scaled = scale.transform(point);
//
assertThat(scaled.x()).isCloseTo(2.0f, within(EPSILON)); // 1 * 2 // assertThat(scaled.x()).isCloseTo(2.0f, within(EPSILON)); // 1 * 2
assertThat(scaled.y()).isCloseTo(1.0f, within(EPSILON)); // 2 * 0.5 // assertThat(scaled.y()).isCloseTo(1.0f, within(EPSILON)); // 2 * 0.5
assertThat(scaled.z()).isCloseTo(9.0f, within(EPSILON)); // 3 * 3 // assertThat(scaled.z()).isCloseTo(9.0f, within(EPSILON)); // 3 * 3
} // }
//
// ==================== 平移测试 ==================== // // ==================== 平移测试 ====================
//
@Test // @Test
void testTranslation() { // void testTranslation() {
Matrix4f translation = new Matrix4f().translate(5, -3, 2.5f); // Matrix4f translation = new Matrix4f().translate(5, -3, 2.5f);
//
Vector4f point = new Vector4f(1, 2, 3, 1); // Vector4f point = new Vector4f(1, 2, 3, 1);
Vector4f translated = translation.transform(point); // Vector4f translated = translation.transform(point);
//
assertThat(translated.x()).isCloseTo(6.0f, within(EPSILON)); // 1 + 5 // assertThat(translated.x()).isCloseTo(6.0f, within(EPSILON)); // 1 + 5
assertThat(translated.y()).isCloseTo(-1.0f, within(EPSILON)); // 2 - 3 // assertThat(translated.y()).isCloseTo(-1.0f, within(EPSILON)); // 2 - 3
assertThat(translated.z()).isCloseTo(5.5f, within(EPSILON)); // 3 + 2.5 // assertThat(translated.z()).isCloseTo(5.5f, within(EPSILON)); // 3 + 2.5
} // }
//
// ==================== 变换顺序测试 ==================== // // ==================== 变换顺序测试 ====================
//
@Test // @Test
void testTransformOrderMatters() { // void testTransformOrderMatters() {
Vector4f point = new Vector4f(1, 0, 0, 1); // Vector4f point = new Vector4f(1, 0, 0, 1);
//
// 顺序1先平移后旋转 // // 顺序1先平移后旋转
Matrix4f translateThenRotate = new Matrix4f() // Matrix4f translateThenRotate = new Matrix4f()
.rotateY((float)Math.PI / 2) // 旋转90度 // .rotateY((float)Math.PI / 2) // 旋转90度
.translate(5, 0, 0); // 平移 // .translate(5, 0, 0); // 平移
//
// 顺序2先旋转后平移 // // 顺序2先旋转后平移
Matrix4f rotateThenTranslate = new Matrix4f() // Matrix4f rotateThenTranslate = new Matrix4f()
.translate(5, 0, 0) // .translate(5, 0, 0)
.rotateY((float)Math.PI / 2); // .rotateY((float)Math.PI / 2);
//
Vector4f result1 = translateThenRotate.transform(point); // Vector4f result1 = translateThenRotate.transform(point);
Vector4f result2 = rotateThenTranslate.transform(point); // Vector4f result2 = rotateThenTranslate.transform(point);
//
// 两个结果应该不同 // // 两个结果应该不同
assertThat(result1.x()).isNotCloseTo(result2.x(), within(EPSILON)); // assertThat(result1.x()).isNotCloseTo(result2.x(), within(EPSILON));
assertThat(result1.z()).isNotCloseTo(result2.z(), within(EPSILON)); // assertThat(result1.z()).isNotCloseTo(result2.z(), within(EPSILON));
//
// 验证具体值 // // 验证具体值
// 顺序1(1,0,0) 先平移->(6,0,0) 再旋转90度->(0,0,-6) // // 顺序1(1,0,0) 先平移->(6,0,0) 再旋转90度->(0,0,-6)
assertThat(result1.x()).isCloseTo(0.0f, within(EPSILON)); // assertThat(result1.x()).isCloseTo(0.0f, within(EPSILON));
assertThat(result1.z()).isCloseTo(-6.0f, within(EPSILON)); // assertThat(result1.z()).isCloseTo(-6.0f, within(EPSILON));
//
// 顺序2(1,0,0) 先旋转90度->(0,0,-1) 再平移->(5,0,-1) // // 顺序2(1,0,0) 先旋转90度->(0,0,-1) 再平移->(5,0,-1)
assertThat(result2.x()).isCloseTo(5.0f, within(EPSILON)); // assertThat(result2.x()).isCloseTo(5.0f, within(EPSILON));
assertThat(result2.z()).isCloseTo(-1.0f, within(EPSILON)); // assertThat(result2.z()).isCloseTo(-1.0f, within(EPSILON));
} // }
//
// ==================== 相机视图变换测试 ==================== // // ==================== 相机视图变换测试 ====================
//
@Test // @Test
void testViewTransform() { // void testViewTransform() {
// 相机在 (0,0,5)看向原点 (0,0,0)上方向为Y轴 // // 相机在 (0,0,5)看向原点 (0,0,0)上方向为Y轴
Matrix4f viewMatrix = new Matrix4f() // Matrix4f viewMatrix = new Matrix4f()
.lookAt(0, 0, 5, // 相机位置 // .lookAt(0, 0, 5, // 相机位置
0, 0, 0, // 看向的点 // 0, 0, 0, // 看向的点
0, 1, 0); // 上方向 // 0, 1, 0); // 上方向
//
// 世界坐标的原点 (0,0,0) 在相机空间中应该在 (0,0,-5) // // 世界坐标的原点 (0,0,0) 在相机空间中应该在 (0,0,-5)
Vector4f worldPoint = new Vector4f(0, 0, 0, 1); // Vector4f worldPoint = new Vector4f(0, 0, 0, 1);
Vector4f viewPoint = viewMatrix.transform(worldPoint); // Vector4f viewPoint = viewMatrix.transform(worldPoint);
//
assertThat(viewPoint.x()).isCloseTo(0.0f, within(EPSILON)); // assertThat(viewPoint.x()).isCloseTo(0.0f, within(EPSILON));
assertThat(viewPoint.y()).isCloseTo(0.0f, within(EPSILON)); // assertThat(viewPoint.y()).isCloseTo(0.0f, within(EPSILON));
assertThat(viewPoint.z()).isCloseTo(-5.0f, within(EPSILON)); // 相机看向-Z方向 // assertThat(viewPoint.z()).isCloseTo(-5.0f, within(EPSILON)); // 相机看向-Z方向
} // }
//
@Test // @Test
void testLookAtMatrixProperties() { // void testLookAtMatrixProperties() {
Vector3f eye = new Vector3f(10, 5, 10); // Vector3f eye = new Vector3f(10, 5, 10);
Vector3f center = new Vector3f(0, 0, 0); // Vector3f center = new Vector3f(0, 0, 0);
Vector3f up = new Vector3f(0, 1, 0); // Vector3f up = new Vector3f(0, 1, 0);
//
Matrix4f view = new Matrix4f().lookAt(eye, center, up); // Matrix4f view = new Matrix4f().lookAt(eye, center, up);
//
// 1. 视图矩阵应该是正交矩阵逆等于转置 // // 1. 视图矩阵应该是正交矩阵逆等于转置
Matrix4f invView = new Matrix4f(view).invert(); // Matrix4f invView = new Matrix4f(view).invert();
Matrix4f transposeView = new Matrix4f(view).transpose(); // Matrix4f transposeView = new Matrix4f(view).transpose();
//
for (int i = 0; i < 4; i++) { // for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) { // for (int j = 0; j < 4; j++) {
assertThat(invView.get(i, j)).isCloseTo(transposeView.get(i, j), within(EPSILON)); // assertThat(invView.get(i, j)).isCloseTo(transposeView.get(i, j), within(EPSILON));
} // }
} // }
//
// 2. 相机位置在视图空间应该是原点 // // 2. 相机位置在视图空间应该是原点
Vector4f eyeHomogeneous = new Vector4f(eye, 1); // Vector4f eyeHomogeneous = new Vector4f(eye, 1);
Vector4f eyeInView = view.transform(eyeHomogeneous); // Vector4f eyeInView = view.transform(eyeHomogeneous);
assertThat(eyeInView.x()).isCloseTo(0.0f, within(EPSILON)); // assertThat(eyeInView.x()).isCloseTo(0.0f, within(EPSILON));
assertThat(eyeInView.y()).isCloseTo(0.0f, within(EPSILON)); // assertThat(eyeInView.y()).isCloseTo(0.0f, within(EPSILON));
assertThat(eyeInView.z()).isCloseTo(0.0f, within(EPSILON)); // assertThat(eyeInView.z()).isCloseTo(0.0f, within(EPSILON));
} // }
//
// ==================== 投影变换测试 ==================== // // ==================== 投影变换测试 ====================
//
@Test // @Test
void testPerspectiveProjection() { // void testPerspectiveProjection() {
float fov = (float)Math.toRadians(60); // float fov = (float)Math.toRadians(60);
float aspect = 16.0f / 9.0f; // float aspect = 16.0f / 9.0f;
float near = 0.1f; // float near = 0.1f;
float far = 100.0f; // float far = 100.0f;
//
Matrix4f projection = new Matrix4f().perspective(fov, aspect, near, far); // Matrix4f projection = new Matrix4f().perspective(fov, aspect, near, far);
//
// 测试近平面上的点 (0,0,-near) 应该映射到 NDC z = -1 // // 测试近平面上的点 (0,0,-near) 应该映射到 NDC z = -1
Vector4f nearPoint = new Vector4f(0, 0, -near, 1); // Vector4f nearPoint = new Vector4f(0, 0, -near, 1);
Vector4f projected = projection.transform(nearPoint); // Vector4f projected = projection.transform(nearPoint);
projected.div(projected.w); // 透视除法 // projected.div(projected.w); // 透视除法
//
assertThat(projected.z()).isCloseTo(-1.0f, within(EPSILON)); // assertThat(projected.z()).isCloseTo(-1.0f, within(EPSILON));
//
// 测试远平面上的点 (0,0,-far) 应该映射到 NDC z = 1 // // 测试远平面上的点 (0,0,-far) 应该映射到 NDC z = 1
Vector4f farPoint = new Vector4f(0, 0, -far, 1); // Vector4f farPoint = new Vector4f(0, 0, -far, 1);
projected = projection.transform(farPoint); // projected = projection.transform(farPoint);
projected.div(projected.w); // projected.div(projected.w);
//
assertThat(projected.z()).isCloseTo(1.0f, within(EPSILON)); // assertThat(projected.z()).isCloseTo(1.0f, within(EPSILON));
} // }
//
@Test // @Test
void testOrthographicProjection() { // void testOrthographicProjection() {
float left = -10, right = 10; // float left = -10, right = 10;
float bottom = -5, top = 5; // float bottom = -5, top = 5;
float near = 0.1f, far = 100.0f; // float near = 0.1f, far = 100.0f;
//
Matrix4f ortho = new Matrix4f().ortho(left, right, bottom, top, near, far); // Matrix4f ortho = new Matrix4f().ortho(left, right, bottom, top, near, far);
//
// 测试边界映射 // // 测试边界映射
Vector4f leftBottomNear = new Vector4f(left, bottom, -near, 1); // Vector4f leftBottomNear = new Vector4f(left, bottom, -near, 1);
Vector4f projected = ortho.transform(leftBottomNear); // Vector4f projected = ortho.transform(leftBottomNear);
//
// 正交投影没有透视除法直接映射到NDC [-1,1] // // 正交投影没有透视除法直接映射到NDC [-1,1]
assertThat(projected.x()).isCloseTo(-1.0f, within(EPSILON)); // assertThat(projected.x()).isCloseTo(-1.0f, within(EPSILON));
assertThat(projected.y()).isCloseTo(-1.0f, within(EPSILON)); // assertThat(projected.y()).isCloseTo(-1.0f, within(EPSILON));
assertThat(projected.z()).isCloseTo(-1.0f, within(EPSILON)); // assertThat(projected.z()).isCloseTo(-1.0f, within(EPSILON));
} // }
//
// ==================== MVP变换链测试 ==================== // // ==================== MVP变换链测试 ====================
//
@Test // @Test
void testMVPChain() { // void testMVPChain() {
// 模型变换 // // 模型变换
Matrix4f model = new Matrix4f() // Matrix4f model = new Matrix4f()
.translate(5, 0, 0) // .translate(5, 0, 0)
.rotateY((float)Math.PI / 4); // .rotateY((float)Math.PI / 4);
//
// 视图变换相机 // // 视图变换相机
Matrix4f view = new Matrix4f() // Matrix4f view = new Matrix4f()
.lookAt(0, 2, 10, 0, 0, 0, 0, 1, 0); // .lookAt(0, 2, 10, 0, 0, 0, 0, 1, 0);
//
// 投影变换 // // 投影变换
Matrix4f projection = new Matrix4f() // Matrix4f projection = new Matrix4f()
.perspective((float)Math.toRadians(60), 16.0f/9.0f, 0.1f, 100.0f); // .perspective((float)Math.toRadians(60), 16.0f/9.0f, 0.1f, 100.0f);
//
// 完整的MVP矩阵 // // 完整的MVP矩阵
Matrix4f mvp = projection.mul(view.mul(model)); // Matrix4f mvp = projection.mul(view.mul(model));
//
// 测试局部坐标 (0,0,0) // // 测试局部坐标 (0,0,0)
Vector4f localPoint = new Vector4f(0, 0, 0, 1); // Vector4f localPoint = new Vector4f(0, 0, 0, 1);
Vector4f clipPoint = mvp.transform(localPoint); // Vector4f clipPoint = mvp.transform(localPoint);
//
// 执行透视除法 // // 执行透视除法
clipPoint.div(clipPoint.w()); // clipPoint.div(clipPoint.w());
//
// 结果应该在NDC范围内 [-1, 1] // // 结果应该在NDC范围内 [-1, 1]
assertThat(clipPoint.x()).isBetween(-1.0f - EPSILON, 1.0f + EPSILON); // assertThat(clipPoint.x()).isBetween(-1.0f - EPSILON, 1.0f + EPSILON);
assertThat(clipPoint.y()).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); // assertThat(clipPoint.z()).isBetween(-1.0f - EPSILON, 1.0f + EPSILON);
} // }
//
// ==================== 法线变换测试 ==================== // // ==================== 法线变换测试 ====================
//
@Test // @Test
void testNormalTransformation() { // void testNormalTransformation() {
// 创建一个包含非均匀缩放的变换 // // 创建一个包含非均匀缩放的变换
Matrix4f model = new Matrix4f() // Matrix4f model = new Matrix4f()
.scale(2, 1, 1) // 只在X轴缩放 // .scale(2, 1, 1) // 只在X轴缩放
.rotateY((float)Math.PI / 6); // .rotateY((float)Math.PI / 6);
//
// 原始法线 (1,0,0) // // 原始法线 (1,0,0)
Vector3f normal = new Vector3f(1, 0, 0); // Vector3f normal = new Vector3f(1, 0, 0);
//
// 错误的做法直接使用模型矩阵的3x3部分 // // 错误的做法直接使用模型矩阵的3x3部分
Matrix3f wrongTransform = new Matrix3f(model); // Matrix3f wrongTransform = new Matrix3f(model);
Vector3f wrongResult = wrongTransform.transform(normal); // Vector3f wrongResult = wrongTransform.transform(normal);
//
// 正确的做法使用逆转置矩阵 // // 正确的做法使用逆转置矩阵
Matrix3f normalMatrix = new Matrix3f(model).invert().transpose(); // Matrix3f normalMatrix = new Matrix3f(model).invert().transpose();
Vector3f correctResult = normalMatrix.transform(normal).normalize(); // Vector3f correctResult = normalMatrix.transform(normal).normalize();
//
// 两个结果应该不同因为缩放不是均匀的 // // 两个结果应该不同因为缩放不是均匀的
assertThat(wrongResult.x()).isNotCloseTo(correctResult.x(), within(EPSILON)); // assertThat(wrongResult.x()).isNotCloseTo(correctResult.x(), within(EPSILON));
assertThat(wrongResult.y()).isNotCloseTo(correctResult.y(), within(EPSILON)); // assertThat(wrongResult.y()).isNotCloseTo(correctResult.y(), within(EPSILON));
assertThat(wrongResult.z()).isNotCloseTo(correctResult.z(), within(EPSILON)); // assertThat(wrongResult.z()).isNotCloseTo(correctResult.z(), within(EPSILON));
//
// 验证法线变换后仍保持垂直性简化测试 // // 验证法线变换后仍保持垂直性简化测试
Vector3f vertex = new Vector3f(1, 0, 0); // Vector3f vertex = new Vector3f(1, 0, 0);
Vector3f transformedVertex = new Vector3f(vertex).mul(normalMatrix); // Vector3f transformedVertex = new Vector3f(vertex).mul(normalMatrix);
//
// 法线和顶点变换后的点积应该接近0垂直 // // 法线和顶点变换后的点积应该接近0垂直
float dot = correctResult.dot(transformedVertex); // float dot = correctResult.dot(transformedVertex);
assertThat(Math.abs(dot)).isCloseTo(0.0f, within(0.1f)); // assertThat(Math.abs(dot)).isCloseTo(0.0f, within(0.1f));
} // }
//
// ==================== 四元数旋转测试 ==================== // // ==================== 四元数旋转测试 ====================
//
@Test // @Test
void testQuaternionRotation() { // void testQuaternionRotation() {
// 创建绕Y轴旋转90度的四元数 // // 创建绕Y轴旋转90度的四元数
Quaternionf quat = new Quaternionf() // Quaternionf quat = new Quaternionf()
.rotateY((float)Math.PI / 2); // .rotateY((float)Math.PI / 2);
//
// 转换为矩阵 // // 转换为矩阵
Matrix4f matrixFromQuat = new Matrix4f().rotation(quat); // Matrix4f matrixFromQuat = new Matrix4f().rotation(quat);
//
// 直接创建矩阵 // // 直接创建矩阵
Matrix4f matrixDirect = new Matrix4f().rotateY((float)Math.PI / 2); // Matrix4f matrixDirect = new Matrix4f().rotateY((float)Math.PI / 2);
//
// 两个矩阵应该相同 // // 两个矩阵应该相同
for (int i = 0; i < 4; i++) { // for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) { // for (int j = 0; j < 4; j++) {
assertThat(matrixFromQuat.get(i, j)) // assertThat(matrixFromQuat.get(i, j))
.isCloseTo(matrixDirect.get(i, j), within(EPSILON)); // .isCloseTo(matrixDirect.get(i, j), within(EPSILON));
} // }
} // }
//
// 测试四元数旋转点 // // 测试四元数旋转点
Vector3f point = new Vector3f(1, 0, 0); // Vector3f point = new Vector3f(1, 0, 0);
Vector3f rotatedByQuat = quat.transform(point); // Vector3f rotatedByQuat = quat.transform(point);
Vector3f rotatedByMatrix = matrixDirect.transformPosition(point, new Vector3f()); // Vector3f rotatedByMatrix = matrixDirect.transformPosition(point, new Vector3f());
//
assertThat(rotatedByQuat.x()).isCloseTo(rotatedByMatrix.x(), within(EPSILON)); // assertThat(rotatedByQuat.x()).isCloseTo(rotatedByMatrix.x(), within(EPSILON));
assertThat(rotatedByQuat.y()).isCloseTo(rotatedByMatrix.y(), within(EPSILON)); // assertThat(rotatedByQuat.y()).isCloseTo(rotatedByMatrix.y(), within(EPSILON));
assertThat(rotatedByQuat.z()).isCloseTo(rotatedByMatrix.z(), within(EPSILON)); // assertThat(rotatedByQuat.z()).isCloseTo(rotatedByMatrix.z(), within(EPSILON));
} // }
//
// ==================== 性能测试 ==================== // // ==================== 性能测试 ====================
//
@RepeatedTest(3) // @RepeatedTest(3)
@DisplayName("变换矩阵性能测试") // @DisplayName("变换矩阵性能测试")
void testTransformPerformance() { // void testTransformPerformance() {
int iterations = 100000; // int iterations = 100000;
Matrix4f transform = new Matrix4f() // Matrix4f transform = new Matrix4f()
.translate(1, 2, 3) // .translate(1, 2, 3)
.rotateXYZ(0.1f, 0.2f, 0.3f) // .rotateXYZ(0.1f, 0.2f, 0.3f)
.scale(1.5f); // .scale(1.5f);
//
Vector4f point = new Vector4f(1, 2, 3, 1); // Vector4f point = new Vector4f(1, 2, 3, 1);
//
long startTime = System.nanoTime(); // long startTime = System.nanoTime();
//
for (int i = 0; i < iterations; i++) { // for (int i = 0; i < iterations; i++) {
Vector4f result = transform.transform(point); // Vector4f result = transform.transform(point);
// 确保结果被使用防止被优化掉 // // 确保结果被使用防止被优化掉
if (Float.isNaN(result.x())) { // if (Float.isNaN(result.x())) {
Assertions.fail("不应该发生"); // Assertions.fail("不应该发生");
} // }
} // }
//
long endTime = System.nanoTime(); // long endTime = System.nanoTime();
long durationMs = (endTime - startTime) / 1_000_000; // long durationMs = (endTime - startTime) / 1_000_000;
//
System.out.printf("Transform %d points in %d ms%n", iterations, durationMs); // System.out.printf("Transform %d points in %d ms%n", iterations, durationMs);
assertThat(durationMs).isLessThan(1000); // 应该在1秒内完成 // assertThat(durationMs).isLessThan(1000); // 应该在1秒内完成
} // }
//
// ==================== 边缘情况测试 ==================== // // ==================== 边缘情况测试 ====================
//
@Test // @Test
void testZeroScaling() { // void testZeroScaling() {
Matrix4f scale = new Matrix4f().scale(0, 1, 1); // Matrix4f scale = new Matrix4f().scale(0, 1, 1);
Vector4f point = new Vector4f(1, 2, 3, 1); // Vector4f point = new Vector4f(1, 2, 3, 1);
Vector4f result = scale.transform(point); // Vector4f result = scale.transform(point);
//
assertThat(result.x()).isCloseTo(0.0f, within(EPSILON)); // assertThat(result.x()).isCloseTo(0.0f, within(EPSILON));
assertThat(result.y()).isCloseTo(2.0f, within(EPSILON)); // assertThat(result.y()).isCloseTo(2.0f, within(EPSILON));
assertThat(result.z()).isCloseTo(3.0f, within(EPSILON)); // assertThat(result.z()).isCloseTo(3.0f, within(EPSILON));
} // }
//
@Test // @Test
void testNegativeScaling() { // void testNegativeScaling() {
Matrix4f scale = new Matrix4f().scale(-1, 1, 1); // Matrix4f scale = new Matrix4f().scale(-1, 1, 1);
Vector4f point = new Vector4f(1, 2, 3, 1); // Vector4f point = new Vector4f(1, 2, 3, 1);
Vector4f result = scale.transform(point); // Vector4f result = scale.transform(point);
//
assertThat(result.x()).isCloseTo(-1.0f, within(EPSILON)); // assertThat(result.x()).isCloseTo(-1.0f, within(EPSILON));
assertThat(result.y()).isCloseTo(2.0f, within(EPSILON)); // assertThat(result.y()).isCloseTo(2.0f, within(EPSILON));
assertThat(result.z()).isCloseTo(3.0f, within(EPSILON)); // assertThat(result.z()).isCloseTo(3.0f, within(EPSILON));
} // }
//
@Test // @Test
void testIdentityTransform() { // void testIdentityTransform() {
Matrix4f identity = new Matrix4f().identity(); // Matrix4f identity = new Matrix4f().identity();
Vector4f point = new Vector4f(1, 2, 3, 1); // Vector4f point = new Vector4f(1, 2, 3, 1);
Vector4f result = identity.transform(point); // Vector4f result = identity.transform(point);
//
assertThat(result.x()).isCloseTo(point.x(), within(EPSILON)); // assertThat(result.x()).isCloseTo(point.x(), within(EPSILON));
assertThat(result.y()).isCloseTo(point.y(), within(EPSILON)); // assertThat(result.y()).isCloseTo(point.y(), within(EPSILON));
assertThat(result.z()).isCloseTo(point.z(), within(EPSILON)); // assertThat(result.z()).isCloseTo(point.z(), within(EPSILON));
} // }
} //}