Skip to content

Commit

Permalink
Improve how tangents are generated.
Browse files Browse the repository at this point in the history
* Replace Assimp's and MikuMikuModel's tangent generator.
* Fix problem where certain vertices with NaN tangents would make screen go black in game.
  • Loading branch information
blueskythlikesclouds committed Aug 21, 2020
1 parent db8fed4 commit e90e243
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 67 deletions.
50 changes: 50 additions & 0 deletions MikuMikuLibrary/Objects/Mesh.cs
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,56 @@ public void SetColorsChannel( int index, Color[] colors )
}
}

public void GenerateTangents()
{
if ( Positions == null || Normals == null || TexCoords0 == null )
return;

var tangents = new Vector3[ Positions.Length ];
var bitangents = new Vector3[ Positions.Length ];

foreach ( var subMesh in SubMeshes )
{
foreach ( var triangle in subMesh.GetTriangles() )
{
var positionA = Positions[ triangle.C ] - Positions[ triangle.A ];
var positionB = Positions[ triangle.B ] - Positions[ triangle.A ];

var texCoordA = TexCoords0[ triangle.C ] - TexCoords0[ triangle.A ];
var texCoordB = TexCoords0[ triangle.B ] - TexCoords0[ triangle.A ];

float direction = texCoordA.X * texCoordB.Y - texCoordA.Y * texCoordB.X > 0.0f ? 1.0f : -1.0f;

var tangent = ( positionA * texCoordB.Y - positionB * texCoordA.Y ) * direction;
var bitangent = ( positionB * texCoordA.X - positionA * texCoordB.X ) * direction;

tangents[ triangle.A ] += tangent;
tangents[ triangle.B ] += tangent;
tangents[ triangle.C ] += tangent;

bitangents[ triangle.A ] += bitangent;
bitangents[ triangle.B ] += bitangent;
bitangents[ triangle.C ] += bitangent;
}
}

Tangents = new Vector4[ Positions.Length ];

for ( int i = 0; i < tangents.Length; i++ )
{
var normal = Normals[ i ];

var tangent = Vector3.Normalize( tangents[ i ] );
var bitangent = Vector3.Normalize( bitangents[ i ] );

tangent = Vector3.Normalize( tangent - normal * Vector3.Dot( tangent, normal ) );
bitangent = Vector3.Normalize( bitangent - normal * Vector3.Dot( bitangent, normal ) );

float directionCheck = Vector3.Dot( Vector3.Normalize( Vector3.Cross( normal, tangent ) ), bitangent );
Tangents[ i ] = new Vector4( tangent, directionCheck > 0.0f ? 1.0f : -1.0f );
}
}

internal void Read( EndianBinaryReader reader, ObjectSection section = null )
{
reader.SeekCurrent( 4 ); // Unused flags
Expand Down
20 changes: 2 additions & 18 deletions MikuMikuLibrary/Objects/Processing/Assimp/AssimpImporter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -190,24 +190,6 @@ private static List<Mesh> CreateMeshesFromAiNode( Ai.Node aiNode, Ai.Scene aiSce
mesh.Normals[ vertexOffset + i ] = Vector3.Normalize( Vector3.TransformNormal( aiMesh.Normals[ i ].ToNumerics(), transform ) );
}

if ( aiMesh.HasTangentBasis && aiMesh.HasNormals )
{
if ( mesh.Tangents == null )
mesh.Tangents = new Vector4[ vertexCount ];

for ( int i = 0; i < aiMesh.VertexCount; i++ )
{
var normal = mesh.Normals[ vertexOffset + i ];
var tangent = Vector3.Normalize( Vector3.TransformNormal( aiMesh.Tangents[ i ].ToNumerics(), transform ) );
var bitangent = Vector3.Normalize( Vector3.TransformNormal( aiMesh.BiTangents[ i ].ToNumerics(), transform ) );

var cross = Vector3.Normalize( Vector3.Cross( normal, tangent ) );
float dot = Vector3.Dot( cross, bitangent );

mesh.Tangents[ vertexOffset + i ] = new Vector4( tangent, dot > 0.0f ? -1.0f : 1.0f );
}
}

for ( int i = 0; i < 4; i++ )
{
if ( !aiMesh.HasTextureCoords( i ) )
Expand Down Expand Up @@ -331,6 +313,8 @@ private static List<Mesh> CreateMeshesFromAiNode( Ai.Node aiNode, Ai.Scene aiSce
mesh.BoneWeights[ i ].Validate();
}

mesh.GenerateTangents();

mesh.BoundingSphere = aabbMesh.ToBoundingSphere();
meshes.Add( mesh );

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,10 @@ public static Ai.Scene Import( string filePath )
aiContext.SetConfig( new VertexCacheSizeConfig( 63 ) );

return aiContext.ImportFile( filePath,
Ai.PostProcessSteps.CalculateTangentSpace | Ai.PostProcessSteps.JoinIdenticalVertices |
Ai.PostProcessSteps.Triangulate | Ai.PostProcessSteps.SplitLargeMeshes |
Ai.PostProcessSteps.LimitBoneWeights | Ai.PostProcessSteps.ImproveCacheLocality |
Ai.PostProcessSteps.SortByPrimitiveType | Ai.PostProcessSteps.SplitByBoneCount |
Ai.PostProcessSteps.FlipUVs );
Ai.PostProcessSteps.JoinIdenticalVertices | Ai.PostProcessSteps.Triangulate |
Ai.PostProcessSteps.SplitLargeMeshes | Ai.PostProcessSteps.LimitBoneWeights |
Ai.PostProcessSteps.ImproveCacheLocality | Ai.PostProcessSteps.SortByPrimitiveType |
Ai.PostProcessSteps.SplitByBoneCount | Ai.PostProcessSteps.FlipUVs );
}

public static void Export( Ai.Scene aiScene, string filePath,
Expand Down
45 changes: 1 addition & 44 deletions MikuMikuModel/Nodes/Objects/ObjectSetNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -484,50 +484,7 @@ protected override void Initialize()
AddDirtyCustomHandler( "Generate tangents", () =>
{
foreach ( var mesh in Data.Objects.SelectMany( x => x.Meshes ) )
{
if ( mesh.Positions == null || mesh.Normals == null || mesh.TexCoords0 == null )
continue;

var tangents = new Vector3[ mesh.Positions.Length ];
var bitangents = new Vector3[ mesh.Positions.Length ];

foreach ( var subMesh in mesh.SubMeshes )
{
foreach ( var triangle in subMesh.GetTriangles() )
{
var e1 = mesh.Positions[ triangle.C ] - mesh.Positions[ triangle.A ];
var e2 = mesh.Positions[ triangle.B ] - mesh.Positions[ triangle.A ];

var uv1 = mesh.TexCoords0[ triangle.C ] - mesh.TexCoords0[ triangle.A ];
var uv2 = mesh.TexCoords0[ triangle.B ] - mesh.TexCoords0[ triangle.A ];

float r = 1.0f / ( uv1.X * uv2.Y - uv1.Y * uv2.X );
var tangent = ( e1 * uv2.Y - e2 * uv1.Y ) * r;
var bitangent = ( e2 * uv1.X - e1 * uv2.X ) * r;

tangents[ triangle.A ] += tangent;
tangents[ triangle.B ] += tangent;
tangents[ triangle.C ] += tangent;

bitangents[ triangle.A ] += bitangent;
bitangents[ triangle.B ] += bitangent;
bitangents[ triangle.C ] += bitangent;
}
}

mesh.Tangents = new Vector4[ mesh.Positions.Length ];

for ( int i = 0; i < tangents.Length; i++ )
{
var tangent = Vector3.Normalize( tangents[ i ] );
var bitangent = Vector3.Normalize( bitangents[ i ] );

var cross = Vector3.Normalize( Vector3.Cross( mesh.Normals[ i ], tangent ) );
float dot = Vector3.Dot( cross, bitangent );

mesh.Tangents[ i ] = new Vector4( tangent, dot > 0.0f ? 1.0f : -1.0f );
}
}
mesh.GenerateTangents();

return true;
} );
Expand Down

0 comments on commit e90e243

Please sign in to comment.