I need to convert a local space mesh into global space. The resulting normal vectors don't seem to be correct.
I got the world transform matrix for the vertex (Which works, because the position of the vertex is correct). Then I get the transform matrix for the normals by getting the transposed matrix of the inverse matrix of the world transform matrix. Which I use for transforming the local space normal vector.
Which results in: Calculation Result
But the result should be more like this: Expected Result
Used code for the current result (Look where the "// TODO: Fix normals (Buggy)" is)
FMeshData &MeshData = ModelData.Meshes[MeshIndex];
aiMesh *Mesh = Scene->mMeshes[MeshIndex];
aiNode *Node = GetParentNode(Scene->mRootNode, MeshIndex);
// Get world transform
FMatrix PositionMatrix = GetWorldTransformOfNode(Node);
FMatrix NormalMatrix = UKismetMathLibrary::Matrix_GetTransposed(PositionMatrix.Inverse());
// Material id
MeshData.MaterialId = Mesh->mMaterialIndex;
// Lod Data
GetLodData(LodFilePath, FString(Mesh->mName.C_Str()), MeshData.LodData);
// Vertices
for (uint32 VertexIndex = 0; VertexIndex < Mesh->mNumVertices; VertexIndex++)
{
// Position
aiVector3D &aiVertex = Mesh->mVertices[VertexIndex];
FVector PositionVertex = PositionMatrix.TransformFVector4(FVector(
aiVertex.x,
aiVertex.y,
aiVertex.z));
FVector3f Position(
PositionVertex.X,
PositionVertex.Y,
PositionVertex.Z);
// Normal
FVector3f Normal = FVector3f::ZeroVector;
if (Mesh->HasNormals())
{
//TODO: Fix normals (Buggy)
aiVector3D &aiNormal = Mesh->mNormals[VertexIndex];
FVector NormalVector = NormalMatrix.TransformFVector4(FVector(
aiNormal.x,
aiNormal.y,
aiNormal.z));
Normal = FVector3f(
NormalVector.X,
NormalVector.Y,
NormalVector.Z);
}
// Tangent
FVector3f Tangent = FVector3f::ZeroVector;
if (Mesh->HasTangentsAndBitangents())
{
aiVector3D &aiTangent = Mesh->mTangents[VertexIndex];
Tangent = FVector3f(
aiTangent.x,
aiTangent.y,
aiTangent.z);
}
// Linear Color
FLinearColor LinearColor = FLinearColor::White;
if (Mesh->HasVertexColors(0))
{
aiColor4D &aiColor = Mesh->mColors[0][VertexIndex];
LinearColor = FLinearColor(
aiColor.r,
aiColor.g,
aiColor.b,
aiColor.a);
}
// UVs
FVector2f UV0 = FVector2f::ZeroVector;
FVector2f UV1 = FVector2f::ZeroVector;
FVector2f UV2 = FVector2f::ZeroVector;
FVector2f UV3 = FVector2f::ZeroVector;
for (uint32 ChannelIndex = 0; ChannelIndex <= 3; ChannelIndex++)
{
if (Mesh->HasTextureCoords(ChannelIndex))
{
aiVector3D &aiCoordinate = Mesh->mTextureCoords[ChannelIndex][VertexIndex];
FVector2f Coordinate(aiCoordinate.x, -aiCoordinate.y);
switch (ChannelIndex)
{
case 0:
UV0 = Coordinate;
break;
case 1:
UV1 = Coordinate;
break;
case 2:
UV2 = Coordinate;
break;
case 3:
UV3 = Coordinate;
break;
}
}
}
// Create vertex
FVertexData Vertex;
Vertex.Position = Position;
Vertex.Normal = Normal;
Vertex.Tangent = Tangent;
Vertex.Color = LinearColor;
Vertex.UV0 = UV0;
Vertex.UV1 = UV1;
Vertex.UV2 = UV2;
Vertex.UV3 = UV3;
// Save vertex
MeshData.Verticies.Push(Vertex);
}
}
Normal matrix is the transpose of the inverse of the rotation part of the transformation matrix. It seems like you are doing it on the transformation matrix which also has the position.
Technically, you only need to invert the rotation matrix because the normal vectors are all directional and thus unit vectors. The reason we use the transpose of the inverse of the rotation part is just to get rid of any scale. See this link for a refresher on the math behind it: http://www.lighthouse3d.com/tutorials/glsl-12-tutorial/the-normal-matrix/