#undef UFBXT_TEST_GROUP #define UFBXT_TEST_GROUP "triangulate" #if UFBXT_IMPL size_t do_triangulate_test(ufbx_scene *scene) { size_t num_fail = 0; for (size_t mesh_ix = 0; mesh_ix < scene->meshes.count; mesh_ix++) { ufbx_mesh *mesh = scene->meshes.data[mesh_ix]; ufbxt_assert(mesh->instances.count == 1); bool should_be_top_left = mesh->instances.data[0]->name.data[0] == 'A'; ufbxt_assert(mesh->num_faces == 1); ufbx_face face = mesh->faces.data[0]; ufbxt_assert(face.index_begin == 0); ufbxt_assert(face.num_indices == 4); uint32_t tris[6]; bool ok = ufbx_triangulate_face(tris, 6, mesh, face); ufbxt_assert(ok); size_t top_left_ix = 0; ufbx_real best_dot = HUGE_VALF; for (size_t ix = 0; ix < 4; ix++) { ufbx_vec3 v = ufbx_get_vertex_vec3(&mesh->vertex_position, ix); ufbx_real dot = v.x + v.z; if (dot < best_dot) { top_left_ix = ix; best_dot = dot; } } uint32_t top_left_count = 0; for (size_t i = 0; i < 6; i++) { if (tris[i] == top_left_ix) top_left_count++; } if (should_be_top_left != (top_left_count == 2)) { ufbxt_logf("Fail: %s", mesh->instances.data[0]->name.data); num_fail++; } } ufbxt_logf("Triangulations OK: %zu/%zu", scene->meshes.count - num_fail, scene->meshes.count); return num_fail; } #endif UFBXT_FILE_TEST(maya_triangulate) #if UFBXT_IMPL { size_t num_fail = do_triangulate_test(scene); ufbxt_assert(num_fail <= 4); } #endif UFBXT_FILE_TEST(maya_triangulate_down) #if UFBXT_IMPL { size_t num_fail = do_triangulate_test(scene); ufbxt_assert(num_fail <= 1); } #endif UFBXT_FILE_TEST(maya_tri_cone) #if UFBXT_IMPL { ufbx_node *node = ufbx_find_node(scene, "pCone1"); ufbxt_assert(node && node->mesh); ufbx_mesh *mesh = node->mesh; for (size_t i = 0; i < mesh->num_faces; i++) { ufbx_face face = mesh->faces.data[i]; ufbxt_assert(face.num_indices >= 3 && face.num_indices <= 32); uint32_t tris[32]; size_t num_tris = face.num_indices - 2; for (size_t i = 0; i < 32; i++) { ufbx_panic panic; panic.did_panic = false; size_t num_tris_returned = ufbx_catch_triangulate_face(&panic, tris, i, mesh, face); if (i >= num_tris * 3) { ufbxt_assert(!panic.did_panic); ufbxt_assert(num_tris_returned == num_tris); } else { ufbxt_assert(panic.did_panic); ufbxt_assert(num_tris_returned == 0); } } ufbxt_assert(ufbx_triangulate_face(tris, ufbxt_arraycount(tris), mesh, face)); } } #endif #if UFBXT_IMPL static void ufbxt_ngon_write_obj(const char *path, ufbx_mesh *mesh, const uint32_t *indices, size_t num_triangles) { FILE *f = fopen(path, "w"); for (size_t i = 0; i < mesh->num_vertices; i++) { ufbx_vec3 v = mesh->vertices.data[i]; fprintf(f, "v %f %f %f\n", v.x, v.y, v.z); } fprintf(f, "\n"); for (size_t i = 0; i < num_triangles; i++) { const uint32_t *tri = indices + i * 3; fprintf(f, "f %u %u %u\n", mesh->vertex_indices.data[tri[0]] + 1, mesh->vertex_indices.data[tri[1]] + 1, mesh->vertex_indices.data[tri[2]] + 1); } fclose(f); } static ufbx_vec2 ufbxt_ngon_3d_to_2d(const ufbx_vec3 basis[2], ufbx_vec3 pos) { ufbx_vec2 uv; uv.x = ufbxt_dot3(basis[0], pos); uv.y = ufbxt_dot3(basis[1], pos); return uv; } static void ufbxt_check_ngon_triangulation(ufbxt_diff_error *err, ufbx_mesh *mesh, ufbx_face face, uint32_t *indices, size_t num_triangles, const ufbx_vec3 basis[2]) { // Check that the area matches for now // TODO: More rigorous tests ufbx_real poly_area = 0.0f; for (size_t i = 0; i < face.num_indices; i++) { ufbx_vec2 a = ufbxt_ngon_3d_to_2d(basis, ufbx_get_vertex_vec3(&mesh->vertex_position, face.index_begin + i + 0)); ufbx_vec2 b = ufbxt_ngon_3d_to_2d(basis, ufbx_get_vertex_vec3(&mesh->vertex_position, face.index_begin + (i + 1) % face.num_indices)); poly_area += 0.5f * (a.x*b.y - a.y*b.x); } ufbx_real tri_area = 0.0f; for (size_t i = 0; i < num_triangles; i++) { ufbx_vec2 a = ufbxt_ngon_3d_to_2d(basis, ufbx_get_vertex_vec3(&mesh->vertex_position, indices[i*3 + 0])); ufbx_vec2 b = ufbxt_ngon_3d_to_2d(basis, ufbx_get_vertex_vec3(&mesh->vertex_position, indices[i*3 + 1])); ufbx_vec2 c = ufbxt_ngon_3d_to_2d(basis, ufbx_get_vertex_vec3(&mesh->vertex_position, indices[i*3 + 2])); ufbx_real area = 0.5f * (a.x*(b.y-c.y) + b.x*(c.y-a.y) + c.x*(a.y-b.y)); ufbxt_assert(area >= -0.01f); tri_area += area; } ufbxt_assert_close_real(err, poly_area, tri_area); } #endif UFBXT_FILE_TEST(blender_300_ngon_intersection) #if UFBXT_IMPL { ufbx_node *node = ufbx_find_node(scene, "Plane"); ufbxt_assert(node && node->mesh); ufbx_mesh *mesh = node->mesh; ufbxt_assert(mesh->num_faces == 1); ufbx_face face = mesh->faces.data[0]; uint32_t indices[3*3]; size_t num_tris = ufbx_triangulate_face(indices, ufbxt_arraycount(indices), mesh, face); ufbxt_assert(num_tris == 3); const ufbx_vec3 basis[2] = { { { 1.0f, 0.0f, 0.0f } }, { { 0.0f, 1.0f, 0.0f } }, }; ufbxt_check_ngon_triangulation(err, mesh, face, indices, num_tris, basis); } #endif UFBXT_FILE_TEST(blender_300_ngon_e) #if UFBXT_IMPL { ufbx_node *node = ufbx_find_node(scene, "Plane"); ufbxt_assert(node && node->mesh); ufbx_mesh *mesh = node->mesh; ufbxt_assert(mesh->num_faces == 1); ufbx_face face = mesh->faces.data[0]; uint32_t indices[10*3]; size_t num_tris = ufbx_triangulate_face(indices, ufbxt_arraycount(indices), mesh, face); ufbxt_assert(num_tris == 10); const ufbx_vec3 basis[2] = { { { 1.0f, 0.0f, 0.0f } }, { { 0.0f, 1.0f, 0.0f } }, }; ufbxt_check_ngon_triangulation(err, mesh, face, indices, num_tris, basis); } #endif UFBXT_FILE_TEST(blender_300_ngon_abstract) #if UFBXT_IMPL { ufbx_node *node = ufbx_find_node(scene, "Plane"); ufbxt_assert(node && node->mesh); ufbx_mesh *mesh = node->mesh; ufbxt_assert(mesh->num_faces == 1); ufbx_face face = mesh->faces.data[0]; uint32_t indices[144*3]; size_t num_tris = ufbx_triangulate_face(indices, ufbxt_arraycount(indices), mesh, face); ufbxt_assert(num_tris == 144); const ufbx_vec3 basis[2] = { { { 1.0f, 0.0f, 0.0f } }, { { 0.0f, 1.0f, 0.0f } }, }; ufbxt_check_ngon_triangulation(err, mesh, face, indices, num_tris, basis); } #endif UFBXT_FILE_TEST(blender_300_ngon_big) #if UFBXT_IMPL { ufbx_node *node = ufbx_find_node(scene, "Plane"); ufbxt_assert(node && node->mesh); ufbx_mesh *mesh = node->mesh; ufbxt_assert(mesh->num_faces == 1); ufbx_face face = mesh->faces.data[0]; uint32_t expected_tris = 8028; uint32_t *indices = malloc(expected_tris * 3 * sizeof(uint32_t)); ufbxt_assert(indices); size_t num_tris = ufbx_triangulate_face(indices, expected_tris * 3, mesh, face); ufbxt_assert(num_tris == expected_tris); const ufbx_vec3 basis[2] = { { { 1.0f, 0.0f, 0.0f } }, { { 0.0f, 1.0f, 0.0f } }, }; ufbxt_check_ngon_triangulation(err, mesh, face, indices, num_tris, basis); free(indices); } #endif UFBXT_FILE_TEST(blender_300_ngon_irregular) #if UFBXT_IMPL { ufbx_node *node = ufbx_find_node(scene, "Plane"); ufbxt_assert(node && node->mesh); ufbx_mesh *mesh = node->mesh; uint32_t indices[256]; for (size_t i = 0; i < mesh->num_faces; i++) { ufbx_face face = mesh->faces.data[i]; size_t num_tris = ufbx_triangulate_face(indices, ufbxt_arraycount(indices), mesh, face); ufbxt_assert(num_tris == face.num_indices - 2); } } #endif