Continuous smoothing between GameObjects

Hi,

I’m working on a new humanoid and I plan to use Blend-shapes / Morph-targets for the facial expressions of this character. I separated the head from the body and created some test targets for the head but the problem with this approach is that the smoothing doesn’t continue(it looks cut). Do know a way to continue the smoothing. Or should I apply the blend-shapes to entire body (which I think may have performance drawbacks).

Thanks

If the body and head are separate meshes, Unity (and real time render in general) will have no way to keep the normals between them smooth. Even if it was the same mesh and there was a seam, there’s still no way to fix it. Real time rendering works on a single vertex at a time with no knowledge of even other vertices in the mesh. This is a content issue that needs to be fixed in the original content.

Most 3D modelling tools will update a mesh’s normals when parts are detached, and this is likely no different. Maya has options for locking the normals so that when you detach parts the original normals are retained. 3ds Max has the option to lock normals too, but it breaks if the mesh is separated into multiple objects. I don’t know of a solution for this apart from custom max scripts to store off the normals and reapply them by matching vertex positions. Something like “Normal Thief” may be an option.

Thanks for the reply gbolus! It was very helpful.

It seems that the Morpher(Blend-shapes) modifier of the 3ds Max (version 2015) breaks the normals/ smoothing information of the edges and the edit normals above Morpher gets ignored during export. I will ask this question at the Autodesk Area forums and if I can find a solution I will post it here. I don’t want to spend time on it but I think I can attempt to write a Unity / C# script that copies the normals information of the edges, the way Normal Thief does, if nothing else works.

Thanks!

No one replied at Area Forums so I wrote the script, to copy normals from overlapping vertices from the other mesh.

Running the script once is enough, but Meshes / Read Write must be enabled from Import Setting. There is also a debug option to display modified normals.

2 Likes

there’s a stitch normal script for 3ds to handle separated mesh normal

Thanks for the reply Reanimate_L,

modified normals above Morpher is ignored by the fbx exporter, and below is modified by Morpher.(3ds Max 2015) But the problem is solved. Anyone with the same issue can use this script to solve these types of problems:

Script simply finds overlapping vertices from seperated meshes and unifys their normals.

6 Likes

A year later and I would like to thank you for this script!

1 Like

@ergin3d_1 is it possible to do the same for the calculated blendshape normal values as well? I’m noticing that using any amount of blendshape breaks the unified normal appearance unfortunately.

Thank you for sharing your script. It helped me to solve this problem, when I separate DAZ mesh in Blender by materials. Resulting figure looked like Frankenstein, but this smoothed out all the transitions between meshes. Something what UMA does, but I don’t use it.

No problem. “Any amount of blendshape breaks the unified normal appearance” is unfortunately correct.

You can try the settings above. The torso mesh in the video is composed of two seperate meshes.

It seems that turning “Bland Shape Normals” in Import Settings from Calculate to None works fine. I didn’t notice it before but with default settings it really messes up the transitions between meshes again when applying body blendshapes. Hopefully it has not some downside.
Anyway, thanks again :slight_smile:

Well, unfortunately it doesn’t work. When I set Normals to “Import” and Blendshapes Normals to “None” in Import Settings and copy Normals from unsplitted mesh to it’s parts, it works and hard edges are gone. But every time I reopen Unity, it’s back, not sure why.
And copying Normals during spawning a character is also not an option as it takes several seconds.

Just an update if it can help somebody in the future. A solved it by duplicating FBX meshes and copying normals from complete figure on these (in editor script).

Very good! Thank you very much. if it won’t be a big trouble for you, can you please share the script?

Sure! First I find desired FBX in active Project folder and save it’s meshes into LOD subfolder:

                    // Find FBX with original meshes

                    string activeFolderPath = GetActiveFolderInProjectWindow(); // Get path active in Project window
                   
                    string[] allActiveFolder_GUI = AssetDatabase.FindAssets("t:model", new[] { activeFolderPath });

                    Log.L("prefabRecipe.FBX_Name: " + prefabRecipe.FBX_Name);

                    GameObject FBXmodel = null;
                    string FBXtoFind = prefabRecipe.FBX_Name + " - FROM BLENDER";
                    foreach (string guid in allActiveFolder_GUI)
                    {
                        // Get path
                        string path = AssetDatabase.GUIDToAssetPath(guid);

                        // Get file names only
                        string fileName = ACore_FilesFolders.GetFileNameWithoutExtension(path);

                        if (fileName == FBXtoFind)
                        {
                            FBXmodel = (GameObject)AssetDatabase.LoadAssetAtPath(path, typeof(GameObject));                          
                        }                          
                    }

                    if (FBXmodel == null)
                    {
                        Log.L("FBX for DAZ CHARACTER NOT FOUND: " + FBXtoFind + ". We cannot duplicated original meshes.", Log.Type.Error);
                    }
                    else
                    {
                        SkinnedMeshRenderer[] renderers = FBXmodel.GetComponentsInChildren<SkinnedMeshRenderer>();

                        foreach (SkinnedMeshRenderer renderer in renderers)
                        {
                            //Log.L("renderer name: " + renderer.name, Log.Type.Warning);

                            // Clone mesh
                            Mesh sharedMesh = (Mesh)Instantiate(renderer.sharedMesh);

                            string filePath = GetActiveFolderInProjectWindow(); // Get path active in Project window
                            filePath += "/LODS";
                            string fileName = sharedMesh.name + "_Quality_Original" + ".asset"; // File name      

                            Regex illegalInFileName = new Regex(@"[\\/:*?""<>|]"); // Characters not allowed in Windows in file name
                            fileName = illegalInFileName.Replace(fileName, "");

                            filePath += "/" + fileName;

                            Log.L("filePath with LODS: " + filePath);
                            // -----------------------

                            if (filePath != "")
                            {
                                AssetDatabase.CreateAsset(sharedMesh, filePath);
                                AssetDatabase.SaveAssets();
                                AssetDatabase.Refresh();
                            }
                        }
                    }     

        public static string GetActiveFolderInProjectWindow()
        {
            Type projectWindowUtilType = typeof(ProjectWindowUtil);
            MethodInfo getActiveFolderPath = projectWindowUtilType.GetMethod("GetActiveFolderPath", BindingFlags.Static | BindingFlags.NonPublic);
            object obj = getActiveFolderPath.Invoke(null, new object[0]);
            string pathToCurrentFolder = obj.ToString();
            return pathToCurrentFolder;
        }

And than I load those meshes on my character model prefab with something like this. It’s in different method so I paste only important part:

                        Mesh originalMesh = (Mesh)AssetDatabase.LoadAssetAtPath(originalMesh_Path, typeof(Mesh));

                        renderer.sharedMesh = originalMesh;
1 Like