From e90e2430a1ba4b9edb8ce2ba6130050eb3a5232b Mon Sep 17 00:00:00 2001 From: Skyth <19259897+blueskythlikesclouds@users.noreply.github.com> Date: Fri, 21 Aug 2020 12:23:04 +0300 Subject: [PATCH] Improve how tangents are generated. * Replace Assimp's and MikuMikuModel's tangent generator. * Fix problem where certain vertices with NaN tangents would make screen go black in game. --- MikuMikuLibrary/Objects/Mesh.cs | 50 +++++++++++++++++++ .../Processing/Assimp/AssimpImporter.cs | 20 +------- .../Processing/Assimp/AssimpSceneHelper.cs | 9 ++-- MikuMikuModel/Nodes/Objects/ObjectSetNode.cs | 45 +---------------- 4 files changed, 57 insertions(+), 67 deletions(-) diff --git a/MikuMikuLibrary/Objects/Mesh.cs b/MikuMikuLibrary/Objects/Mesh.cs index 3f57079a..00c7f50f 100644 --- a/MikuMikuLibrary/Objects/Mesh.cs +++ b/MikuMikuLibrary/Objects/Mesh.cs @@ -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 diff --git a/MikuMikuLibrary/Objects/Processing/Assimp/AssimpImporter.cs b/MikuMikuLibrary/Objects/Processing/Assimp/AssimpImporter.cs index 0340776b..b70db95a 100644 --- a/MikuMikuLibrary/Objects/Processing/Assimp/AssimpImporter.cs +++ b/MikuMikuLibrary/Objects/Processing/Assimp/AssimpImporter.cs @@ -190,24 +190,6 @@ private static List 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 ) ) @@ -331,6 +313,8 @@ private static List CreateMeshesFromAiNode( Ai.Node aiNode, Ai.Scene aiSce mesh.BoneWeights[ i ].Validate(); } + mesh.GenerateTangents(); + mesh.BoundingSphere = aabbMesh.ToBoundingSphere(); meshes.Add( mesh ); diff --git a/MikuMikuLibrary/Objects/Processing/Assimp/AssimpSceneHelper.cs b/MikuMikuLibrary/Objects/Processing/Assimp/AssimpSceneHelper.cs index 231b4803..841db460 100644 --- a/MikuMikuLibrary/Objects/Processing/Assimp/AssimpSceneHelper.cs +++ b/MikuMikuLibrary/Objects/Processing/Assimp/AssimpSceneHelper.cs @@ -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, diff --git a/MikuMikuModel/Nodes/Objects/ObjectSetNode.cs b/MikuMikuModel/Nodes/Objects/ObjectSetNode.cs index 779010af..4d39e2b3 100644 --- a/MikuMikuModel/Nodes/Objects/ObjectSetNode.cs +++ b/MikuMikuModel/Nodes/Objects/ObjectSetNode.cs @@ -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; } );