320 lines
9.7 KiB
C
320 lines
9.7 KiB
C
#undef UFBXT_TEST_GROUP
|
|
#define UFBXT_TEST_GROUP "math"
|
|
|
|
#if UFBXT_IMPL
|
|
|
|
static ufbx_real ufbxt_quat_error(ufbx_quat a, ufbx_quat b)
|
|
{
|
|
double pos = fabs(a.x-b.x) + fabs(a.y-b.y) + fabs(a.z-b.z) + fabs(a.w-b.w);
|
|
double neg = fabs(a.x+b.x) + fabs(a.y+b.y) + fabs(a.z+b.z) + fabs(a.w+b.w);
|
|
return pos < neg ? pos : neg;
|
|
}
|
|
|
|
static uint32_t ufbxt_xorshift32(uint32_t *state)
|
|
{
|
|
/* Algorithm "xor" from p. 4 of Marsaglia, "Xorshift RNGs" */
|
|
uint32_t x = *state;
|
|
x ^= x << 13;
|
|
x ^= x >> 17;
|
|
x ^= x << 5;
|
|
return *state = x;
|
|
}
|
|
|
|
static ufbx_real ufbxt_xorshift32_real(uint32_t *state)
|
|
{
|
|
uint32_t u = ufbxt_xorshift32(state);
|
|
return (ufbx_real)u * (ufbx_real)2.3283064365386962890625e-10;
|
|
}
|
|
|
|
#endif
|
|
|
|
UFBXT_TEST(quat_to_euler_structured)
|
|
#if UFBXT_IMPL
|
|
{
|
|
ufbxt_diff_error err = { 0 };
|
|
|
|
for (int iorder = 0; iorder < 6; iorder++) {
|
|
ufbx_rotation_order order = (ufbx_rotation_order)iorder;
|
|
|
|
for (int x = -360; x <= 360; x += 45)
|
|
for (int y = -360; y <= 360; y += 45)
|
|
for (int z = -360; z <= 360; z += 45) {
|
|
ufbx_vec3 v = { (ufbx_real)x, (ufbx_real)y, (ufbx_real)z };
|
|
|
|
ufbx_quat q = ufbx_euler_to_quat(v, order);
|
|
ufbx_vec3 v2 = ufbx_quat_to_euler(q, order);
|
|
ufbx_quat q2 = ufbx_euler_to_quat(v2, order);
|
|
|
|
ufbxt_assert_close_real(&err, ufbxt_quat_error(q, q2), 0.0f);
|
|
ufbxt_assert_close_real(&err, ufbxt_quat_error(q, q2), 0.0f);
|
|
}
|
|
}
|
|
|
|
ufbxt_logf(".. Absolute diff: avg %.3g, max %.3g (%zu tests)", err.sum / (ufbx_real)err.num, err.max, err.num);
|
|
}
|
|
#endif
|
|
|
|
UFBXT_TEST(quat_to_euler_random)
|
|
#if UFBXT_IMPL
|
|
{
|
|
size_t steps = ufbxt_begin_fuzz() ? 10000000 : 100000;
|
|
ufbxt_diff_error err = { 0 };
|
|
|
|
for (int iorder = 0; iorder < 6; iorder++) {
|
|
ufbx_rotation_order order = (ufbx_rotation_order)iorder;
|
|
|
|
uint32_t state = 1;
|
|
|
|
for (size_t i = 0; i < steps; i++) {
|
|
if (g_fuzz && ufbxt_fuzz_should_skip((int)i >> 8)) continue;
|
|
|
|
ufbx_quat q;
|
|
q.x = ufbxt_xorshift32_real(&state) * 2.0f - 1.0f;
|
|
q.y = ufbxt_xorshift32_real(&state) * 2.0f - 1.0f;
|
|
q.z = ufbxt_xorshift32_real(&state) * 2.0f - 1.0f;
|
|
q.w = ufbxt_xorshift32_real(&state) * 2.0f - 1.0f;
|
|
ufbx_real qm = (ufbx_real)sqrt(q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w);
|
|
q.x /= qm;
|
|
q.y /= qm;
|
|
q.z /= qm;
|
|
q.w /= qm;
|
|
|
|
ufbx_vec3 v = ufbx_quat_to_euler(q, order);
|
|
ufbx_quat q2 = ufbx_euler_to_quat(v, order);
|
|
|
|
ufbxt_assert_close_real(&err, ufbxt_quat_error(q, q2), 0.0f);
|
|
}
|
|
}
|
|
|
|
ufbxt_logf(".. Absolute diff: avg %.3g, max %.3g (%zu tests)", err.sum / (ufbx_real)err.num, err.max, err.num);
|
|
}
|
|
#endif
|
|
|
|
UFBXT_TEST(matrix_to_transform_structured)
|
|
#if UFBXT_IMPL
|
|
{
|
|
ufbxt_diff_error err = { 0 };
|
|
|
|
for (int sx = -2; sx <= 2; sx++)
|
|
for (int sy = -2; sy <= 2; sy++)
|
|
for (int sz = -2; sz <= 2; sz++)
|
|
for (int rx = -2; rx <= 2; rx++)
|
|
for (int ry = -2; ry <= 2; ry++)
|
|
for (int rz = -2; rz <= 2; rz++)
|
|
for (int rw = -2; rw <= 2; rw++)
|
|
{
|
|
// TODO: Support single axis squish?
|
|
if (sx == 0 || sy == 0 || sz == 0) continue;
|
|
if (rx == 0 && ry == 0 && rz == 0 && rw == 0) continue;
|
|
|
|
ufbx_transform t;
|
|
|
|
ufbx_quat q = { (ufbx_real)rx / 3.0f, (ufbx_real)ry / 3.0f, (ufbx_real)rz / 3.0f, (ufbx_real)rw / 3.0f };
|
|
ufbx_real qm = (ufbx_real)sqrt(q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w);
|
|
t.rotation.x = q.x / qm;
|
|
t.rotation.y = q.y / qm;
|
|
t.rotation.z = q.z / qm;
|
|
t.rotation.w = q.w / qm;
|
|
|
|
t.translation.x = 1.0f;
|
|
t.translation.y = 2.0f;
|
|
t.translation.z = 3.0f;
|
|
t.scale.x = (ufbx_real)sx / 2.0f;
|
|
t.scale.y = (ufbx_real)sy / 2.0f;
|
|
t.scale.z = (ufbx_real)sz / 2.0f;
|
|
|
|
ufbx_matrix m = ufbx_transform_to_matrix(&t);
|
|
ufbx_transform t2 = ufbx_matrix_to_transform(&m);
|
|
|
|
if (sx < 0 || sy < 0 || sz < 0) {
|
|
// Flipped signs cannot be uniquely recovered, check that the transforms are identical
|
|
ufbx_matrix m2 = ufbx_transform_to_matrix(&t2);
|
|
ufbxt_assert_close_matrix(&err, m, m2);
|
|
} else {
|
|
ufbxt_assert_close_vec3(&err, t.translation, t2.translation);
|
|
ufbxt_assert_close_vec3(&err, t.scale, t2.scale);
|
|
ufbxt_assert_close_real(&err, ufbxt_quat_error(t.rotation, t2.rotation), 0.0f);
|
|
}
|
|
}
|
|
|
|
ufbxt_logf(".. Absolute diff: avg %.3g, max %.3g (%zu tests)", err.sum / (ufbx_real)err.num, err.max, err.num);
|
|
}
|
|
#endif
|
|
|
|
UFBXT_TEST(matrix_to_transform_random)
|
|
#if UFBXT_IMPL
|
|
{
|
|
ufbxt_diff_error err = { 0 };
|
|
|
|
uint32_t state = 1;
|
|
size_t steps = ufbxt_begin_fuzz() ? 1000000 : 100000;
|
|
|
|
for (size_t i = 0; i < steps; i++) {
|
|
if (g_fuzz && ufbxt_fuzz_should_skip((int)i >> 4)) continue;
|
|
|
|
ufbx_transform t;
|
|
|
|
ufbx_quat q;
|
|
q.x = ufbxt_xorshift32_real(&state) * 2.0f - 1.0f;
|
|
q.y = ufbxt_xorshift32_real(&state) * 2.0f - 1.0f;
|
|
q.z = ufbxt_xorshift32_real(&state) * 2.0f - 1.0f;
|
|
q.w = ufbxt_xorshift32_real(&state) * 2.0f - 1.0f;
|
|
ufbx_real qm = (ufbx_real)sqrt(q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w);
|
|
t.rotation.x = q.x / qm;
|
|
t.rotation.y = q.y / qm;
|
|
t.rotation.z = q.z / qm;
|
|
t.rotation.w = q.w / qm;
|
|
|
|
t.translation.x = ufbxt_xorshift32_real(&state) * 20.0f - 10.0f;
|
|
t.translation.y = ufbxt_xorshift32_real(&state) * 20.0f - 10.0f;
|
|
t.translation.z = ufbxt_xorshift32_real(&state) * 20.0f - 10.0f;
|
|
t.scale.x = ufbxt_xorshift32_real(&state) * 10.0f + 0.01f;
|
|
t.scale.y = ufbxt_xorshift32_real(&state) * 10.0f + 0.01f;
|
|
t.scale.z = ufbxt_xorshift32_real(&state) * 10.0f + 0.01f;
|
|
|
|
uint32_t flip = ufbxt_xorshift32(&state);
|
|
|
|
// Prevent most of the inputs being flips
|
|
if (flip & 8) flip = 0;
|
|
|
|
if (flip & 1) t.scale.x *= -1.0f;
|
|
if (flip & 2) t.scale.y *= -1.0f;
|
|
if (flip & 4) t.scale.z *= -1.0f;
|
|
|
|
ufbx_matrix m = ufbx_transform_to_matrix(&t);
|
|
ufbx_transform t2 = ufbx_matrix_to_transform(&m);
|
|
|
|
if (flip) {
|
|
// Flipped signs cannot be uniquely recovered, check that the transforms are identical
|
|
ufbx_matrix m2 = ufbx_transform_to_matrix(&t2);
|
|
ufbxt_assert_close_matrix(&err, m, m2);
|
|
} else {
|
|
ufbxt_assert_close_vec3(&err, t.translation, t2.translation);
|
|
ufbxt_assert_close_vec3(&err, t.scale, t2.scale);
|
|
ufbxt_assert_close_real(&err, ufbxt_quat_error(t.rotation, t2.rotation), 0.0f);
|
|
}
|
|
}
|
|
|
|
ufbxt_logf(".. Absolute diff: avg %.3g, max %.3g (%zu tests)", err.sum / (ufbx_real)err.num, err.max, err.num);
|
|
}
|
|
#endif
|
|
|
|
UFBXT_TEST(matrix_inverse_simple)
|
|
#if UFBXT_IMPL
|
|
{
|
|
ufbxt_diff_error err = { 0 };
|
|
|
|
{
|
|
ufbx_matrix m = { 0 };
|
|
m.m00 = 2.0f;
|
|
m.m11 = 0.5f;
|
|
m.m22 = 0.25f;
|
|
m.m03 = 1.0f;
|
|
m.m13 = 2.0f;
|
|
m.m23 = 3.0f;
|
|
|
|
ufbx_matrix im = ufbx_matrix_invert(&m);
|
|
ufbxt_assert_close_real(&err, im.m00, 0.5f);
|
|
ufbxt_assert_close_real(&err, im.m10, 0.0f);
|
|
ufbxt_assert_close_real(&err, im.m20, 0.0f);
|
|
ufbxt_assert_close_real(&err, im.m01, 0.0f);
|
|
ufbxt_assert_close_real(&err, im.m11, 2.0f);
|
|
ufbxt_assert_close_real(&err, im.m21, 0.0f);
|
|
ufbxt_assert_close_real(&err, im.m02, 0.0f);
|
|
ufbxt_assert_close_real(&err, im.m12, 0.0f);
|
|
ufbxt_assert_close_real(&err, im.m22, 4.0f);
|
|
ufbxt_assert_close_real(&err, im.m03, -0.5f);
|
|
ufbxt_assert_close_real(&err, im.m13, -4.0f);
|
|
ufbxt_assert_close_real(&err, im.m23, -12.0f);
|
|
}
|
|
|
|
{
|
|
ufbx_matrix m = { 0 };
|
|
m.m00 = 1.0f;
|
|
m.m12 = -1.0f;
|
|
m.m21 = 1.0f;
|
|
m.m13 = 1.0f;
|
|
m.m23 = 2.0f;
|
|
|
|
ufbx_matrix im = ufbx_matrix_invert(&m);
|
|
ufbxt_assert_close_real(&err, im.m00, 1.0f);
|
|
ufbxt_assert_close_real(&err, im.m10, 0.0f);
|
|
ufbxt_assert_close_real(&err, im.m20, 0.0f);
|
|
ufbxt_assert_close_real(&err, im.m01, 0.0f);
|
|
ufbxt_assert_close_real(&err, im.m11, 0.0f);
|
|
ufbxt_assert_close_real(&err, im.m21, -1.0f);
|
|
ufbxt_assert_close_real(&err, im.m02, 0.0f);
|
|
ufbxt_assert_close_real(&err, im.m12, 1.0f);
|
|
ufbxt_assert_close_real(&err, im.m22, 0.0f);
|
|
ufbxt_assert_close_real(&err, im.m03, 0.0f);
|
|
ufbxt_assert_close_real(&err, im.m13, -2.0f);
|
|
ufbxt_assert_close_real(&err, im.m23, 1.0f);
|
|
}
|
|
|
|
ufbxt_logf(".. Absolute diff: avg %.3g, max %.3g (%zu tests)", err.sum / (ufbx_real)err.num, err.max, err.num);
|
|
}
|
|
#endif
|
|
|
|
UFBXT_TEST(matrix_inverse_random)
|
|
#if UFBXT_IMPL
|
|
{
|
|
ufbxt_diff_error err = { 0 };
|
|
|
|
size_t steps = ufbxt_begin_fuzz() ? 1000000 : 10000;
|
|
|
|
uint32_t state = 1;
|
|
|
|
for (size_t i = 0; i < steps; i++) {
|
|
if (g_fuzz && ufbxt_fuzz_should_skip((int)i >> 4)) continue;
|
|
|
|
ufbx_transform t;
|
|
|
|
ufbx_quat q;
|
|
q.x = ufbxt_xorshift32_real(&state) * 2.0f - 1.0f;
|
|
q.y = ufbxt_xorshift32_real(&state) * 2.0f - 1.0f;
|
|
q.z = ufbxt_xorshift32_real(&state) * 2.0f - 1.0f;
|
|
q.w = ufbxt_xorshift32_real(&state) * 2.0f - 1.0f;
|
|
ufbx_real qm = (ufbx_real)sqrt(q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w);
|
|
t.rotation.x = q.x / qm;
|
|
t.rotation.y = q.y / qm;
|
|
t.rotation.z = q.z / qm;
|
|
t.rotation.w = q.w / qm;
|
|
|
|
t.translation.x = ufbxt_xorshift32_real(&state) * 20.0f - 10.0f;
|
|
t.translation.y = ufbxt_xorshift32_real(&state) * 20.0f - 10.0f;
|
|
t.translation.z = ufbxt_xorshift32_real(&state) * 20.0f - 10.0f;
|
|
t.scale.x = ufbxt_xorshift32_real(&state) * 10.0f + 0.1f;
|
|
t.scale.y = ufbxt_xorshift32_real(&state) * 10.0f + 0.1f;
|
|
t.scale.z = ufbxt_xorshift32_real(&state) * 10.0f + 0.1f;
|
|
|
|
uint32_t flip = ufbxt_xorshift32(&state);
|
|
|
|
// Prevent most of the inputs being flips
|
|
if (flip & 8) flip = 0;
|
|
|
|
if (flip & 1) t.scale.x *= -1.0f;
|
|
if (flip & 2) t.scale.y *= -1.0f;
|
|
if (flip & 4) t.scale.z *= -1.0f;
|
|
|
|
ufbx_matrix m = ufbx_transform_to_matrix(&t);
|
|
ufbx_matrix im = ufbx_matrix_invert(&m);
|
|
ufbx_matrix identity = ufbx_matrix_mul(&m, &im);
|
|
|
|
ufbxt_assert_close_real(&err, identity.m00, 1.0f);
|
|
ufbxt_assert_close_real(&err, identity.m10, 0.0f);
|
|
ufbxt_assert_close_real(&err, identity.m20, 0.0f);
|
|
ufbxt_assert_close_real(&err, identity.m01, 0.0f);
|
|
ufbxt_assert_close_real(&err, identity.m11, 1.0f);
|
|
ufbxt_assert_close_real(&err, identity.m21, 0.0f);
|
|
ufbxt_assert_close_real(&err, identity.m02, 0.0f);
|
|
ufbxt_assert_close_real(&err, identity.m12, 0.0f);
|
|
ufbxt_assert_close_real(&err, identity.m22, 1.0f);
|
|
ufbxt_assert_close_real(&err, identity.m03, 0.0f);
|
|
ufbxt_assert_close_real(&err, identity.m13, 0.0f);
|
|
ufbxt_assert_close_real(&err, identity.m23, 0.0f);
|
|
}
|
|
|
|
ufbxt_logf(".. Absolute diff: avg %.3g, max %.3g (%zu tests)", err.sum / (ufbx_real)err.num, err.max, err.num);
|
|
}
|
|
#endif
|