08 August 2014

Combine meshes in Unity

If there are a lot of objects in a scene that share the same material but do not share the same meshes you can lower the number of draw calls by combining them into one single mesh.

Unity has a "MeshCombineUtility" in the standard assets that is supposed to do exactly that, but several of our combined meshes were missing parts in an unpredictable way. It uses a hashtable to index the used materials. I replaced it with a typed dictionary and that fixed it. Somehow we must have had hash collisions.

In the documentation of Mesh.Combinemeshes there is a script that intends to do the same, with simpler code. Oddly, that script does combine multiple meshes into one but it generates an object without materials.

So I wrote my own version of the CombineChildren script that combines both into one, working script:

using UnityEngine;
using System.Collections.Generic;

[AddComponentMenu("Mesh/Combine Children")]
public class CombineChildren : MonoBehaviour {

void Start()
{
 Matrix4x4 myTransform = transform.worldToLocalMatrix;
 Dictionary<string, List<CombineInstance>> combines = new Dictionary<string, List<CombineInstance>>();
 Dictionary<string , Material> namedMaterials = new Dictionary<string, Material>();
 MeshRenderer[] meshRenderers = GetComponentsInChildren<MeshRenderer>();
 foreach (var meshRenderer in meshRenderers)
 {
  foreach (var material in meshRenderer.sharedMaterials)
    if (material != null && !combines.ContainsKey(material.name)) {
   combines.Add(material.name, new List<CombineInstance>());
   namedMaterials.Add(material.name, material);
    }
 }

 MeshFilter[] meshFilters = GetComponentsInChildren<MeshFilter>();
 foreach(var filter in meshFilters)
 {
  if (filter.sharedMesh == null)
   continue;
  var filterRenderer = filter.GetComponent<Renderer>();
  if (filterRenderer.sharedMaterial == null)
   continue;
  if (filterRenderer.sharedMaterials.Length > 1)
   continue;
  CombineInstance ci = new CombineInstance
  {
   mesh = filter.sharedMesh,
   transform = myTransform*filter.transform.localToWorldMatrix
  };
  combines[filterRenderer.sharedMaterial.name].Add(ci);

  Destroy(filterRenderer);
 }

 foreach (Material m in namedMaterials.Values)
 {
  var go = new GameObject("Combined mesh");
  go.transform.parent = transform;
  go.transform.localPosition = Vector3.zero;
  go.transform.localRotation = Quaternion.identity;
  go.transform.localScale = Vector3.one;

  var filter = go.AddComponent<MeshFilter>();
  filter.mesh.CombineMeshes(combines[m.name].ToArray(), true, true);

  var arenderer = go.AddComponent<MeshRenderer>();
  arenderer.material = m;
 }
}}

04 August 2014

Quickly adding bloom to Unity

Recently I wanted to add bloom to a personal project I'm working on. In the Image Effects (Pro Only) package that you can import directly via the editor you have exactly what I wanted, a simple bloom effect. Attach it to the camera and boom you have bloom.

Sad thing though is that the compiler spits out a bunch of warnings when you import that package, clearly it's been a while since the package has seen some love from the Unity developers. I can understand that their priorities lie elsewhere.
Obviously I code in C# and I wanted to add the bloom effect on the main camera and change its public values via code. Since the effect was written in unityscript this was not possible.

So I spent some time converting it to C#, you can download it here if you want to use it. I did not clean the code or anything, just made sure it worked like it did before and that the compiler was completely happy with it.