Project: Gauntlet - Current Iteration

Friday, March 19, 2010

Mesh Combine at Runtime - Unity.

Sooooooo busy. And also - I got a promotion! Figure THAT out. 

So I've been trying to create a ... "thing" .... in Unity, to combine meshes at runtime, so I can make a large, realtime-updating world in which to do "stuff".

So say, for example, our procedurally-generated map is 20x20 tiles, and each tile is a type of environment.

That is 20x20 draw calls, 400. Tooooo many, given we'd be looking at an upper-bound of 500 draw calls.

So we'd want to combine those similar meshes into single meshes, get the calls down to say, 5, instead of 400.

Now, I started out doing exactly that, using the CombineChildren.cs script that comes with Unity.

And it worked. Just fine. Performance overhead? Yeah, but it worked.

However, it worked for only 3 minutes, before crashing out.

The cause? Huge memory leak.

I tried Destroy() on the meshes, the gameobjects, the meshfilters, but no - it stays in memory.

Now, long story short:

1) Here's a link to the forum post and the... case history: http://forum.unity3d.com/viewtopic.php?t=46369

This includes the final script that works, and an actual rundown of how the thing works.

But this, being a blog, i'll SHOW you how it works:


Ok, so above:

That's the world. It's many tiles as you can see. 5 different type of tile, 20x20, potential 400 drawcalls, down to only 5.

Framerate: That's the framerate during a combine frame, normal framerate: 2000+.

Projectview: They are my "Tile" prefabs and their respective Materials.
Hierarchy: Combined GameObject contains 5 dynamically created parent objects, under each of which the tiles are parented. These are the parent objects that the MeshCombine (see the forum link for the code) will be called on.

This is the object containing my Tile types. As you can see, each object contains every possible Tile type, with all types that currently aren't in use, deactivated. There's probably a memory overhead on it, a little one, but i took performance over memory.

At runtime, this will activate/deactive the relevant tile types (man, i think i found a bug in the above screenshot), and thus make the tilemap shift and change.

Hope this is useful to you all!

And... here's the code:

using System;
using System.Collections;
using UnityEngine;

public class MeshCombine
{
    public void combineMeshes(GameObject parent)
    {
        MeshFilter targetFilter = null;

        targetFilter = parent.GetComponent();

        if (targetFilter == null)
        {
            targetFilter = parent.AddComponent();
        }

        MeshRenderer targetRenderer = null;

        targetRenderer = parent.GetComponent();

        if (targetRenderer == null)
        {
            targetRenderer = parent.AddComponent();
        }

        MeshFilter[] meshFilters = parent.GetComponentsInChildren();
        CombineInstance[] combine = new CombineInstance[meshFilters.Length];
        int index = 0;

        int matIndex = -1;

        for (int i = 0; i < meshFilters.Length; i++)
        {
            if (meshFilters[i].sharedMesh == null) continue;
            if (meshFilters[i].renderer.enabled == false)
            {
                continue;
            }
            else if (matIndex == -1)
            {
                matIndex = i;
            }
            if (meshFilters[i].Equals(targetFilter)) continue;

           
            combine[index].mesh = meshFilters[i].sharedMesh;

            combine[index++].transform = meshFilters[i].transform.localToWorldMatrix;
            meshFilters[i].renderer.enabled = false;
        }

        targetFilter.mesh.CombineMeshes(combine);

        targetFilter.renderer.material = meshFilters[matIndex].renderer.material;

    }
}