Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save Chaosed0/a0b7a70b53988d87c08358374084144b to your computer and use it in GitHub Desktop.
Save Chaosed0/a0b7a70b53988d87c08358374084144b to your computer and use it in GitHub Desktop.
A Unity asset postprocessor for sorting mesh triangles back-to-front
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
// This sorts the triangles within a mesh back-to-front based on Wizgun's fixed camera
// angle, solving mesh self-sorting issues when transparent shaders are used.
// Normally, these objects are opaque, but when TintedUnlitShaderReveal is swapped in
// to reveal what is behind the object, depth sorting is turned off. We need Unity to
// draw the mesh in the correct order so that the triangles' depths are respected.
// Note that this asset postprocessor does not change the asset on disk. It only changes
// the internal representation (in Unity's library) of the Mesh objects that get
// generated from our FBX files.
public class SortMeshTrianglesAssetPostprocessor : AssetPostprocessor
{
private const string PROPS_PATH = "Assets/Art/Environment/Props";
private const float CAMERA_PITCH = 40;
private List<Triangle> _cachedTriangles = new List<Triangle>();
private List<Vector3> _cachedVertices = new List<Vector3>();
private List<int> _cachedTriangleIndices = new List<int>();
private List<int> _cachedNewTriangleIndices = new List<int>();
private struct Triangle
{
public int index;
public float dot;
}
// Called by Unity
private void OnPostprocessModel(GameObject go)
{
if (!assetPath.StartsWith(PROPS_PATH))
{
return;
}
foreach (MeshFilter filter in go.GetComponentsInChildren<MeshFilter>())
{
Mesh mesh = filter.sharedMesh;
Vector3 sortAxis = Quaternion.AngleAxis(CAMERA_PITCH, Vector3.right) * Vector3.forward;
SortMeshTriangles(mesh, sortAxis);
}
foreach (SkinnedMeshRenderer skinnedMeshRenderer in go.GetComponentsInChildren<SkinnedMeshRenderer>())
{
Mesh mesh = skinnedMeshRenderer.sharedMesh;
// Truth be told, I have no clue why this needs to be reversed for skinned mesh renderers.
// Does Unity draw them in the reverse order from normal meshes?
Vector3 sortAxis = Quaternion.AngleAxis(-CAMERA_PITCH, Vector3.right) * Vector3.back;
SortMeshTriangles(mesh, sortAxis);
}
}
private void SortMeshTriangles(Mesh mesh, Vector3 sortAxis)
{
_cachedVertices.Clear();
mesh.GetVertices(_cachedVertices);
for (int i = 0; i < mesh.subMeshCount; i++)
{
SortSubmeshTriangles(mesh, i, sortAxis);
}
}
private void SortSubmeshTriangles(Mesh mesh, int submeshIndex, Vector3 sortAxis)
{
_cachedTriangles.Clear();
_cachedTriangleIndices.Clear();
mesh.GetTriangles(_cachedTriangleIndices, submeshIndex);
int baseVertex = (int)mesh.GetBaseVertex(submeshIndex);
for (int i = 0; i < _cachedTriangleIndices.Count / 3; i++)
{
Vector3 v1 = _cachedVertices[baseVertex + _cachedTriangleIndices[i * 3]];
Vector3 v2 = _cachedVertices[baseVertex + _cachedTriangleIndices[i * 3 + 1]];
Vector3 v3 = _cachedVertices[baseVertex + _cachedTriangleIndices[i * 3 + 2]];
Vector3 avg = (v1 + v2 + v3) / 3f;
Triangle triangle = default;
triangle.index = i;
triangle.dot = Vector3.Dot(avg, sortAxis);
_cachedTriangles.Add(triangle);
}
_cachedTriangles.Sort((x, y) => y.dot.CompareTo(x.dot));
_cachedNewTriangleIndices.Clear();
foreach (Triangle triangle in _cachedTriangles)
{
_cachedNewTriangleIndices.Add(_cachedTriangleIndices[triangle.index * 3]);
_cachedNewTriangleIndices.Add(_cachedTriangleIndices[triangle.index * 3 + 1]);
_cachedNewTriangleIndices.Add(_cachedTriangleIndices[triangle.index * 3 + 2]);
}
mesh.SetTriangles(_cachedNewTriangleIndices.ToArray(), submeshIndex);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment