فهرست منبع

Avoid singularity when generated tangents and validate that tangents are good enough when using compression

clayjohn 1 سال پیش
والد
کامیت
781cd27fe4

+ 15 - 2
editor/import/3d/editor_import_collada.cpp

@@ -938,11 +938,11 @@ Error ColladaImport::_create_mesh_surfaces(bool p_optimize, Ref<ImporterMesh> &p
 					if (binormal_src && tangent_src) {
 					if (binormal_src && tangent_src) {
 						surftool->set_tangent(vertex_array[k].tangent);
 						surftool->set_tangent(vertex_array[k].tangent);
 					} else if (generate_dummy_tangents) {
 					} 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));
 						surftool->set_tangent(Plane(tan.x, tan.y, tan.z, 1.0));
 					}
 					}
 				} else {
 				} 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) {
 					if (generate_dummy_tangents) {
 						surftool->set_tangent(Plane(1.0, 0.0, 0.0, 1.0));
 						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<ImporterMesh> &p
 			Array d = surftool->commit_to_arrays();
 			Array d = surftool->commit_to_arrays();
 			d.resize(RS::ARRAY_MAX);
 			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<Vector3> normals = d[Mesh::ARRAY_NORMAL];
+				Vector<float> 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;
 			Array mr;
 
 
 			////////////////////////////
 			////////////////////////////

+ 16 - 2
editor/import/3d/resource_importer_obj.cpp

@@ -329,11 +329,11 @@ static Error _parse_obj(const String &p_path, List<Ref<ImporterMesh>> &r_meshes,
 						surf_tool->set_normal(normals[norm]);
 						surf_tool->set_normal(normals[norm]);
 						if (generate_tangents && uvs.is_empty()) {
 						if (generate_tangents && uvs.is_empty()) {
 							// We can't generate tangents without UVs, so create dummy tangents.
 							// 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));
 							surf_tool->set_tangent(Plane(tan.x, tan.y, tan.z, 1.0));
 						}
 						}
 					} else {
 					} 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()) {
 						if (generate_tangents && uvs.is_empty()) {
 							// We can't generate tangents without UVs, so create dummy tangents.
 							// We can't generate tangents without UVs, so create dummy tangents.
 							surf_tool->set_tangent(Plane(1.0, 0.0, 0.0, 1.0));
 							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<Ref<ImporterMesh>> &r_meshes,
 					mesh->set_surface_name(mesh->get_surface_count() - 1, current_group);
 					mesh->set_surface_name(mesh->get_surface_count() - 1, current_group);
 				}
 				}
 				Array array = surf_tool->commit_to_arrays();
 				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<Vector3> norms = array[Mesh::ARRAY_NORMAL];
+					Vector<float> 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<Array>(), Dictionary(), material, name, mesh_flags);
 				mesh->add_surface(Mesh::PRIMITIVE_TRIANGLES, array, TypedArray<Array>(), Dictionary(), material, name, mesh_flags);
 				print_verbose("OBJ: Added surface :" + mesh->get_surface_name(mesh->get_surface_count() - 1));
 				print_verbose("OBJ: Added surface :" + mesh->get_surface_name(mesh->get_surface_count() - 1));
 
 

+ 14 - 1
modules/gltf/gltf_document.cpp

@@ -2813,7 +2813,7 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> p_state) {
 
 
 				Vector<Vector3> normals = array[Mesh::ARRAY_NORMAL];
 				Vector<Vector3> normals = array[Mesh::ARRAY_NORMAL];
 				for (int k = 0; k < vertex_num; k++) {
 				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 + 0] = tan.x;
 					tangentsw[k * 4 + 1] = tan.y;
 					tangentsw[k * 4 + 1] = tan.y;
 					tangentsw[k * 4 + 2] = tan.z;
 					tangentsw[k * 4 + 2] = tan.z;
@@ -2839,6 +2839,19 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> p_state) {
 			}
 			}
 			array = mesh_surface_tool->commit_to_arrays();
 			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<Vector3> normals = array[Mesh::ARRAY_NORMAL];
+				Vector<float> 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;
 			Array morphs;
 			//blend shapes
 			//blend shapes
 			if (p.has("targets")) {
 			if (p.has("targets")) {

+ 1 - 1
scene/resources/immediate_mesh.cpp

@@ -208,7 +208,7 @@ void ImmediateMesh::surface_end() {
 				if (uses_tangents) {
 				if (uses_tangents) {
 					t = tangents[i].normal.octahedron_tangent_encode(tangents[i].d);
 					t = tangents[i].normal.octahedron_tangent_encode(tangents[i].d);
 				} else {
 				} 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);
 					t = tan.octahedron_tangent_encode(1.0);
 				}
 				}
 
 

+ 4 - 2
servers/rendering_server.cpp

@@ -566,7 +566,8 @@ Error RenderingServer::_surface_set_data(Array p_arrays, uint64_t p_format, uint
 								float angle;
 								float angle;
 								Vector3 axis;
 								Vector3 axis;
 								// Generate an arbitrary vector that is tangential to normal.
 								// 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);
 								Vector4 tangent = Vector4(tan.x, tan.y, tan.z, 1.0);
 								_get_axis_angle(normal_src[i], tangent, angle, axis);
 								_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.
 						// Set data for tangent.
 						for (int i = 0; i < p_vertex_array_len; i++) {
 						for (int i = 0; i < p_vertex_array_len; i++) {
 							// Generate an arbitrary vector that is tangential to normal.
 							// 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);
 							Vector2 res = tan.octahedron_tangent_encode(1.0);
 							uint16_t vector[2] = {
 							uint16_t vector[2] = {
 								(uint16_t)CLAMP(res.x * 65535, 0, 65535),
 								(uint16_t)CLAMP(res.x * 65535, 0, 65535),