From 781cd27fe432349c36c5363be4f879b1c3c48c10 Mon Sep 17 00:00:00 2001 From: clayjohn Date: Fri, 23 Feb 2024 13:54:20 -0800 Subject: [PATCH] Avoid singularity when generated tangents and validate that tangents are good enough when using compression --- editor/import/3d/editor_import_collada.cpp | 17 +++++++++++++++-- editor/import/3d/resource_importer_obj.cpp | 18 ++++++++++++++++-- modules/gltf/gltf_document.cpp | 15 ++++++++++++++- scene/resources/immediate_mesh.cpp | 2 +- servers/rendering_server.cpp | 6 ++++-- 5 files changed, 50 insertions(+), 8 deletions(-) diff --git a/editor/import/3d/editor_import_collada.cpp b/editor/import/3d/editor_import_collada.cpp index 49fec679bf8..f8ffd70b681 100644 --- a/editor/import/3d/editor_import_collada.cpp +++ b/editor/import/3d/editor_import_collada.cpp @@ -938,11 +938,11 @@ Error ColladaImport::_create_mesh_surfaces(bool p_optimize, Ref &p if (binormal_src && tangent_src) { surftool->set_tangent(vertex_array[k].tangent); } else if (generate_dummy_tangents) { - Vector3 tan = Vector3(0.0, 1.0, 0.0).cross(vertex_array[k].normal); + Vector3 tan = Vector3(vertex_array[k].normal.z, -vertex_array[k].normal.x, vertex_array[k].normal.y).cross(vertex_array[k].normal.normalized()).normalized(); surftool->set_tangent(Plane(tan.x, tan.y, tan.z, 1.0)); } } else { - // No normals, use a dummy normal since normals will be generated. + // No normals, use a dummy tangent since normals will be generated. if (generate_dummy_tangents) { surftool->set_tangent(Plane(1.0, 0.0, 0.0, 1.0)); } @@ -1008,6 +1008,19 @@ Error ColladaImport::_create_mesh_surfaces(bool p_optimize, Ref &p Array d = surftool->commit_to_arrays(); d.resize(RS::ARRAY_MAX); + if (mesh_flags & RS::ARRAY_FLAG_COMPRESS_ATTRIBUTES && (generate_dummy_tangents || generate_tangents)) { + // Compression is enabled, so let's validate that the normals and tangents are correct. + Vector normals = d[Mesh::ARRAY_NORMAL]; + Vector tangents = d[Mesh::ARRAY_TANGENT]; + for (int vert = 0; vert < normals.size(); vert++) { + Vector3 tan = Vector3(tangents[vert * 4 + 0], tangents[vert * 4 + 1], tangents[vert * 4 + 2]); + if (abs(tan.dot(normals[vert])) > 0.0001) { + // Tangent is not perpendicular to the normal, so we can't use compression. + mesh_flags &= ~RS::ARRAY_FLAG_COMPRESS_ATTRIBUTES; + } + } + } + Array mr; //////////////////////////// diff --git a/editor/import/3d/resource_importer_obj.cpp b/editor/import/3d/resource_importer_obj.cpp index 4b340ea997f..2ca9260d1f9 100644 --- a/editor/import/3d/resource_importer_obj.cpp +++ b/editor/import/3d/resource_importer_obj.cpp @@ -329,11 +329,11 @@ static Error _parse_obj(const String &p_path, List> &r_meshes, surf_tool->set_normal(normals[norm]); if (generate_tangents && uvs.is_empty()) { // We can't generate tangents without UVs, so create dummy tangents. - Vector3 tan = Vector3(0.0, 1.0, 0.0).cross(normals[norm]); + Vector3 tan = Vector3(normals[norm].z, -normals[norm].x, normals[norm].y).cross(normals[norm].normalized()).normalized(); surf_tool->set_tangent(Plane(tan.x, tan.y, tan.z, 1.0)); } } else { - // No normals, use a dummy normal since normals will be generated. + // No normals, use a dummy tangent since normals and tangents will be generated. if (generate_tangents && uvs.is_empty()) { // We can't generate tangents without UVs, so create dummy tangents. surf_tool->set_tangent(Plane(1.0, 0.0, 0.0, 1.0)); @@ -415,6 +415,20 @@ static Error _parse_obj(const String &p_path, List> &r_meshes, mesh->set_surface_name(mesh->get_surface_count() - 1, current_group); } Array array = surf_tool->commit_to_arrays(); + + if (mesh_flags & RS::ARRAY_FLAG_COMPRESS_ATTRIBUTES && generate_tangents) { + // Compression is enabled, so let's validate that the normals and tangents are correct. + Vector norms = array[Mesh::ARRAY_NORMAL]; + Vector tangents = array[Mesh::ARRAY_TANGENT]; + for (int vert = 0; vert < norms.size(); vert++) { + Vector3 tan = Vector3(tangents[vert * 4 + 0], tangents[vert * 4 + 1], tangents[vert * 4 + 2]); + if (abs(tan.dot(norms[vert])) > 0.0001) { + // Tangent is not perpendicular to the normal, so we can't use compression. + mesh_flags &= ~RS::ARRAY_FLAG_COMPRESS_ATTRIBUTES; + } + } + } + mesh->add_surface(Mesh::PRIMITIVE_TRIANGLES, array, TypedArray(), Dictionary(), material, name, mesh_flags); print_verbose("OBJ: Added surface :" + mesh->get_surface_name(mesh->get_surface_count() - 1)); diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp index ff2f290e4fd..9aed4997515 100644 --- a/modules/gltf/gltf_document.cpp +++ b/modules/gltf/gltf_document.cpp @@ -2813,7 +2813,7 @@ Error GLTFDocument::_parse_meshes(Ref p_state) { Vector normals = array[Mesh::ARRAY_NORMAL]; for (int k = 0; k < vertex_num; k++) { - Vector3 tan = Vector3(0.0, 1.0, 0.0).cross(normals[k]); + Vector3 tan = Vector3(normals[i].z, -normals[i].x, normals[i].y).cross(normals[k].normalized()).normalized(); tangentsw[k * 4 + 0] = tan.x; tangentsw[k * 4 + 1] = tan.y; tangentsw[k * 4 + 2] = tan.z; @@ -2839,6 +2839,19 @@ Error GLTFDocument::_parse_meshes(Ref p_state) { } array = mesh_surface_tool->commit_to_arrays(); + if ((flags & RS::ARRAY_FLAG_COMPRESS_ATTRIBUTES) && a.has("NORMAL") && (a.has("TANGENT") || generate_tangents)) { + // Compression is enabled, so let's validate that the normals and tangents are correct. + Vector normals = array[Mesh::ARRAY_NORMAL]; + Vector tangents = array[Mesh::ARRAY_TANGENT]; + for (int vert = 0; vert < normals.size(); vert++) { + Vector3 tan = Vector3(tangents[vert * 4 + 0], tangents[vert * 4 + 1], tangents[vert * 4 + 2]); + if (abs(tan.dot(normals[vert])) > 0.0001) { + // Tangent is not perpendicular to the normal, so we can't use compression. + flags &= ~RS::ARRAY_FLAG_COMPRESS_ATTRIBUTES; + } + } + } + Array morphs; //blend shapes if (p.has("targets")) { diff --git a/scene/resources/immediate_mesh.cpp b/scene/resources/immediate_mesh.cpp index a51e28c9fe2..907c0ab4ca3 100644 --- a/scene/resources/immediate_mesh.cpp +++ b/scene/resources/immediate_mesh.cpp @@ -208,7 +208,7 @@ void ImmediateMesh::surface_end() { if (uses_tangents) { t = tangents[i].normal.octahedron_tangent_encode(tangents[i].d); } else { - Vector3 tan = Vector3(0.0, 1.0, 0.0).cross(normals[i].normalized()); + Vector3 tan = Vector3(normals[i].z, -normals[i].x, normals[i].y).cross(normals[i].normalized()).normalized(); t = tan.octahedron_tangent_encode(1.0); } diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp index 6e9b525f31c..3aa042ce781 100644 --- a/servers/rendering_server.cpp +++ b/servers/rendering_server.cpp @@ -566,7 +566,8 @@ Error RenderingServer::_surface_set_data(Array p_arrays, uint64_t p_format, uint float angle; Vector3 axis; // Generate an arbitrary vector that is tangential to normal. - Vector3 tan = Vector3(0.0, 1.0, 0.0).cross(normal_src[i].normalized()); + // This assumes that the normal is never (0,0,0). + Vector3 tan = Vector3(normal_src[i].z, -normal_src[i].x, normal_src[i].y).cross(normal_src[i].normalized()).normalized(); Vector4 tangent = Vector4(tan.x, tan.y, tan.z, 1.0); _get_axis_angle(normal_src[i], tangent, angle, axis); @@ -689,7 +690,8 @@ Error RenderingServer::_surface_set_data(Array p_arrays, uint64_t p_format, uint // Set data for tangent. for (int i = 0; i < p_vertex_array_len; i++) { // Generate an arbitrary vector that is tangential to normal. - Vector3 tan = Vector3(0.0, 1.0, 0.0).cross(normal_src[i].normalized()); + // This assumes that the normal is never (0,0,0). + Vector3 tan = Vector3(normal_src[i].z, -normal_src[i].x, normal_src[i].y).cross(normal_src[i].normalized()).normalized(); Vector2 res = tan.octahedron_tangent_encode(1.0); uint16_t vector[2] = { (uint16_t)CLAMP(res.x * 65535, 0, 65535),