meshunreal-engine5assimp

Converting local space normals into world space


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);
        }
    }

Solution

  • 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/