skinned mesh renderers w/ submeshes combining issues

What I’m trying to do is combine all SMRs into a single one while preserving all submeshes. At least, this is what I think I need to do since all submeshes use different materials.

The prefabs produced by the tool I’m using have 1 SMR for the base model + 1 SMR per article of clothing/hair, all pointing to the same root bone.
The base model mesh has about 8-20 submeshes, depending on the model.

I am creating the combine instances by looping through all smrs, and then looping through the all submeshes, using the submeshindex parameter. I’m doing this to preserve submeshes. e.g.

for (int j = 0; j < smr.sharedMesh.subMeshCount; j++)
{
    CombineInstance combine = new CombineInstance
    {
        mesh = mesh,
        transform = smr.transform.localToWorldMatrix,
        subMeshIndex = j
    };
    combineInstances.Add(combine);
}

The problem is that the final resulting mesh has a way higher vertex count than the sum of all smrs.sharedMesh.vertexCount. The total vertex count ends up being the sum of all submeshes vertices instead.

It’s my understanding that all submeshes use the basemesh vertices. However, when combining, all vertices on each submeshes are added independently. Then, from that point, the boneweights count != vertex count and kaboom.

Characters are something new for me. I’m sure this post is a nice mashup of fundamental misunderstandings and misused terms.

Anyway, I’m not even sure where this is going wrong. I’d appreciate any pointers towards getting my combine working.

Me neither, but I know that you will need some way to maintain correlation of the skin weights and everything else special and different that a SMR needs (compared with a regular old Mesh Renderer).

To have a fighting chance of pulling this off you will need to massively crush down your data size. I recommend making two small animated objects (by small I mean like two triangles and two submeshes and animations) and combine them until you are satisfied you understand everything going on.

Before you move back to real data, I suggest you test things extremely thoroughly with the small data set so that you are confident all the animation and submesh linkages are properly maintained.

That’s good advice! I’ll investigate and report back with my findings.

Seems like the problem is that CombineMeshes treats every submesh like it was its own mesh. Therefore all vertices that were shared across submeshes are duplicated and I end up with a higher vert count.

I don’t think I’ll be able to use CombineMeshes. I’ll post the solution when I find it.

I struggled with CombinedMeshes, I forget what all it was, but I did improve upon it and posted it in my MakeGeo repository. It’s in the TestExampleCombinerImproved scene and the script is ExampleCombinerImproved.cs.

MakeGeo is presently hosted at these locations:

https://bitbucket.org/kurtdekker/makegeo

The script:

// code originally from this Unity3D website:
// https://docs.unity3d.com/ScriptReference/Mesh.CombineMeshes.html
//
// Improved by Kurt Dekker @kurtdekker
//
// Usage:
// - make a blank object
// - put this script on it
// - parent all other mesh GameObjects below this one
// - run
// - voila!

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

[RequireComponent(typeof(MeshFilter))]
[RequireComponent(typeof(MeshRenderer))]
public class ExampleCombinerImproved : MonoBehaviour
{
    void Start()
    {
        MeshFilter[] meshFilters = GetComponentsInChildren<MeshFilter>();
        List<CombineInstance> combines = new List<CombineInstance>();

        // KurtFixed: handle materials... I mean, they're kind of important!
        List<Material> materials = new List<Material>();

        for (int i = 0; i < meshFilters.Length; i++)
        {
            // KurtFixed: we gotta ignore ourselves or our count would be off!
            if (meshFilters[i] == GetComponent<MeshFilter>())
            {
                continue;
            }

            // KurtFixed: tally up the materials, since each mesh could have multiple
            var mr = meshFilters[i].GetComponent<MeshRenderer>();
            for (int j = 0; j < mr.materials.Length; j++)
            {
                var combine = new CombineInstance();

                combine.mesh = meshFilters[i].sharedMesh;
                combine.subMeshIndex = j;

                combine.transform = meshFilters[i].transform.localToWorldMatrix;
                meshFilters[i].gameObject.SetActive(false);

                combines.Add(combine);

                materials.Add( mr.materials[j]);
            }
        }
        transform.GetComponent<MeshFilter>().mesh = new Mesh();
        transform.GetComponent<MeshFilter>().mesh.CombineMeshes(combines.ToArray(), false);
        transform.gameObject.SetActive(true);

        // KurtFixed: inject the original materials
        gameObject.GetComponent<MeshRenderer>().materials = materials.ToArray();
    }
}

@Kurt-Dekker Thanks for sharing, however I think this will suffer from the same issue.

Vertices will be duplicated where they are shared between submeshes. For SMR it’s problematic because bone weights won’t match, and I’m not sure but I believe that even if I managed to make them match, there’d be tearing in the mesh when animated, on top of the extra verts.

e.g. Top half of a plane is a submesh, bottom half is another submesh. The 2 center verts are shared across submeshes. Those verts will be duplicated and the final mesh will result in 8 verts instead of 6.
example

1 Like

Just so you know, Unity is a real beast of a game engine… if you’re doing this for optimization I’m going to guess it will buy you nothing or next to nothing. Unless you have profiler-backed proof that it WILL help you on the target hardware you are building on (measurements in the editor are meaningless; any difference could just be an inspector effect), you might be wasting time.

But I mean if you need it for your game design, that’s something else, by all means, continue! :slight_smile:

1 Like