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;

    }
}

3 comments:

Anonymous said...

Hi, big thanks for the example code. I will try it out as I want to do something similar in my iPhone project. Will be interesting to see if it will benefit from runtime combining. Usually it should but in my specific case not sure about it. Also want to mention that there are some errors in the code example that might irritate beginners. The code that is attached in the unity form works though. I think for beginners it's really hard when code has errors to figure out correct usage.

Anonymous said...

Edit: The code that is attached in the UNITY FORUM works though. I think for beginners it's really hard when code has errors to figure out correct usage.

tuone said...

thanks! If you could explain in beginners terms, which lines to add at the end of an instantiate script to combine the clones, that would be amazinG!!!

say... instantiate 400 cubes i++

set to cubes to parent object

combine all meshes in parent.

i am new it's very hard to do!