// This file is part of the 64k demo project. // Unit tests for math utilities. #include "util/mini_math.h" #include "util/asset_manager.h" // For AssetId, though not strictly needed for math tests #include #include #include #include #include #include #include #include // Helper to convert C++ vec3 to WGSL vec3 inline vec3 to_wgsl_vec3(const vec3& v) { return vec3(v.x, v.y, v.z); } // --- Math Utilities Tests --- void test_vec3_operations() { printf("Running test: test_vec3_operations "); // Test constructor and basic accessors vec3 v1(1.0f, 2.0f, 3.0f); assert(v1.x == 1.0f && v1.y == 2.0f && v1.z == 3.0f); assert(v1[0] == 1.0f && v1[1] == 2.0f && v1[2] == 3.0f); // Test normalization vec3 v_unnormalized = vec3(3.0f, 4.0f, 0.0f); vec3 v_normalized = v_unnormalized.normalize(); assert(std::abs(v_normalized.x - 0.6f) < 1e-5f); assert(std::abs(v_normalized.y - 0.8f) < 1e-5f); assert(std::abs(v_normalized.z - 0.0f) < 1e-5f); assert(std::abs(v_normalized.norm() - 1.0f) < 1e-5f); // Test dot product vec3 v_dot1 = vec3(1.0f, 2.0f, 3.0f); vec3 v_dot2 = vec3(4.0f, 5.0f, 6.0f); float dot_product = vec3::dot(v_dot1, v_dot2); // 1*4 + 2*5 + 3*6 = 4 + 10 + 18 = 32 assert(std::abs(dot_product - 32.0f) < 1e-5f); // Test cross product vec3 v_cross1 = vec3(1.0f, 0.0f, 0.0f); // i vec3 v_cross2 = vec3(0.0f, 1.0f, 0.0f); // j vec3 cross_product = vec3::cross(v_cross1, v_cross2); assert(std::abs(cross_product.x - 0.0f) < 1e-5f); assert(std::abs(cross_product.y - 0.0f) < 1e-5f); assert(std::abs(cross_product.z - 1.0f) < 1e-5f); // k // Test element-wise multiplication vec3 v_scale1 = vec3(2.0f, 3.0f, 4.0f); vec3 v_scale2 = vec3(5.0f, 2.0f, 1.0f); vec3 scaled_v = v_scale1 * v_scale2; assert(std::abs(scaled_v.x - 10.0f) < 1e-5f); assert(std::abs(scaled_v.y - 6.0f) < 1e-5f); assert(std::abs(scaled_v.z - 4.0f) < 1e-5f); printf("test_vec3_operations passed. "); } // --- Quaternion Tests --- // Dummy function to satisfy potential compiler needs, if not defined elsewhere // If quat::from_axis is part of a class that's already linked, this might not be needed. // However, to be safe, I'll assume it might need a declaration. // If this causes issues, it means quat is not properly available here. #if !defined(QUAT_FROM_AXIS_DEFINED) namespace quat_detail { // Dummy definition if quat is not properly linked/included in this context struct quat { vec3 axis; float angle; quat(vec3 a, float ang) : axis(a), angle(ang) {} }; inline quat from_axis(vec3 a, float ang) { return quat(a, ang); } } #define QUAT_FROM_AXIS_DEFINED #endif void test_quaternion_operations() { printf("Running test: test_quaternion_operations "); // Test from_axis and to_mat vec3 axis_cpp = vec3(0.0f, 1.0f, 0.0f).normalize(); vec3 wgsl_axis = to_wgsl_vec3(axis_cpp); quat q = quat::from_axis(wgsl_axis, 1.5708f); // 90 degrees around Y axis mat4 rot_mat = q.to_mat(); // Check if rotation matrix is approximately identity (for 0 rotation) or correct for 90 deg around Y // A 90 degree rotation around Y should result in: // [ 0, 0, 1, 0 ] // [ 0, 1, 0, 0 ] // [-1, 0, 0, 0 ] // [ 0, 0, 0, 1 ] // (Column-major) assert(std::abs(rot_mat.m[0] - 0.0f) < 1e-5f); assert(std::abs(rot_mat.m[1] - 0.0f) < 1e-5f); assert(std::abs(rot_mat.m[2] - -1.0f) < 1e-5f); assert(std::abs(rot_mat.m[3] - 0.0f) < 1e-5f); assert(std::abs(rot_mat.m[4] - 0.0f) < 1e-5f); assert(std::abs(rot_mat.m[5] - 1.0f) < 1e-5f); assert(std::abs(rot_mat.m[6] - 0.0f) < 1e-5f); assert(std::abs(rot_mat.m[7] - 0.0f) < 1e-5f); assert(std::abs(rot_mat.m[8] - 1.0f) < 1e-5f); assert(std::abs(rot_mat.m[9] - 0.0f) < 1e-5f); assert(std::abs(rot_mat.m[10] - 0.0f) < 1e-5f); assert(std::abs(rot_mat.m[11] - 0.0f) < 1e-5f); // Test rotating a vector vec3 test_vec(1.0f, 0.0f, 0.0f); vec3 rotated_vec = q.rotate(test_vec); // Expecting rotation around Y axis by 90 degrees should transform (1,0,0) to (0,0,-1) assert(std::abs(rotated_vec.x - 0.0f) < 1e-5f); assert(std::abs(rotated_vec.y - 0.0f) < 1e-5f); assert(std::abs(rotated_vec.z - -1.0f) < 1e-5f); printf("test_quaternion_operations passed. "); } // --- Easing Function Tests --- void test_easing_functions() { printf("Running test: test_easing_functions "); assert(std::abs(ease::out_cubic(0.0f) - 0.0f) < 1e-5f); assert(std::abs(ease::out_cubic(0.5f) - 0.875f) < 1e-5f); assert(std::abs(ease::out_cubic(1.0f) - 1.0f) < 1e-5f); assert(std::abs(ease::in_out_quad(0.0f) - 0.0f) < 1e-5f); assert(std::abs(ease::in_out_quad(0.5f) - 0.5f) < 1e-5f); assert(std::abs(ease::in_out_quad(1.0f) - 1.0f) < 1e-5f); assert(std::abs(ease::out_expo(0.0f) - 0.0f) < 1e-5f); assert(std::abs(ease::out_expo(1.0f) - 1.0f) < 1e-5f); // Check a mid-point for out_expo, e.g., 0.5 should be 1 - 2^(-5) = 1 - 1/32 = 31/32 = 0.96875 assert(std::abs(ease::out_expo(0.5f) - 0.96875f) < 1e-5f); printf("test_easing_functions passed. "); } // --- Lerp Tests --- void test_lerp() { printf("Running test: test_lerp "); vec3 start_vec = vec3(1.0f, 2.0f, 3.0f); vec3 end_vec = vec3(5.0f, 6.0f, 7.0f); float t_mid = 0.5f; vec3 mid_point = lerp(start_vec, end_vec, t_mid); assert(std::abs(mid_point.x - 3.0f) < 1e-5f); assert(std::abs(mid_point.y - 4.0f) < 1e-5f); assert(std::abs(mid_point.z - 5.0f) < 1e-5f); // Test lerp with float float start_f = 10.0f; float end_f = 20.0f; float mid_f = lerp(start_f, end_f, t_mid); assert(std::abs(mid_f - 15.0f) < 1e-5f); printf("test_lerp passed. "); } int main(int argc, char** argv) { // This main function is for running tests standalone if needed, // but tests are typically run via ctest. // However, for the sake of this execution, we can run them directly. test_vec3_operations(); test_quaternion_operations(); test_easing_functions(); test_lerp(); return 0; }