diff --git a/modules/fbx/fbx_document.cpp b/modules/fbx/fbx_document.cpp index bdd9f057a98e..99ab05d6280e 100644 --- a/modules/fbx/fbx_document.cpp +++ b/modules/fbx/fbx_document.cpp @@ -758,8 +758,7 @@ Error FBXDocument::_parse_meshes(Ref p_state) { mesh_surface_tool->set_skin_weight_count(num_skin_weights == 8 ? SurfaceTool::SKIN_8_WEIGHTS : SurfaceTool::SKIN_4_WEIGHTS); mesh_surface_tool->index(); if (generate_tangents) { - //must generate mikktspace tangents.. ergh.. - mesh_surface_tool->generate_tangents(); + mesh_surface_tool->generate_tangents(/*split*/ !use_blend_shapes); } array = mesh_surface_tool->commit_to_arrays(); @@ -828,8 +827,7 @@ Error FBXDocument::_parse_meshes(Ref p_state) { blend_surface_tool->create_from_triangle_arrays(array_copy); blend_surface_tool->set_skin_weight_count(num_skin_weights == 8 ? SurfaceTool::SKIN_8_WEIGHTS : SurfaceTool::SKIN_4_WEIGHTS); if (generate_tangents) { - //must generate mikktspace tangents.. ergh.. - blend_surface_tool->generate_tangents(); + blend_surface_tool->generate_tangents(/*split*/ false); } array_copy = blend_surface_tool->commit_to_arrays(); diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp index 5d2bc1bae04e..c59f3e4f0b5b 100644 --- a/modules/gltf/gltf_document.cpp +++ b/modules/gltf/gltf_document.cpp @@ -1834,8 +1834,7 @@ Error GLTFDocument::_parse_meshes(Ref p_state) { } mesh_surface_tool->index(); if (generate_tangents && a.has("TEXCOORD_0")) { - //must generate mikktspace tangents.. ergh.. - mesh_surface_tool->generate_tangents(); + mesh_surface_tool->generate_tangents(/*split*/ !mesh_prim.has("targets")); } array = mesh_surface_tool->commit_to_arrays(); @@ -1974,7 +1973,7 @@ Error GLTFDocument::_parse_meshes(Ref p_state) { } blend_surface_tool->index(); if (generate_tangents) { - blend_surface_tool->generate_tangents(); + blend_surface_tool->generate_tangents(/*split*/ false); } array_copy = blend_surface_tool->commit_to_arrays(); diff --git a/modules/meshoptimizer/register_types.cpp b/modules/meshoptimizer/register_types.cpp index a709993a61f2..03d57f6789a8 100644 --- a/modules/meshoptimizer/register_types.cpp +++ b/modules/meshoptimizer/register_types.cpp @@ -47,6 +47,7 @@ void initialize_meshoptimizer_module(ModuleInitializationLevel p_level) { SurfaceTool::generate_remap_func = meshopt_generateVertexRemap; SurfaceTool::remap_vertex_func = meshopt_remapVertexBuffer; SurfaceTool::remap_index_func = meshopt_remapIndexBuffer; + SurfaceTool::generate_tangents_func = meshopt_generateTangents; } void uninitialize_meshoptimizer_module(ModuleInitializationLevel p_level) { @@ -61,4 +62,5 @@ void uninitialize_meshoptimizer_module(ModuleInitializationLevel p_level) { SurfaceTool::generate_remap_func = nullptr; SurfaceTool::remap_vertex_func = nullptr; SurfaceTool::remap_index_func = nullptr; + SurfaceTool::generate_tangents_func = nullptr; } diff --git a/scene/resources/surface_tool.cpp b/scene/resources/surface_tool.cpp index 725c9daa0446..6b7cccbaf781 100644 --- a/scene/resources/surface_tool.cpp +++ b/scene/resources/surface_tool.cpp @@ -43,6 +43,7 @@ SurfaceTool::SimplifyScaleFunc SurfaceTool::simplify_scale_func = nullptr; SurfaceTool::GenerateRemapFunc SurfaceTool::generate_remap_func = nullptr; SurfaceTool::RemapVertexFunc SurfaceTool::remap_vertex_func = nullptr; SurfaceTool::RemapIndexFunc SurfaceTool::remap_index_func = nullptr; +SurfaceTool::GenerateTangentsFunc SurfaceTool::generate_tangents_func = nullptr; void SurfaceTool::strip_mesh_arrays(PackedVector3Array &r_vertices, PackedInt32Array &r_indices) { ERR_FAIL_COND_MSG(!generate_remap_func || !remap_vertex_func || !remap_index_func, "Meshoptimizer library is not initialized."); @@ -1053,123 +1054,105 @@ void SurfaceTool::append_from(const Ref &p_existing, int p_surface, const } } -//mikktspace callbacks -namespace { -struct TangentGenerationContextUserData { - LocalVector *vertices; - LocalVector *indices; +struct TangentVertex { + float position[3]; + float normal[3]; + float uv[2]; }; -} // namespace -int SurfaceTool::mikktGetNumFaces(const SMikkTSpaceContext *pContext) { - TangentGenerationContextUserData &triangle_data = *reinterpret_cast(pContext->m_pUserData); +static void _propagate_tangents_or_split(LocalVector &r_vertex_array, LocalVector &r_index_array, const float *p_tangents, bool p_split) { + // Seed each vertex with one of its corner tangents; the loop below fixes any mismatches. + for (size_t i = 0; i < r_index_array.size(); i++) { + Vector4 tangent(p_tangents[i * 4 + 0], p_tangents[i * 4 + 1], p_tangents[i * 4 + 2], p_tangents[i * 4 + 3]); + r_vertex_array[r_index_array[i]].tangent = Vector4(p_tangents[i * 4 + 0], p_tangents[i * 4 + 1], p_tangents[i * 4 + 2], p_tangents[i * 4 + 3]); + } - if (triangle_data.indices->size() > 0) { - return triangle_data.indices->size() / 3; - } else { - return triangle_data.vertices->size() / 3; + if (!p_split) { + return; } -} -int SurfaceTool::mikktGetNumVerticesOfFace(const SMikkTSpaceContext *pContext, const int iFace) { - return 3; //always 3 -} + LocalVector splits; + splits.resize(r_vertex_array.size()); + memset(splits.ptr(), -1, splits.size() * sizeof(uint32_t)); // ~0u means "no split copy" -void SurfaceTool::mikktGetPosition(const SMikkTSpaceContext *pContext, float fvPosOut[], const int iFace, const int iVert) { - TangentGenerationContextUserData &triangle_data = *reinterpret_cast(pContext->m_pUserData); - Vector3 v; - if (triangle_data.indices->size() > 0) { - uint32_t index = triangle_data.indices->operator[](iFace * 3 + iVert); - if (index < triangle_data.vertices->size()) { - v = triangle_data.vertices->operator[](index).vertex; + for (size_t i = 0; i < r_index_array.size(); i++) { + // Walk the chain of split copies looking for a vertex whose tangent matches. + Vector4 tangent(p_tangents[i * 4 + 0], p_tangents[i * 4 + 1], p_tangents[i * 4 + 2], p_tangents[i * 4 + 3]); + uint32_t v = r_index_array[i]; + while (v != ~0u && r_vertex_array[v].tangent != tangent) { + v = splits[v]; } - } else { - v = triangle_data.vertices->operator[](iFace * 3 + iVert).vertex; - } - - fvPosOut[0] = v.x; - fvPosOut[1] = v.y; - fvPosOut[2] = v.z; -} -void SurfaceTool::mikktGetNormal(const SMikkTSpaceContext *pContext, float fvNormOut[], const int iFace, const int iVert) { - TangentGenerationContextUserData &triangle_data = *reinterpret_cast(pContext->m_pUserData); - Vector3 v; - if (triangle_data.indices->size() > 0) { - uint32_t index = triangle_data.indices->operator[](iFace * 3 + iVert); - if (index < triangle_data.vertices->size()) { - v = triangle_data.vertices->operator[](index).normal; + // No match in chain: append a new split copy with the target tangent and chain it. + if (v == ~0u) { + v = r_vertex_array.size(); + r_vertex_array.push_back(r_vertex_array[r_index_array[i]]); + r_vertex_array[v].tangent = tangent; + splits.push_back(splits[r_index_array[i]]); + splits[r_index_array[i]] = v; } - } else { - v = triangle_data.vertices->operator[](iFace * 3 + iVert).normal; + + r_index_array[i] = int(v); } +} - fvNormOut[0] = v.x; - fvNormOut[1] = v.y; - fvNormOut[2] = v.z; +void SurfaceTool::_generate_tangents_bind() { + generate_tangents(/*split*/ false); } -void SurfaceTool::mikktGetTexCoord(const SMikkTSpaceContext *pContext, float fvTexcOut[], const int iFace, const int iVert) { - TangentGenerationContextUserData &triangle_data = *reinterpret_cast(pContext->m_pUserData); - Vector2 v; - if (triangle_data.indices->size() > 0) { - uint32_t index = triangle_data.indices->operator[](iFace * 3 + iVert); - if (index < triangle_data.vertices->size()) { - v = triangle_data.vertices->operator[](index).uv; - } - } else { - v = triangle_data.vertices->operator[](iFace * 3 + iVert).uv; +void SurfaceTool::generate_tangents(bool p_split) { + ERR_FAIL_COND_MSG(!generate_tangents_func, "Meshoptimizer library is not initialized."); + ERR_FAIL_COND_MSG(!(format & Mesh::ARRAY_FORMAT_TEX_UV), "UVs are required to generate tangents."); + ERR_FAIL_COND(!(format & Mesh::ARRAY_FORMAT_NORMAL)); + ERR_FAIL_COND(primitive != Mesh::PRIMITIVE_TRIANGLES); + + if (vertex_array.is_empty()) { + format |= Mesh::ARRAY_FORMAT_TANGENT; + return; } - fvTexcOut[0] = v.x; - fvTexcOut[1] = v.y; -} + size_t corner_count = index_array.size() > 0 ? index_array.size() : vertex_array.size(); + ERR_FAIL_COND(corner_count % 3 != 0); -void SurfaceTool::mikktSetTSpaceDefault(const SMikkTSpaceContext *pContext, const float fvTangent[], const float fvBiTangent[], const float fMagS, const float fMagT, - const tbool bIsOrientationPreserving, const int iFace, const int iVert) { - TangentGenerationContextUserData &triangle_data = *reinterpret_cast(pContext->m_pUserData); - Vertex *vtx = nullptr; - if (triangle_data.indices->size() > 0) { - uint32_t index = triangle_data.indices->operator[](iFace * 3 + iVert); - if (index < triangle_data.vertices->size()) { - vtx = &triangle_data.vertices->operator[](index); - } - } else { - vtx = &triangle_data.vertices->operator[](iFace * 3 + iVert); - } + // We can't operate on SurfaceTool::Vertex directly because in double-precision builds, vectors use double components + // So we convert the inputs to single precision floats before generating tangents. + LocalVector tangent_vertices; + tangent_vertices.resize(vertex_array.size()); - if (vtx != nullptr) { - vtx->tangent = Vector4(fvTangent[0], fvTangent[1], fvTangent[2], bIsOrientationPreserving ? 1.0f : -1.0f); + for (size_t i = 0; i < vertex_array.size(); i++) { + const Vertex &vertex = vertex_array[i]; + TangentVertex &tangent_vertex = tangent_vertices[i]; + + tangent_vertex.position[0] = vertex.vertex.x; + tangent_vertex.position[1] = vertex.vertex.y; + tangent_vertex.position[2] = vertex.vertex.z; + tangent_vertex.normal[0] = vertex.normal.x; + tangent_vertex.normal[1] = vertex.normal.y; + tangent_vertex.normal[2] = vertex.normal.z; + tangent_vertex.uv[0] = vertex.uv.x; + tangent_vertex.uv[1] = vertex.uv.y; } -} -void SurfaceTool::generate_tangents() { - ERR_FAIL_COND_MSG(!(format & Mesh::ARRAY_FORMAT_TEX_UV), "UVs are required to generate tangents."); - ERR_FAIL_COND(!(format & Mesh::ARRAY_FORMAT_NORMAL)); + const TangentVertex *vertices = tangent_vertices.ptr(); - SMikkTSpaceInterface mkif; - mkif.m_getNormal = mikktGetNormal; - mkif.m_getNumFaces = mikktGetNumFaces; - mkif.m_getNumVerticesOfFace = mikktGetNumVerticesOfFace; - mkif.m_getPosition = mikktGetPosition; - mkif.m_getTexCoord = mikktGetTexCoord; - mkif.m_setTSpace = mikktSetTSpaceDefault; - mkif.m_setTSpaceBasic = nullptr; + LocalVector tangents; + tangents.resize(corner_count * 4); - SMikkTSpaceContext msc; - msc.m_pInterface = &mkif; + generate_tangents_func(tangents.ptr(), + index_array.size() > 0 ? reinterpret_cast(index_array.ptr()) : nullptr, corner_count, + vertices->position, tangent_vertices.size(), sizeof(TangentVertex), + vertices->normal, sizeof(TangentVertex), + vertices->uv, sizeof(TangentVertex), 0); - TangentGenerationContextUserData triangle_data; - triangle_data.vertices = &vertex_array; - for (Vertex &vertex : vertex_array) { - vertex.tangent = Vector4(); + if (index_array.size() > 0) { + _propagate_tangents_or_split(vertex_array, index_array, tangents.ptr(), p_split); + } else { + for (size_t i = 0; i < corner_count; i++) { + Vector4 tangent(tangents[i * 4 + 0], tangents[i * 4 + 1], tangents[i * 4 + 2], tangents[i * 4 + 3]); + vertex_array[i].tangent = tangent; + } } - triangle_data.indices = &index_array; - msc.m_pUserData = &triangle_data; - - bool res = genTangSpaceDefault(&msc); - ERR_FAIL_COND(!res); format |= Mesh::ARRAY_FORMAT_TANGENT; } @@ -1355,7 +1338,7 @@ void SurfaceTool::_bind_methods() { ClassDB::bind_method(D_METHOD("index"), &SurfaceTool::index); ClassDB::bind_method(D_METHOD("deindex"), &SurfaceTool::deindex); ClassDB::bind_method(D_METHOD("generate_normals", "flip"), &SurfaceTool::generate_normals, DEFVAL(false)); - ClassDB::bind_method(D_METHOD("generate_tangents"), &SurfaceTool::generate_tangents); + ClassDB::bind_method(D_METHOD("generate_tangents"), &SurfaceTool::_generate_tangents_bind); ClassDB::bind_method(D_METHOD("optimize_indices_for_cache"), &SurfaceTool::optimize_indices_for_cache); diff --git a/scene/resources/surface_tool.h b/scene/resources/surface_tool.h index 530d8c3684e9..4083045d6998 100644 --- a/scene/resources/surface_tool.h +++ b/scene/resources/surface_tool.h @@ -35,8 +35,6 @@ #include "scene/resources/mesh.h" #include "servers/rendering/rendering_server_enums.h" -#include - class SurfaceTool : public RefCounted { GDCLASS(SurfaceTool, RefCounted); @@ -95,6 +93,8 @@ class SurfaceTool : public RefCounted { SIMPLIFY_REGULARIZE = 1 << 4, // From meshopt_SimplifyRegularize /* Allow collapses across attribute discontinuities, except for vertices that are tagged with 0x02 in vertex_lock. */ SIMPLIFY_PERMISSIVE = 1 << 5, // From meshopt_SimplifyPermissive + /* Produce tangents compatible with MikkTSpace (same weighting and fallbacks) at the cost of reduced quality. Not recommended unless normal maps are baked. */ + TANGENT_COMPATIBLE = 1 << 0, // From meshopt_TangentCompatible }; typedef void (*OptimizeVertexCacheFunc)(unsigned int *destination, const unsigned int *indices, size_t index_count, size_t vertex_count); @@ -113,6 +113,8 @@ class SurfaceTool : public RefCounted { static RemapVertexFunc remap_vertex_func; typedef void (*RemapIndexFunc)(unsigned int *destination, const unsigned int *indices, size_t index_count, const unsigned int *remap); static RemapIndexFunc remap_index_func; + typedef void (*GenerateTangentsFunc)(float *result, const unsigned int *indices, size_t index_count, const float *vertex_positions, size_t vertex_count, size_t vertex_positions_stride, const float *vertex_normals, size_t vertex_normals_stride, const float *vertex_uvs, size_t vertex_uvs_stride, unsigned int options); + static GenerateTangentsFunc generate_tangents_func; static void strip_mesh_arrays(PackedVector3Array &r_vertices, PackedInt32Array &r_indices); private: @@ -176,17 +178,10 @@ class SurfaceTool : public RefCounted { void _create_list_from_arrays(Array arr, LocalVector *r_vertex, LocalVector *r_index, uint64_t &lformat); void _create_list(const Ref &p_existing, int p_surface, LocalVector *r_vertex, LocalVector *r_index, uint64_t &lformat); - //mikktspace callbacks - static int mikktGetNumFaces(const SMikkTSpaceContext *pContext); - static int mikktGetNumVerticesOfFace(const SMikkTSpaceContext *pContext, const int iFace); - static void mikktGetPosition(const SMikkTSpaceContext *pContext, float fvPosOut[], const int iFace, const int iVert); - static void mikktGetNormal(const SMikkTSpaceContext *pContext, float fvNormOut[], const int iFace, const int iVert); - static void mikktGetTexCoord(const SMikkTSpaceContext *pContext, float fvTexcOut[], const int iFace, const int iVert); - static void mikktSetTSpaceDefault(const SMikkTSpaceContext *pContext, const float fvTangent[], const float fvBiTangent[], const float fMagS, const float fMagT, - const tbool bIsOrientationPreserving, const int iFace, const int iVert); - void _add_triangle_fan(const Vector &p_vertices, const Vector &p_uvs = Vector(), const Vector &p_colors = Vector(), const Vector &p_uv2s = Vector(), const Vector &p_normals = Vector(), const TypedArray &p_tangents = TypedArray()); + void _generate_tangents_bind(); + protected: static void _bind_methods(); @@ -220,7 +215,7 @@ class SurfaceTool : public RefCounted { void index(); void deindex(); void generate_normals(bool p_flip = false); - void generate_tangents(); + void generate_tangents(bool p_split = false); void optimize_indices_for_cache(); AABB get_aabb() const;