更新内容
1. 優化方法
This commit is contained in:
parent
c0a25f9172
commit
b54cd4acea
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
// }
|
||||
//}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user