@@ -3608,109 +3608,164 @@ BoundingBox GetMeshBoundingBox(Mesh mesh)
3608
3608
}
3609
3609
3610
3610
// 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
3613
3611
void GenMeshTangents (Mesh * mesh )
3614
3612
{
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 ))
3616
3615
{
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" );
3618
3617
return ;
3619
3618
}
3620
3619
3620
+ // Allocate or reallocate tangents data
3621
3621
if (mesh -> tangents == NULL ) mesh -> tangents = (float * )RL_MALLOC (mesh -> vertexCount * 4 * sizeof (float ));
3622
3622
else
3623
3623
{
3624
3624
RL_FREE (mesh -> tangents );
3625
3625
mesh -> tangents = (float * )RL_MALLOC (mesh -> vertexCount * 4 * sizeof (float ));
3626
3626
}
3627
3627
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 ));
3630
3631
3631
- if (mesh -> vertexCount % 3 != 0 )
3632
+ if (tan1 == NULL || tan2 == NULL )
3632
3633
{
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 ;
3634
3638
}
3635
3639
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 ++ )
3637
3643
{
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 ] };
3642
3666
3643
3667
// 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 ] };
3647
3671
3672
+ // Calculate triangle edges
3648
3673
float x1 = v2 .x - v1 .x ;
3649
3674
float y1 = v2 .y - v1 .y ;
3650
3675
float z1 = v2 .z - v1 .z ;
3651
3676
float x2 = v3 .x - v1 .x ;
3652
3677
float y2 = v3 .y - v1 .y ;
3653
3678
float z2 = v3 .z - v1 .z ;
3654
3679
3680
+ // Calculate texture coordinate differences
3655
3681
float s1 = uv2 .x - uv1 .x ;
3656
3682
float t1 = uv2 .y - uv1 .y ;
3657
3683
float s2 = uv3 .x - uv1 .x ;
3658
3684
float t2 = uv3 .y - uv1 .y ;
3659
3685
3686
+ // Calculate denominator and check for degenerate UV
3660
3687
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 ;
3662
3689
3690
+ // Calculate tangent and bitangent directions
3663
3691
Vector3 sdir = { (t2 * x1 - t1 * x2 )* r , (t2 * y1 - t1 * y2 )* r , (t2 * z1 - t1 * z2 )* r };
3664
3692
Vector3 tdir = { (s1 * x2 - s2 * x1 )* r , (s1 * y2 - s2 * y1 )* r , (s1 * z2 - s2 * z1 )* r };
3665
3693
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 );
3669
3698
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 ) ;
3673
3702
}
3674
3703
3675
- // Compute tangents considering normals
3704
+ // Calculate final tangents for each vertex
3676
3705
for (int i = 0 ; i < mesh -> vertexCount ; i ++ )
3677
3706
{
3678
3707
Vector3 normal = { mesh -> normals [i * 3 + 0 ], mesh -> normals [i * 3 + 1 ], mesh -> normals [i * 3 + 2 ] };
3679
3708
Vector3 tangent = tan1 [i ];
3680
3709
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 ;
3696
3748
}
3697
3749
3750
+ // Free temporary arrays
3698
3751
RL_FREE (tan1 );
3699
3752
RL_FREE (tan2 );
3700
3753
3754
+ // Update vertex buffers if available
3701
3755
if (mesh -> vboId != NULL )
3702
3756
{
3703
3757
if (mesh -> vboId [SHADER_LOC_VERTEX_TANGENT ] != 0 )
3704
3758
{
3705
- // Update existing vertex buffer
3759
+ // Update existing tangent vertex buffer
3706
3760
rlUpdateVertexBuffer (mesh -> vboId [SHADER_LOC_VERTEX_TANGENT ], mesh -> tangents , mesh -> vertexCount * 4 * sizeof (float ), 0 );
3707
3761
}
3708
3762
else
3709
3763
{
3710
- // Load a new tangent attributes buffer
3764
+ // Create new tangent vertex buffer
3711
3765
mesh -> vboId [SHADER_LOC_VERTEX_TANGENT ] = rlLoadVertexBuffer (mesh -> tangents , mesh -> vertexCount * 4 * sizeof (float ), false);
3712
3766
}
3713
3767
3768
+ // Set up vertex attributes for shader
3714
3769
rlEnableVertexArray (mesh -> vaoId );
3715
3770
rlSetVertexAttribute (RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT , 4 , RL_FLOAT , 0 , 0 , 0 );
3716
3771
rlEnableVertexAttribute (RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT );
0 commit comments