Skip to content

Commit 6e72052

Browse files
committed
Merge pull request #119161 from zeux/nice-tangents
Improve tangent space generation
2 parents 0b67dab + e9b27fd commit 6e72052

5 files changed

Lines changed: 90 additions & 113 deletions

File tree

modules/fbx/fbx_document.cpp

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -758,8 +758,7 @@ Error FBXDocument::_parse_meshes(Ref<FBXState> p_state) {
758758
mesh_surface_tool->set_skin_weight_count(num_skin_weights == 8 ? SurfaceTool::SKIN_8_WEIGHTS : SurfaceTool::SKIN_4_WEIGHTS);
759759
mesh_surface_tool->index();
760760
if (generate_tangents) {
761-
//must generate mikktspace tangents.. ergh..
762-
mesh_surface_tool->generate_tangents();
761+
mesh_surface_tool->generate_tangents(/*split*/ !use_blend_shapes);
763762
}
764763
array = mesh_surface_tool->commit_to_arrays();
765764

@@ -828,8 +827,7 @@ Error FBXDocument::_parse_meshes(Ref<FBXState> p_state) {
828827
blend_surface_tool->create_from_triangle_arrays(array_copy);
829828
blend_surface_tool->set_skin_weight_count(num_skin_weights == 8 ? SurfaceTool::SKIN_8_WEIGHTS : SurfaceTool::SKIN_4_WEIGHTS);
830829
if (generate_tangents) {
831-
//must generate mikktspace tangents.. ergh..
832-
blend_surface_tool->generate_tangents();
830+
blend_surface_tool->generate_tangents(/*split*/ false);
833831
}
834832
array_copy = blend_surface_tool->commit_to_arrays();
835833

modules/gltf/gltf_document.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1834,8 +1834,7 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> p_state) {
18341834
}
18351835
mesh_surface_tool->index();
18361836
if (generate_tangents && a.has("TEXCOORD_0")) {
1837-
//must generate mikktspace tangents.. ergh..
1838-
mesh_surface_tool->generate_tangents();
1837+
mesh_surface_tool->generate_tangents(/*split*/ !mesh_prim.has("targets"));
18391838
}
18401839
array = mesh_surface_tool->commit_to_arrays();
18411840

@@ -1974,7 +1973,7 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> p_state) {
19741973
}
19751974
blend_surface_tool->index();
19761975
if (generate_tangents) {
1977-
blend_surface_tool->generate_tangents();
1976+
blend_surface_tool->generate_tangents(/*split*/ false);
19781977
}
19791978
array_copy = blend_surface_tool->commit_to_arrays();
19801979

modules/meshoptimizer/register_types.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ void initialize_meshoptimizer_module(ModuleInitializationLevel p_level) {
4747
SurfaceTool::generate_remap_func = meshopt_generateVertexRemap;
4848
SurfaceTool::remap_vertex_func = meshopt_remapVertexBuffer;
4949
SurfaceTool::remap_index_func = meshopt_remapIndexBuffer;
50+
SurfaceTool::generate_tangents_func = meshopt_generateTangents;
5051
}
5152

5253
void uninitialize_meshoptimizer_module(ModuleInitializationLevel p_level) {
@@ -61,4 +62,5 @@ void uninitialize_meshoptimizer_module(ModuleInitializationLevel p_level) {
6162
SurfaceTool::generate_remap_func = nullptr;
6263
SurfaceTool::remap_vertex_func = nullptr;
6364
SurfaceTool::remap_index_func = nullptr;
65+
SurfaceTool::generate_tangents_func = nullptr;
6466
}

scene/resources/surface_tool.cpp

Lines changed: 77 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ SurfaceTool::SimplifyScaleFunc SurfaceTool::simplify_scale_func = nullptr;
4343
SurfaceTool::GenerateRemapFunc SurfaceTool::generate_remap_func = nullptr;
4444
SurfaceTool::RemapVertexFunc SurfaceTool::remap_vertex_func = nullptr;
4545
SurfaceTool::RemapIndexFunc SurfaceTool::remap_index_func = nullptr;
46+
SurfaceTool::GenerateTangentsFunc SurfaceTool::generate_tangents_func = nullptr;
4647

4748
void SurfaceTool::strip_mesh_arrays(PackedVector3Array &r_vertices, PackedInt32Array &r_indices) {
4849
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<Mesh> &p_existing, int p_surface, const
10531054
}
10541055
}
10551056

1056-
//mikktspace callbacks
1057-
namespace {
1058-
struct TangentGenerationContextUserData {
1059-
LocalVector<SurfaceTool::Vertex> *vertices;
1060-
LocalVector<int> *indices;
1057+
struct TangentVertex {
1058+
float position[3];
1059+
float normal[3];
1060+
float uv[2];
10611061
};
1062-
} // namespace
10631062

1064-
int SurfaceTool::mikktGetNumFaces(const SMikkTSpaceContext *pContext) {
1065-
TangentGenerationContextUserData &triangle_data = *reinterpret_cast<TangentGenerationContextUserData *>(pContext->m_pUserData);
1063+
static void _propagate_tangents_or_split(LocalVector<SurfaceTool::Vertex> &r_vertex_array, LocalVector<int> &r_index_array, const float *p_tangents, bool p_split) {
1064+
// Seed each vertex with one of its corner tangents; the loop below fixes any mismatches.
1065+
for (size_t i = 0; i < r_index_array.size(); i++) {
1066+
Vector4 tangent(p_tangents[i * 4 + 0], p_tangents[i * 4 + 1], p_tangents[i * 4 + 2], p_tangents[i * 4 + 3]);
1067+
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]);
1068+
}
10661069

1067-
if (triangle_data.indices->size() > 0) {
1068-
return triangle_data.indices->size() / 3;
1069-
} else {
1070-
return triangle_data.vertices->size() / 3;
1070+
if (!p_split) {
1071+
return;
10711072
}
1072-
}
10731073

1074-
int SurfaceTool::mikktGetNumVerticesOfFace(const SMikkTSpaceContext *pContext, const int iFace) {
1075-
return 3; //always 3
1076-
}
1074+
LocalVector<uint32_t> splits;
1075+
splits.resize(r_vertex_array.size());
1076+
memset(splits.ptr(), -1, splits.size() * sizeof(uint32_t)); // ~0u means "no split copy"
10771077

1078-
void SurfaceTool::mikktGetPosition(const SMikkTSpaceContext *pContext, float fvPosOut[], const int iFace, const int iVert) {
1079-
TangentGenerationContextUserData &triangle_data = *reinterpret_cast<TangentGenerationContextUserData *>(pContext->m_pUserData);
1080-
Vector3 v;
1081-
if (triangle_data.indices->size() > 0) {
1082-
uint32_t index = triangle_data.indices->operator[](iFace * 3 + iVert);
1083-
if (index < triangle_data.vertices->size()) {
1084-
v = triangle_data.vertices->operator[](index).vertex;
1078+
for (size_t i = 0; i < r_index_array.size(); i++) {
1079+
// Walk the chain of split copies looking for a vertex whose tangent matches.
1080+
Vector4 tangent(p_tangents[i * 4 + 0], p_tangents[i * 4 + 1], p_tangents[i * 4 + 2], p_tangents[i * 4 + 3]);
1081+
uint32_t v = r_index_array[i];
1082+
while (v != ~0u && r_vertex_array[v].tangent != tangent) {
1083+
v = splits[v];
10851084
}
1086-
} else {
1087-
v = triangle_data.vertices->operator[](iFace * 3 + iVert).vertex;
1088-
}
1089-
1090-
fvPosOut[0] = v.x;
1091-
fvPosOut[1] = v.y;
1092-
fvPosOut[2] = v.z;
1093-
}
10941085

1095-
void SurfaceTool::mikktGetNormal(const SMikkTSpaceContext *pContext, float fvNormOut[], const int iFace, const int iVert) {
1096-
TangentGenerationContextUserData &triangle_data = *reinterpret_cast<TangentGenerationContextUserData *>(pContext->m_pUserData);
1097-
Vector3 v;
1098-
if (triangle_data.indices->size() > 0) {
1099-
uint32_t index = triangle_data.indices->operator[](iFace * 3 + iVert);
1100-
if (index < triangle_data.vertices->size()) {
1101-
v = triangle_data.vertices->operator[](index).normal;
1086+
// No match in chain: append a new split copy with the target tangent and chain it.
1087+
if (v == ~0u) {
1088+
v = r_vertex_array.size();
1089+
r_vertex_array.push_back(r_vertex_array[r_index_array[i]]);
1090+
r_vertex_array[v].tangent = tangent;
1091+
splits.push_back(splits[r_index_array[i]]);
1092+
splits[r_index_array[i]] = v;
11021093
}
1103-
} else {
1104-
v = triangle_data.vertices->operator[](iFace * 3 + iVert).normal;
1094+
1095+
r_index_array[i] = int(v);
11051096
}
1097+
}
11061098

1107-
fvNormOut[0] = v.x;
1108-
fvNormOut[1] = v.y;
1109-
fvNormOut[2] = v.z;
1099+
void SurfaceTool::_generate_tangents_bind() {
1100+
generate_tangents(/*split*/ false);
11101101
}
11111102

1112-
void SurfaceTool::mikktGetTexCoord(const SMikkTSpaceContext *pContext, float fvTexcOut[], const int iFace, const int iVert) {
1113-
TangentGenerationContextUserData &triangle_data = *reinterpret_cast<TangentGenerationContextUserData *>(pContext->m_pUserData);
1114-
Vector2 v;
1115-
if (triangle_data.indices->size() > 0) {
1116-
uint32_t index = triangle_data.indices->operator[](iFace * 3 + iVert);
1117-
if (index < triangle_data.vertices->size()) {
1118-
v = triangle_data.vertices->operator[](index).uv;
1119-
}
1120-
} else {
1121-
v = triangle_data.vertices->operator[](iFace * 3 + iVert).uv;
1103+
void SurfaceTool::generate_tangents(bool p_split) {
1104+
ERR_FAIL_COND_MSG(!generate_tangents_func, "Meshoptimizer library is not initialized.");
1105+
ERR_FAIL_COND_MSG(!(format & Mesh::ARRAY_FORMAT_TEX_UV), "UVs are required to generate tangents.");
1106+
ERR_FAIL_COND(!(format & Mesh::ARRAY_FORMAT_NORMAL));
1107+
ERR_FAIL_COND(primitive != Mesh::PRIMITIVE_TRIANGLES);
1108+
1109+
if (vertex_array.is_empty()) {
1110+
format |= Mesh::ARRAY_FORMAT_TANGENT;
1111+
return;
11221112
}
11231113

1124-
fvTexcOut[0] = v.x;
1125-
fvTexcOut[1] = v.y;
1126-
}
1114+
size_t corner_count = index_array.size() > 0 ? index_array.size() : vertex_array.size();
1115+
ERR_FAIL_COND(corner_count % 3 != 0);
11271116

1128-
void SurfaceTool::mikktSetTSpaceDefault(const SMikkTSpaceContext *pContext, const float fvTangent[], const float fvBiTangent[], const float fMagS, const float fMagT,
1129-
const tbool bIsOrientationPreserving, const int iFace, const int iVert) {
1130-
TangentGenerationContextUserData &triangle_data = *reinterpret_cast<TangentGenerationContextUserData *>(pContext->m_pUserData);
1131-
Vertex *vtx = nullptr;
1132-
if (triangle_data.indices->size() > 0) {
1133-
uint32_t index = triangle_data.indices->operator[](iFace * 3 + iVert);
1134-
if (index < triangle_data.vertices->size()) {
1135-
vtx = &triangle_data.vertices->operator[](index);
1136-
}
1137-
} else {
1138-
vtx = &triangle_data.vertices->operator[](iFace * 3 + iVert);
1139-
}
1117+
// We can't operate on SurfaceTool::Vertex directly because in double-precision builds, vectors use double components
1118+
// So we convert the inputs to single precision floats before generating tangents.
1119+
LocalVector<TangentVertex> tangent_vertices;
1120+
tangent_vertices.resize(vertex_array.size());
11401121

1141-
if (vtx != nullptr) {
1142-
vtx->tangent = Vector4(fvTangent[0], fvTangent[1], fvTangent[2], bIsOrientationPreserving ? 1.0f : -1.0f);
1122+
for (size_t i = 0; i < vertex_array.size(); i++) {
1123+
const Vertex &vertex = vertex_array[i];
1124+
TangentVertex &tangent_vertex = tangent_vertices[i];
1125+
1126+
tangent_vertex.position[0] = vertex.vertex.x;
1127+
tangent_vertex.position[1] = vertex.vertex.y;
1128+
tangent_vertex.position[2] = vertex.vertex.z;
1129+
tangent_vertex.normal[0] = vertex.normal.x;
1130+
tangent_vertex.normal[1] = vertex.normal.y;
1131+
tangent_vertex.normal[2] = vertex.normal.z;
1132+
tangent_vertex.uv[0] = vertex.uv.x;
1133+
tangent_vertex.uv[1] = vertex.uv.y;
11431134
}
1144-
}
11451135

1146-
void SurfaceTool::generate_tangents() {
1147-
ERR_FAIL_COND_MSG(!(format & Mesh::ARRAY_FORMAT_TEX_UV), "UVs are required to generate tangents.");
1148-
ERR_FAIL_COND(!(format & Mesh::ARRAY_FORMAT_NORMAL));
1136+
const TangentVertex *vertices = tangent_vertices.ptr();
11491137

1150-
SMikkTSpaceInterface mkif;
1151-
mkif.m_getNormal = mikktGetNormal;
1152-
mkif.m_getNumFaces = mikktGetNumFaces;
1153-
mkif.m_getNumVerticesOfFace = mikktGetNumVerticesOfFace;
1154-
mkif.m_getPosition = mikktGetPosition;
1155-
mkif.m_getTexCoord = mikktGetTexCoord;
1156-
mkif.m_setTSpace = mikktSetTSpaceDefault;
1157-
mkif.m_setTSpaceBasic = nullptr;
1138+
LocalVector<float> tangents;
1139+
tangents.resize(corner_count * 4);
11581140

1159-
SMikkTSpaceContext msc;
1160-
msc.m_pInterface = &mkif;
1141+
generate_tangents_func(tangents.ptr(),
1142+
index_array.size() > 0 ? reinterpret_cast<const unsigned int *>(index_array.ptr()) : nullptr, corner_count,
1143+
vertices->position, tangent_vertices.size(), sizeof(TangentVertex),
1144+
vertices->normal, sizeof(TangentVertex),
1145+
vertices->uv, sizeof(TangentVertex), 0);
11611146

1162-
TangentGenerationContextUserData triangle_data;
1163-
triangle_data.vertices = &vertex_array;
1164-
for (Vertex &vertex : vertex_array) {
1165-
vertex.tangent = Vector4();
1147+
if (index_array.size() > 0) {
1148+
_propagate_tangents_or_split(vertex_array, index_array, tangents.ptr(), p_split);
1149+
} else {
1150+
for (size_t i = 0; i < corner_count; i++) {
1151+
Vector4 tangent(tangents[i * 4 + 0], tangents[i * 4 + 1], tangents[i * 4 + 2], tangents[i * 4 + 3]);
1152+
vertex_array[i].tangent = tangent;
1153+
}
11661154
}
1167-
triangle_data.indices = &index_array;
1168-
msc.m_pUserData = &triangle_data;
1169-
1170-
bool res = genTangSpaceDefault(&msc);
11711155

1172-
ERR_FAIL_COND(!res);
11731156
format |= Mesh::ARRAY_FORMAT_TANGENT;
11741157
}
11751158

@@ -1355,7 +1338,7 @@ void SurfaceTool::_bind_methods() {
13551338
ClassDB::bind_method(D_METHOD("index"), &SurfaceTool::index);
13561339
ClassDB::bind_method(D_METHOD("deindex"), &SurfaceTool::deindex);
13571340
ClassDB::bind_method(D_METHOD("generate_normals", "flip"), &SurfaceTool::generate_normals, DEFVAL(false));
1358-
ClassDB::bind_method(D_METHOD("generate_tangents"), &SurfaceTool::generate_tangents);
1341+
ClassDB::bind_method(D_METHOD("generate_tangents"), &SurfaceTool::_generate_tangents_bind);
13591342

13601343
ClassDB::bind_method(D_METHOD("optimize_indices_for_cache"), &SurfaceTool::optimize_indices_for_cache);
13611344

scene/resources/surface_tool.h

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,6 @@
3535
#include "scene/resources/mesh.h"
3636
#include "servers/rendering/rendering_server_enums.h"
3737

38-
#include <thirdparty/misc/mikktspace.h>
39-
4038
class SurfaceTool : public RefCounted {
4139
GDCLASS(SurfaceTool, RefCounted);
4240

@@ -95,6 +93,8 @@ class SurfaceTool : public RefCounted {
9593
SIMPLIFY_REGULARIZE = 1 << 4, // From meshopt_SimplifyRegularize
9694
/* Allow collapses across attribute discontinuities, except for vertices that are tagged with 0x02 in vertex_lock. */
9795
SIMPLIFY_PERMISSIVE = 1 << 5, // From meshopt_SimplifyPermissive
96+
/* Produce tangents compatible with MikkTSpace (same weighting and fallbacks) at the cost of reduced quality. Not recommended unless normal maps are baked. */
97+
TANGENT_COMPATIBLE = 1 << 0, // From meshopt_TangentCompatible
9898
};
9999

100100
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 {
113113
static RemapVertexFunc remap_vertex_func;
114114
typedef void (*RemapIndexFunc)(unsigned int *destination, const unsigned int *indices, size_t index_count, const unsigned int *remap);
115115
static RemapIndexFunc remap_index_func;
116+
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);
117+
static GenerateTangentsFunc generate_tangents_func;
116118
static void strip_mesh_arrays(PackedVector3Array &r_vertices, PackedInt32Array &r_indices);
117119

118120
private:
@@ -176,17 +178,10 @@ class SurfaceTool : public RefCounted {
176178
void _create_list_from_arrays(Array arr, LocalVector<Vertex> *r_vertex, LocalVector<int> *r_index, uint64_t &lformat);
177179
void _create_list(const Ref<Mesh> &p_existing, int p_surface, LocalVector<Vertex> *r_vertex, LocalVector<int> *r_index, uint64_t &lformat);
178180

179-
//mikktspace callbacks
180-
static int mikktGetNumFaces(const SMikkTSpaceContext *pContext);
181-
static int mikktGetNumVerticesOfFace(const SMikkTSpaceContext *pContext, const int iFace);
182-
static void mikktGetPosition(const SMikkTSpaceContext *pContext, float fvPosOut[], const int iFace, const int iVert);
183-
static void mikktGetNormal(const SMikkTSpaceContext *pContext, float fvNormOut[], const int iFace, const int iVert);
184-
static void mikktGetTexCoord(const SMikkTSpaceContext *pContext, float fvTexcOut[], const int iFace, const int iVert);
185-
static void mikktSetTSpaceDefault(const SMikkTSpaceContext *pContext, const float fvTangent[], const float fvBiTangent[], const float fMagS, const float fMagT,
186-
const tbool bIsOrientationPreserving, const int iFace, const int iVert);
187-
188181
void _add_triangle_fan(const Vector<Vector3> &p_vertices, const Vector<Vector2> &p_uvs = Vector<Vector2>(), const Vector<Color> &p_colors = Vector<Color>(), const Vector<Vector2> &p_uv2s = Vector<Vector2>(), const Vector<Vector3> &p_normals = Vector<Vector3>(), const TypedArray<Plane> &p_tangents = TypedArray<Plane>());
189182

183+
void _generate_tangents_bind();
184+
190185
protected:
191186
static void _bind_methods();
192187

@@ -220,7 +215,7 @@ class SurfaceTool : public RefCounted {
220215
void index();
221216
void deindex();
222217
void generate_normals(bool p_flip = false);
223-
void generate_tangents();
218+
void generate_tangents(bool p_split = false);
224219

225220
void optimize_indices_for_cache();
226221
AABB get_aabb() const;

0 commit comments

Comments
 (0)