x


Iterating and Aggregating Meshes

I have a method that locates all mesh filters attached to a transform and its children and aggregates its vertices and triangles. A copy of the code is further down.

[Edit: Based on skovacs1's answer and further troubleshooting.] The below code works as expected. The mirroring that is mentioned was due to the display method used for debugging. So the only remaining question is whether there is a more efficient way of aggregating meshes for purposes other than display.

The problem I'm having is that the resulting aggregate mesh is mirrored along the x-axis. So only if I view the aggregate mesh with Scale X = -1 is everything good.

Two questions:

  1. [Edit: No longer applies] Anyone see something I'm doing wrong? E.g. Applying the transform incorrectly.
  2. Is this the most efficient way of doing this sort of thing?

Code (C#)

private void AggregateTransform(Transform source
    , List<float> aggregateVerts
    , List<int> aggregateIndices
    , List<int> indexMap)
{
    // Get all mesh filters on this transform and its children.
    MeshFilter[] filters = source.GetComponentsInChildren<MeshFilter>();

    if (filters == null)
    {
        Debug.LogWarning("No meshes found in " + source.name + ".");
    }
    else
    {
        int currentIndex = aggregateVerts.Count / 3;
        foreach (MeshFilter filter in filters)
        {
            Mesh mesh = filter.sharedMesh;
            Transform trans = filter.transform;
            if (mesh != null)
            {
                // Loop through all vertices, transform to world position,
                // and add to aggregate.
                indexMap.Clear();
                for (int i = 0; i < mesh.vertexCount; i++)
                {
                    Vector3 vert = trans.TransformPoint(mesh.vertices[i]);
                    aggregateVerts.Add(vert.x);
                    aggregateVerts.Add(vert.y);
                    aggregateVerts.Add(vert.z);
                    indexMap.Add(currentIndex);
                    currentIndex++;
                } 
                // Loop through all indices.  Add to aggregate list, 
                // mapping from local to global index value.
                for (int i = 0; i < mesh.triangles.Length; i++)
                {
                    aggregateIndices.Add(indexMap[mesh.triangles[i]]);
                }
                IncrementProgressBar();
            }
        }
    }
}
more ▼

asked Nov 07 '10 at 10:39 PM

SteveFSP gravatar image

SteveFSP
1k 8 13 29

(comments are locked)
10|3000 characters needed characters left

2 answers: sort voted first

The answer was hiding in plain sight.

First, a review of the requirements implicit in the question:

  • The only data needed from the MeshFilters is the vertices and triangles. The uv's and normals don't matter.
  • The vertices in the aggregate mesh need to be in world space.
  • The solution needs to support more more than 64K vertices in the aggregate mesh.

Another requirement is that the vertices be a flattened array, rather than an array of Vector3 structures. But that requirement effects the detail of the implementation, not the answer.

And the answer is to use Mesh.CombineMeshes.

Most of the documentation involving the Mesh.CombineMeshes function discusses using it to combine meshes that share the same material. But, if all you need are the vertices and triangles, then it can be used to combine meshes with different materials.

Just prepare and use Mesh.CombineMeshes as shown in the function's example code, then extract the vertices and triangles from mesh.vertices and mesh.triangles.

The 64K vertices limit for a single Mesh object applies. But that can be worked around using a batching process:

  1. Get an array of MeshFitlers to combine.
  2. Batch together the filters with each batch containing less than 64K vertices.
  3. Run each batch through Mesh.CombineMeshes. (Separately)
  4. Aggregate the vertices and triangles from each batch into aggregate vertices and triangle arrays.

As far as the performance goes: The aggregation took over 2 minutes to complete using the original algorithm. (See question.) It took less than 50 milliseconds using Mesh.CombineMeshes.

A slight performance boost.

more ▼

answered Nov 19 '10 at 01:59 AM

SteveFSP gravatar image

SteveFSP
1k 8 13 29

(comments are locked)
10|3000 characters needed characters left

It is possible that there is a problem with that portion of code which you use to make this data viewable.

What is going on with currentIndex and indexMap? You go through a lot of work with currentIndex, but all you really want to do is add your triangles to the aggregate triangles, right? wouldn't that simply entail getting the offset for the vertices in the aggregate vertices?

Here's a simpler version of what your code does (each vertex is still split into separate components and the triangle indices index to the start index of each vertex):

private void AggregateMesh(Transform source,
                           List<float> aggregateVerts,
                           List<int> aggregateTriangles) {
    MeshFilter[] filters = source.GetComponentsInChildren<MeshFilter>();

    if(filters == null) {
        Debug.LogWarning("No meshes found in " + source.name + ".");
        return;
    }
    foreach (MeshFilter filter in filters) {
        Mesh mesh = filter.sharedMesh;
        if(mesh == null) continue;

        int currentIndex = aggregateVerts.Count;
        int i = 0;
        Transform trans = filter.transform;
        for(; i < mesh.vertices.Length; i++) {
            Vector3 vert = trans.TransformPoint(vertices[i]);
            aggregateVerts.Add(vert.x);
            aggregateVerts.Add(vert.y);
            aggregateVerts.Add(vert.z);
        }
        for(i = 0; i < mesh.triangles.Length; i++)
            aggregateTriangles.Add(mesh.triangles[i] * 3 + currentIndex);
        IncrementProgressBar();
    }
}
more ▼

answered Nov 08 '10 at 11:14 PM

skovacs1 gravatar image

skovacs1
10k 11 25 91

Thanks for checking out the code! I'll do one topic per comment: RE: Mirroring of mesh. You are correct. The problem was with the code I used to view the aggregate mesh. My original code provides the expected output. So no need to look at that further.

Nov 09 '10 at 02:49 PM SteveFSP

RE: Using an array of Vector3's for triangles. I'm afraid that is not an option in this case. The primary reason for gathering the data is to pass it to an external library which does not have any linkage to the UnityEngine library. So a flattened indices array a requirement. (Wish I could use your simpler approach though.)

Nov 09 '10 at 02:55 PM SteveFSP

The Vector3's were for the verts. I will update the simplified script to use the start indices of a flattened array of vertex components for the triangle indices

Nov 09 '10 at 03:37 PM skovacs1

RE: Purpose of indexMap. Yes, you are definitely correct that the map has no useful purpose. A bit embarrassing, but that was left over from an earlier code iteration and I never re-evaluated its need.

Nov 09 '10 at 04:40 PM SteveFSP
(comments are locked)
10|3000 characters needed characters left
Your answer
toggle preview:

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this question

By Email:

Once you sign in you will be able to subscribe for any updates here

By RSS:

Answers

Answers and Comments

Topics:

x1353
x96

asked: Nov 07 '10 at 10:39 PM

Seen: 1343 times

Last Updated: Nov 09 '10 at 03:04 PM