Skip to content

Instantly share code, notes, and snippets.

@simonwittber
Last active July 16, 2023 20:41
Show Gist options
  • Select an option

  • Save simonwittber/61fc0f7572116f85a139aaa5359d78f9 to your computer and use it in GitHub Desktop.

Select an option

Save simonwittber/61fc0f7572116f85a139aaa5359d78f9 to your computer and use it in GitHub Desktop.
A Unity post processor to optimize meshes created with Asset Forge. Reduces draw calls and allows batching.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine;
public class AssetForgePostProcessor : AssetPostprocessor
{
void OnPostprocessModel(GameObject gameObject)
{
if (assetImporter.assetPath.Contains("AssetForge"))
Apply(gameObject);
}
void Apply(GameObject gameObject)
{
var filters = gameObject.GetComponentsInChildren<MeshFilter>();
foreach (var f in filters)
FixScale(f);
var newGameObject = CombineMaterials(gameObject);
foreach (var f in filters)
f.gameObject.SetActive(false);
gameObject.transform.position = gameObject.transform.position;
gameObject.transform.rotation = gameObject.transform.rotation;
newGameObject.transform.parent = gameObject.transform;
}
class VertexAttributeCollection
{
public List<Vector3> vertex = new List<Vector3>();
public List<Vector3> normal = new List<Vector3>();
public List<Color> color = new List<Color>();
public List<Vector2> uv = new List<Vector2>();
}
GameObject CombineMaterials(GameObject go)
{
var filters = go.GetComponentsInChildren<MeshFilter>();
var materialAttributeMap = new Dictionary<Material, VertexAttributeCollection>();
foreach (var f in filters)
{
var mesh = f.sharedMesh;
var vertices = mesh.vertices;
var normals = mesh.normals;
var colors = mesh.colors;
var uvs = mesh.uv;
var addUV = uvs.Length > 0;
var addNormal = normals.Length > 0;
var addColor = colors.Length > 0;
var meshMaterials = f.GetComponent<MeshRenderer>().sharedMaterials;
for (var submeshIndex = 0; submeshIndex < mesh.subMeshCount; submeshIndex++)
{
var currentMaterial = meshMaterials[submeshIndex];
VertexAttributeCollection vac;
if (!materialAttributeMap.TryGetValue(currentMaterial, out vac))
vac = materialAttributeMap[currentMaterial] = new VertexAttributeCollection();
var indices = mesh.GetTriangles(submeshIndex);
for (var i = 0; i < indices.Length; i++)
{
vac.vertex.Add(vertices[indices[i]] + f.transform.localPosition);
if (addNormal) vac.normal.Add(normals[indices[i]]);
if (addUV) vac.uv.Add(uvs[indices[i]]);
if (addColor) vac.color.Add(colors[indices[i]]);
}
}
}
var newSharedMaterials = materialAttributeMap.Keys.ToArray();
var allAttributes = new VertexAttributeCollection();
for (var i = 0; i < newSharedMaterials.Length; i++)
{
var va = materialAttributeMap[newSharedMaterials[i]];
allAttributes.vertex.AddRange(va.vertex);
allAttributes.normal.AddRange(va.normal);
allAttributes.uv.AddRange(va.uv);
allAttributes.color.AddRange(va.color);
}
var newMesh = new Mesh() { name = "CombinedMesh" };
newMesh.SetVertices(allAttributes.vertex);
newMesh.SetNormals(allAttributes.normal);
newMesh.SetColors(allAttributes.color);
newMesh.SetUVs(0, allAttributes.uv);
newMesh.subMeshCount = newSharedMaterials.Length;
var triangleIndex = 0;
for (var i = 0; i < newSharedMaterials.Length; i++)
{
var va = materialAttributeMap[newSharedMaterials[i]];
var vertCount = va.vertex.Count;
newMesh.SetTriangles(Enumerable.Range(triangleIndex, vertCount).ToArray(), i);
triangleIndex += vertCount;
}
newMesh.RecalculateNormals();
newMesh.RecalculateTangents();
var g = new GameObject("CombinedMesh");
g.AddComponent<MeshFilter>().sharedMesh = newMesh;
g.AddComponent<MeshRenderer>().sharedMaterials = newSharedMaterials;
AssetDatabase.AddObjectToAsset(newMesh, assetPath);
return g;
}
void FixScale(MeshFilter filter)
{
var mesh = filter.sharedMesh;
var scale = filter.transform.localScale;
var vertices = mesh.vertices;
var reverseWinding = scale.x < 0 || scale.y < 0 || scale.z < 0;
var matrix = Matrix4x4.TRS(Vector3.zero, filter.transform.localRotation, scale);
var normals = mesh.normals;
for (var i = 0; i < vertices.Length; i++)
{
vertices[i] = matrix * vertices[i];
normals[i] = Vector3.Scale(scale, normals[i]).normalized;
}
for (var submeshIndex = 0; submeshIndex < mesh.subMeshCount; submeshIndex++)
{
var indices = mesh.GetTriangles(submeshIndex);
for (var i = 0; i < indices.Length; i += 3)
{
if (reverseWinding)
{
var a = indices[i + 0];
var b = indices[i + 1];
var c = indices[i + 2];
indices[i + 0] = a;
indices[i + 1] = c;
indices[i + 2] = b;
}
}
mesh.SetTriangles(indices, submeshIndex);
}
mesh.vertices = vertices;
mesh.normals = normals;
filter.transform.localScale = Vector3.one;
filter.transform.localRotation = Quaternion.identity;
}
}
@simonwittber

Copy link
Copy Markdown
Author

Normals are not quite working yet, the new model comes out with flat polygons.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment