Skip to content

Commit ba31219

Browse files
authored
Merge pull request #4937 from Bigfoot71/fix-gen-tangents
[rmodels] Fix and improve `GenMeshTangents`
2 parents 5076d57 + d135eef commit ba31219

File tree

1 file changed

+96
-41
lines changed

1 file changed

+96
-41
lines changed

src/rmodels.c

Lines changed: 96 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -3608,109 +3608,164 @@ BoundingBox GetMeshBoundingBox(Mesh mesh)
36083608
}
36093609

36103610
// Compute mesh tangents
3611-
// NOTE: To calculate mesh tangents and binormals we need mesh vertex positions and texture coordinates
3612-
// Implementation based on: https://answers.unity.com/questions/7789/calculating-tangents-vector4.html
36133611
void GenMeshTangents(Mesh *mesh)
36143612
{
3615-
if ((mesh->vertices == NULL) || (mesh->texcoords == NULL))
3613+
// Check if input mesh data is useful
3614+
if ((mesh == NULL) || (mesh->vertices == NULL) || (mesh->texcoords == NULL) || (mesh->normals == NULL))
36163615
{
3617-
TRACELOG(LOG_WARNING, "MESH: Tangents generation requires texcoord vertex attribute data");
3616+
TRACELOG(LOG_WARNING, "MESH: Tangents generation requires vertices, texcoords and normals vertex attribute data");
36183617
return;
36193618
}
36203619

3620+
// Allocate or reallocate tangents data
36213621
if (mesh->tangents == NULL) mesh->tangents = (float *)RL_MALLOC(mesh->vertexCount*4*sizeof(float));
36223622
else
36233623
{
36243624
RL_FREE(mesh->tangents);
36253625
mesh->tangents = (float *)RL_MALLOC(mesh->vertexCount*4*sizeof(float));
36263626
}
36273627

3628-
Vector3 *tan1 = (Vector3 *)RL_MALLOC(mesh->vertexCount*sizeof(Vector3));
3629-
Vector3 *tan2 = (Vector3 *)RL_MALLOC(mesh->vertexCount*sizeof(Vector3));
3628+
// Allocate temporary arrays for tangents calculation
3629+
Vector3 *tan1 = (Vector3 *)RL_CALLOC(mesh->vertexCount, sizeof(Vector3));
3630+
Vector3 *tan2 = (Vector3 *)RL_CALLOC(mesh->vertexCount, sizeof(Vector3));
36303631

3631-
if (mesh->vertexCount % 3 != 0)
3632+
if (tan1 == NULL || tan2 == NULL)
36323633
{
3633-
TRACELOG(LOG_WARNING, "MESH: vertexCount expected to be a multiple of 3. Expect uninitialized values.");
3634+
TRACELOG(LOG_WARNING, "MESH: Failed to allocate temporary memory for tangent calculation");
3635+
if (tan1) RL_FREE(tan1);
3636+
if (tan2) RL_FREE(tan2);
3637+
return;
36343638
}
36353639

3636-
for (int i = 0; i <= mesh->vertexCount - 3; i += 3)
3640+
// Process all triangles of the mesh
3641+
// 'triangleCount' must be always valid
3642+
for (int t = 0; t < mesh->triangleCount; t++)
36373643
{
3638-
// Get triangle vertices
3639-
Vector3 v1 = { mesh->vertices[(i + 0)*3 + 0], mesh->vertices[(i + 0)*3 + 1], mesh->vertices[(i + 0)*3 + 2] };
3640-
Vector3 v2 = { mesh->vertices[(i + 1)*3 + 0], mesh->vertices[(i + 1)*3 + 1], mesh->vertices[(i + 1)*3 + 2] };
3641-
Vector3 v3 = { mesh->vertices[(i + 2)*3 + 0], mesh->vertices[(i + 2)*3 + 1], mesh->vertices[(i + 2)*3 + 2] };
3644+
// Get triangle vertex indices
3645+
int i0, i1, i2;
3646+
3647+
if (mesh->indices != NULL)
3648+
{
3649+
// Use indices if available
3650+
i0 = mesh->indices[t*3 + 0];
3651+
i1 = mesh->indices[t*3 + 1];
3652+
i2 = mesh->indices[t*3 + 2];
3653+
}
3654+
else
3655+
{
3656+
// Sequential access for non-indexed mesh
3657+
i0 = t*3 + 0;
3658+
i1 = t*3 + 1;
3659+
i2 = t*3 + 2;
3660+
}
3661+
3662+
// Get triangle vertices position
3663+
Vector3 v1 = { mesh->vertices[i0*3 + 0], mesh->vertices[i0*3 + 1], mesh->vertices[i0*3 + 2] };
3664+
Vector3 v2 = { mesh->vertices[i1*3 + 0], mesh->vertices[i1*3 + 1], mesh->vertices[i1*3 + 2] };
3665+
Vector3 v3 = { mesh->vertices[i2*3 + 0], mesh->vertices[i2*3 + 1], mesh->vertices[i2*3 + 2] };
36423666

36433667
// Get triangle texcoords
3644-
Vector2 uv1 = { mesh->texcoords[(i + 0)*2 + 0], mesh->texcoords[(i + 0)*2 + 1] };
3645-
Vector2 uv2 = { mesh->texcoords[(i + 1)*2 + 0], mesh->texcoords[(i + 1)*2 + 1] };
3646-
Vector2 uv3 = { mesh->texcoords[(i + 2)*2 + 0], mesh->texcoords[(i + 2)*2 + 1] };
3668+
Vector2 uv1 = { mesh->texcoords[i0*2 + 0], mesh->texcoords[i0*2 + 1] };
3669+
Vector2 uv2 = { mesh->texcoords[i1*2 + 0], mesh->texcoords[i1*2 + 1] };
3670+
Vector2 uv3 = { mesh->texcoords[i2*2 + 0], mesh->texcoords[i2*2 + 1] };
36473671

3672+
// Calculate triangle edges
36483673
float x1 = v2.x - v1.x;
36493674
float y1 = v2.y - v1.y;
36503675
float z1 = v2.z - v1.z;
36513676
float x2 = v3.x - v1.x;
36523677
float y2 = v3.y - v1.y;
36533678
float z2 = v3.z - v1.z;
36543679

3680+
// Calculate texture coordinate differences
36553681
float s1 = uv2.x - uv1.x;
36563682
float t1 = uv2.y - uv1.y;
36573683
float s2 = uv3.x - uv1.x;
36583684
float t2 = uv3.y - uv1.y;
36593685

3686+
// Calculate denominator and check for degenerate UV
36603687
float div = s1*t2 - s2*t1;
3661-
float r = (div == 0.0f)? 0.0f : 1.0f/div;
3688+
float r = (fabsf(div) < 0.0001f)? 0.0f : 1.0f/div;
36623689

3690+
// Calculate tangent and bitangent directions
36633691
Vector3 sdir = { (t2*x1 - t1*x2)*r, (t2*y1 - t1*y2)*r, (t2*z1 - t1*z2)*r };
36643692
Vector3 tdir = { (s1*x2 - s2*x1)*r, (s1*y2 - s2*y1)*r, (s1*z2 - s2*z1)*r };
36653693

3666-
tan1[i + 0] = sdir;
3667-
tan1[i + 1] = sdir;
3668-
tan1[i + 2] = sdir;
3694+
// Accumulate tangents and bitangents for each vertex of the triangle
3695+
tan1[i0] = Vector3Add(tan1[i0], sdir);
3696+
tan1[i1] = Vector3Add(tan1[i1], sdir);
3697+
tan1[i2] = Vector3Add(tan1[i2], sdir);
36693698

3670-
tan2[i + 0] = tdir;
3671-
tan2[i + 1] = tdir;
3672-
tan2[i + 2] = tdir;
3699+
tan2[i0] = Vector3Add(tan2[i0], tdir);
3700+
tan2[i1] = Vector3Add(tan2[i1], tdir);
3701+
tan2[i2] = Vector3Add(tan2[i2], tdir);
36733702
}
36743703

3675-
// Compute tangents considering normals
3704+
// Calculate final tangents for each vertex
36763705
for (int i = 0; i < mesh->vertexCount; i++)
36773706
{
36783707
Vector3 normal = { mesh->normals[i*3 + 0], mesh->normals[i*3 + 1], mesh->normals[i*3 + 2] };
36793708
Vector3 tangent = tan1[i];
36803709

3681-
// TODO: Review, not sure if tangent computation is right, just used reference proposed maths...
3682-
#if defined(COMPUTE_TANGENTS_METHOD_01)
3683-
Vector3 tmp = Vector3Subtract(tangent, Vector3Scale(normal, Vector3DotProduct(normal, tangent)));
3684-
tmp = Vector3Normalize(tmp);
3685-
mesh->tangents[i*4 + 0] = tmp.x;
3686-
mesh->tangents[i*4 + 1] = tmp.y;
3687-
mesh->tangents[i*4 + 2] = tmp.z;
3688-
mesh->tangents[i*4 + 3] = 1.0f;
3689-
#else
3690-
Vector3OrthoNormalize(&normal, &tangent);
3691-
mesh->tangents[i*4 + 0] = tangent.x;
3692-
mesh->tangents[i*4 + 1] = tangent.y;
3693-
mesh->tangents[i*4 + 2] = tangent.z;
3694-
mesh->tangents[i*4 + 3] = (Vector3DotProduct(Vector3CrossProduct(normal, tangent), tan2[i]) < 0.0f)? -1.0f : 1.0f;
3695-
#endif
3710+
// Handle zero tangent (can happen with degenerate UVs)
3711+
if (Vector3Length(tangent) < 0.0001f)
3712+
{
3713+
// Create a tangent perpendicular to the normal
3714+
if (fabsf(normal.z) > 0.707f) tangent = (Vector3){ 1.0f, 0.0f, 0.0f };
3715+
else tangent = Vector3Normalize((Vector3){ -normal.y, normal.x, 0.0f });
3716+
3717+
mesh->tangents[i*4 + 0] = tangent.x;
3718+
mesh->tangents[i*4 + 1] = tangent.y;
3719+
mesh->tangents[i*4 + 2] = tangent.z;
3720+
mesh->tangents[i*4 + 3] = 1.0f;
3721+
continue;
3722+
}
3723+
3724+
// Gram-Schmidt orthogonalization to make tangent orthogonal to normal
3725+
// T_prime = T - N * dot(N, T)
3726+
Vector3 orthogonalized = Vector3Subtract(tangent, Vector3Scale(normal, Vector3DotProduct(normal, tangent)));
3727+
3728+
// Handle cases where orthogonalized vector is too small
3729+
if (Vector3Length(orthogonalized) < 0.0001f)
3730+
{
3731+
// Create a tangent perpendicular to the normal
3732+
if (fabsf(normal.z) > 0.707f) orthogonalized = (Vector3){ 1.0f, 0.0f, 0.0f };
3733+
else orthogonalized = Vector3Normalize((Vector3){ -normal.y, normal.x, 0.0f });
3734+
}
3735+
else
3736+
{
3737+
// Normalize the orthogonalized tangent
3738+
orthogonalized = Vector3Normalize(orthogonalized);
3739+
}
3740+
3741+
// Store the calculated tangent
3742+
mesh->tangents[i*4 + 0] = orthogonalized.x;
3743+
mesh->tangents[i*4 + 1] = orthogonalized.y;
3744+
mesh->tangents[i*4 + 2] = orthogonalized.z;
3745+
3746+
// Calculate the handedness (w component)
3747+
mesh->tangents[i*4 + 3] = (Vector3DotProduct(Vector3CrossProduct(normal, orthogonalized), tan2[i]) < 0.0f)? -1.0f : 1.0f;
36963748
}
36973749

3750+
// Free temporary arrays
36983751
RL_FREE(tan1);
36993752
RL_FREE(tan2);
37003753

3754+
// Update vertex buffers if available
37013755
if (mesh->vboId != NULL)
37023756
{
37033757
if (mesh->vboId[SHADER_LOC_VERTEX_TANGENT] != 0)
37043758
{
3705-
// Update existing vertex buffer
3759+
// Update existing tangent vertex buffer
37063760
rlUpdateVertexBuffer(mesh->vboId[SHADER_LOC_VERTEX_TANGENT], mesh->tangents, mesh->vertexCount*4*sizeof(float), 0);
37073761
}
37083762
else
37093763
{
3710-
// Load a new tangent attributes buffer
3764+
// Create new tangent vertex buffer
37113765
mesh->vboId[SHADER_LOC_VERTEX_TANGENT] = rlLoadVertexBuffer(mesh->tangents, mesh->vertexCount*4*sizeof(float), false);
37123766
}
37133767

3768+
// Set up vertex attributes for shader
37143769
rlEnableVertexArray(mesh->vaoId);
37153770
rlSetVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT, 4, RL_FLOAT, 0, 0, 0);
37163771
rlEnableVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT);

0 commit comments

Comments
 (0)