Vertex Normal Rotation Alignment Tool v1.0 [Maya Script]

:sunglasses: Vertex Normal Rotation Alignment Tool v1.0 [Maya Script]:beers:
This script will set the vertex normal rotations to the same value as the vertex normal rotations of another object (2 separate skeleton meshes) if the vertex are in the same position space. All the vertex of the 2nd object that matched will be selected to show which vertex was aligned.

Select 2 objects first being the object you want the vertex normal rotations to change and execute the script.

Extremely useful if you have multiple skeleton mesh heads that all share a single skeleton mesh body where you want to set all the (overlapping head & body vertex) head normal vertex rotations to match the same value as the body vertex normal rotation while retaining the vertex normal rotations of the body. :beers:


////////////////////////////////////////////////////////////////////////////
//  /$$     /$$          /$$     /$$ /$$$$$$$$                  /$$       //    
// |  $$   /$$/         | $$    |__/|__  $$__/                 | $$       //    
//  \  $$ /$$//$$$$$$  /$$$$$$   /$$   | $$  /$$$$$$   /$$$$$$$| $$$$$$$  //    
//   \  $$$$//$$__  $$|_  $$_/  | $$   | $$ /$$__  $$ /$$_____/| $$__  $$ //    
//    \  $$/| $$$$$$$$  | $$    | $$   | $$| $$$$$$$$| $$      | $$  \ $$ //    
//     | $$ | $$_____/  | $$ /$$| $$   | $$| $$_____/| $$      | $$  | $$ //    
//     | $$ |  $$$$$$$  |  $$$$/| $$   | $$|  $$$$$$$|  $$$$$$$| $$  | $$ //    
//     |__/  \_______/   \___/  |__/   |__/ \_______/ \_______/|__/  |__/ //    
//                                                                        //                                                                                                                                                      
//      /$$$$$$   /$$                     /$$ /$$                         //        
//     /$$__  $$ | $$                    | $$|__/                         //        
//    | $$  \__//$$$$$$   /$$   /$$  /$$$$$$$ /$$  /$$$$$$   /$$$$$$$     //        
//    |  $$$$$$|_  $$_/  | $$  | $$ /$$__  $$| $$ /$$__  $$ /$$_____/     //        
//     \____  $$ | $$    | $$  | $$| $$  | $$| $$| $$  \ $$|  $$$$$$      //        
//     /$$  \ $$ | $$ /$$| $$  | $$| $$  | $$| $$| $$  | $$ \____  $$     //        
//    |  $$$$$$/ |  $$$$/|  $$$$$$/|  $$$$$$$| $$|  $$$$$$/ /$$$$$$$/     //        
//     \______/   \___/   \______/  \_______/|__/ \______/ |_______/      //            
////////////////////////////////////////////////////////////////////////////  
//YetiTech $tudios                                                        //
//Vertex Normal Rotation Alignment Tool v1.0 2024                         //
//by Nikhil Jeewa (◣_◢)                                                  //
////////////////////////////////////////////////////////////////////////////

//Use case: Matches the vertex of 2 objects that are overlapping. Sets the vertex normal rotation of first object to the same vertex normal rotation value of the 2nd object and selects all the matched vertex of the 2nd object to show what got matched.

//Instructions: 1. Select 2 objects, the first object being the object you want the vertex normal rotations to change. 2. Execute script.

//Note: Can be use used for any 2 objects that has matching vertex. Useful for character that has multiple heads and share same 1 body, retains the body vertex normal rotations and only changes the head normal vertex rotation to match the body (head being first selected object).  

string $objSel[] = `ls -sl`;
if (size($objSel) == 2) {
    // Get the selected Objs
    string $headObj = $objSel[0]; // Assuming the first selected Obj is the head
    string $bodyObj = $objSel[1]; // Assuming the second selected Obj is the body
    select -cl;

    // Vertex indices for head and body
    int $vertexCountH[] = `polyEvaluate -vertex $objSel[0]`;
    int $vertexCountB[] = `polyEvaluate -vertex $objSel[1]`;
    int $headV = $vertexCountH[0];
    int $bodyV = $vertexCountB[0]; 

    // Create empty array for selecting matched body vertex
    int $matchedB[] = {};
    
    // Query all vertex positions at once
    vector $headPositions[] = `xform -q -ws -t ($headObj + ".vtx[*]")`;
    vector $bodyPositions[] = `xform -q -ws -t ($bodyObj + ".vtx[*]")`;

    // Loop through all head vertices and get their translations
    int $successCount = 0;
    float $tolerance = 0.001; // Adjust tolerance as needed

    print("Starting vertex matching...\n");

    for ($i = 0; $i < $headV; $i++) {
        vector $headPos = $headPositions[$i];

        // Loop through all body vertices and compare their translations
        for ($j = 0; $j < $bodyV; $j++) {
            vector $bodyPos = $bodyPositions[$j];

            // If the translations match (within tolerance)
            if (abs($headPos.x - $bodyPos.x) < $tolerance &&
                abs($headPos.y - $bodyPos.y) < $tolerance &&
                abs($headPos.z - $bodyPos.z) < $tolerance) {
                $successCount++;
            
                // Set the vertex normal rotation of the head to the same value as the body vertex normal rotation  
                float $normals[] = `polyNormalPerVertex -q -xyz ($bodyObj + ".vtx[" + $j + "]")`;
                polyNormalPerVertex -xyz $normals[0] $normals[1] $normals[2] ($headObj + ".vtx[" + $i + "]");        

                // Matched body vertex for selection
                $matchedB[size($matchedB)] = $j;

                print("Matched head vertex " + $i + " with body vertex " + $j + "\n");

                break; // No need to check other body vertices for this head vertex
            }
        }
    }
    // Select Matched Body Vertex
    for ($i = 0; $i < size($matchedB); $i++) {
        select -add ($bodyObj + ".vtx[" + $matchedB[$i] + "]");
    }
    // Print the number of successful matches
    print ("Number of successful vertex matches: " + $successCount + "\n");
} else {
    print("Select 2 objects!\n");
}
1 Like

Wow! Excellent work. I’ve seen others have difficulty fixing the neck seam post Maya. We learned it was a vertex normal situation. Ill be sure to share this if I see others with the same issue again.

1 Like

The script works flawless with 2 separate meshes maintaining the exact vertex normal rotation values of the over lapping vertex.

However there is a slight issue if the head is having morph targets. When exporting a head with morph targets, the vertex rotation normals get slightly offset. I don’t know exactly why, it could be a maya issue or a fbx issue but I have a strong feeling it is a lousy fbx compression issue. :thinking:

Although there is a slight difference when using meshes with morph targets it still reduces the lighting issue compared to not using the script.

̶I̶’̶m̶ ̶u̶s̶i̶n̶g̶ ̶M̶a̶y̶a̶ ̶L̶T̶ ̶2̶0̶2̶0̶ ̶e̶x̶p̶o̶r̶t̶i̶n̶g̶ ̶a̶s̶ ̶f̶b̶x̶ ̶2̶0̶2̶0̶.̶1̶.̶1̶ ̶M̶a̶y̶b̶e̶ ̶s̶o̶m̶e̶o̶n̶e̶ ̶c̶a̶n̶ ̶e̶x̶p̶e̶r̶i̶m̶e̶n̶t̶ ̶w̶i̶t̶h̶ ̶a̶n̶o̶t̶h̶e̶r̶ ̶s̶o̶f̶t̶w̶a̶r̶e̶ ̶l̶i̶k̶e̶ ̶l̶a̶t̶e̶s̶t̶ ̶f̶u̶l̶l̶ ̶v̶e̶r̶s̶i̶o̶n̶ ̶o̶f̶ ̶m̶a̶y̶a̶ ̶o̶r̶ ̶b̶l̶e̶n̶d̶e̶r̶,̶ ̶h̶o̶u̶d̶i̶n̶i̶,̶ ̶3̶d̶ ̶m̶a̶x̶ ̶o̶r̶ ̶m̶a̶y̶b̶e̶ ̶t̶r̶y̶ ̶e̶x̶p̶o̶r̶t̶i̶n̶g̶ ̶a̶s̶ ̶a̶ ̶d̶i̶f̶f̶e̶r̶e̶n̶t̶ ̶f̶b̶x̶ ̶v̶e̶r̶s̶i̶o̶n̶ ̶o̶t̶h̶e̶r̶ ̶t̶h̶a̶n̶ ̶f̶b̶x̶ ̶2̶0̶2̶0̶1̶.̶1̶.̶ ̶ ̶ ̶

̶I̶f̶ ̶y̶o̶u̶ ̶a̶r̶e̶ ̶p̶l̶a̶n̶n̶i̶n̶g̶ ̶t̶o̶ ̶e̶x̶p̶e̶r̶i̶m̶e̶n̶t̶ ̶i̶n̶ ̶a̶n̶o̶t̶h̶e̶r̶ ̶s̶o̶f̶t̶w̶a̶r̶e̶ ̶t̶a̶k̶e̶ ̶a̶ ̶m̶e̶s̶h̶,̶ ̶d̶u̶p̶l̶i̶c̶a̶t̶e̶ ̶i̶t̶,̶ ̶l̶o̶c̶k̶ ̶t̶h̶e̶ ̶v̶e̶r̶t̶e̶x̶ ̶n̶o̶r̶m̶a̶l̶s̶,̶ ̶d̶e̶l̶e̶t̶e̶ ̶h̶a̶l̶f̶ ̶t̶h̶e̶ ̶m̶e̶s̶h̶ ̶o̶f̶ ̶t̶h̶e̶ ̶o̶n̶e̶ ̶a̶n̶d̶ ̶t̶h̶e̶ ̶o̶t̶h̶e̶r̶ ̶h̶a̶l̶f̶ ̶o̶f̶ ̶t̶h̶e̶ ̶o̶t̶h̶e̶r̶,̶ ̶c̶r̶e̶a̶t̶e̶ ̶a̶ ̶m̶o̶r̶p̶h̶ ̶t̶a̶r̶g̶e̶t̶ ̶e̶x̶p̶o̶r̶t̶,̶ ̶r̶e̶i̶m̶p̶o̶r̶t̶ ̶a̶n̶d̶ ̶c̶h̶e̶c̶k̶ ̶i̶f̶ ̶v̶e̶r̶t̶e̶x̶ ̶n̶o̶r̶m̶a̶l̶ ̶r̶o̶t̶a̶t̶i̶o̶n̶ ̶a̶r̶e̶ ̶m̶a̶i̶n̶t̶a̶i̶n̶e̶d̶.̶ ̶⚗̶️̶

̶T̶h̶e̶ ̶i̶m̶a̶g̶e̶ ̶i̶ ̶s̶h̶o̶w̶e̶d̶ ̶a̶s̶ ̶a̶n̶ ̶e̶x̶a̶m̶p̶l̶e̶ ̶a̶c̶t̶u̶a̶l̶l̶y̶ ̶h̶a̶s̶ ̶m̶o̶r̶p̶h̶ ̶t̶a̶r̶g̶e̶t̶s̶ ̶a̶n̶d̶ ̶i̶t̶s̶ ̶b̶a̶r̶e̶l̶y̶ ̶n̶o̶t̶i̶c̶e̶a̶b̶l̶e̶ ̶a̶n̶d̶ ̶d̶o̶a̶b̶l̶e̶ ̶f̶o̶r̶ ̶m̶y̶ ̶c̶u̶r̶r̶e̶n̶t̶ ̶p̶r̶o̶j̶e̶c̶t̶ ̶s̶o̶ ̶I̶’̶m̶ ̶n̶o̶t̶ ̶g̶o̶i̶n̶g̶ ̶t̶o̶ ̶e̶x̶p̶e̶r̶i̶m̶e̶n̶t̶ ̶f̶u̶r̶t̶h̶e̶r̶.̶ ̶�̶�̶

EDIT: I HAVE THE FIXED THE ISSUE!


̶H̶e̶r̶e̶ ̶y̶o̶u̶ ̶c̶a̶n̶ ̶s̶e̶e̶ ̶t̶h̶e̶ ̶i̶s̶s̶u̶e̶ ̶i̶f̶ ̶t̶h̶e̶ ̶h̶e̶a̶d̶ ̶i̶s̶ ̶h̶a̶v̶i̶n̶g̶ ̶m̶o̶r̶p̶h̶ ̶t̶a̶r̶g̶e̶t̶s̶,̶ ̶h̶a̶r̶d̶l̶y̶ ̶v̶i̶s̶i̶b̶l̶e̶ ̶t̶h̶o̶u̶g̶h̶.̶

@YetiTech_Studios

Oh my god, you’re my hero!
I was trying to fix this darn neck seam for days on a Metahuman.
I am loading the Metahuman to Maya 2023 directly via Quixel Bridge.
Before that I was exporting the Metahuman to FBX and then imported it into Maya.
I thought the direct Quixel process would fix it, but it didn’t.
Then I export the body out with a blendshape and I have the seam in Unreal.
I was going crazy trying dozens of things and nothing worked.
Transfer vertex normals with Transfer attributes, I tried millions of combinations.

Your script improves it a lot, but it’s still visible, although a lot smoother.
I also tried it without blendshape, but it looks the same.
I guess it’s related to the head in Maya having slightly different vertex normals than the head in Unreal.

It’s FBX 2020.3.1 and Maya 2023.2.
Let me know if I should test something.

Here are some screenshots:



1 Like

So it seams like its a lousy fbx compression.

How did you import the skeleton mesh, compute normals or import normals and tangents from fbx?

Like I said the script works flawless on meshes that dont have morph targets, Im getting zero seam visibility in both Unreal and Maya after using script and reimporting.


What I discovered is that if a mesh is having only skin cluster or if a mesh is having only morph targets then the vertex normal rotation do not change however if a mesh is having both skin cluster and morph targets then the vertex normal rotations get off set this is likely due to the combination of both skin cluster and morph targets being influenced by the tangent space.

I tried different combinations now but there is always this smooth seam left.
I guess it’s something specific to Metahumans.
Your script works perfectly, though.

I imported the head from Unreal into Maya and compared it to the head I got from Quixel bridge and the vertex normals are almost perfectly identical.
No idea at the moment what else to try.

Maybe it’s related to this other issue I’m having: Import Metahuman to Maya from Bridge, head and body disconnected when exporting to FBX from Unreal

Make sure you exporting with smooth groups and Tangents and Binormals and do not delete history after using script in maya. In Unreal while importing make sure you enable preserve smoothing groups, use T0 As Ref Pose and set Normal Import Method to Import Normals and Tangents.

This is a root bone issue you have to freeze the root bones translations at 0, better to do it in maya than BP.

That’s exactly the settings I’m using.

Hmm, I have no idea how to do that :-/
If you have time maybe you can reply to the other post.

Sorry Not freeze joint, use the Move Skinned Joints tool and move the root bone to zero.

I finally managed to find a fix if you are having 2 skeleton meshes with morph targets and ZERO normal lighting issues! Have tested with multiple head types on the same body with ZERO lighting issues! :partying_face:
Will write it up in the script🍻


This is a character with 2 skeleton meshes head & body separate with the head having morph targets and ZERO lighting issues around the neck seam!

This is the head just to show you it has morph targets.

Nice! Is the script updated yet?