You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

282 lines
9.0 KiB
C#

/******************************************************************************/
/*
Project - MudBun
Publisher - Long Bunny Labs
http://LongBunnyLabs.com
Author - Ming-Lun "Allen" Chou
http://AllenChou.net
*/
/******************************************************************************/
using System.Collections.Generic;
using UnityEngine;
namespace MudBun
{
public class MeshUtil
{
public static int EmissionHashUvIndex = 2;
public static int MetallicSmoothnessUvIndex = 3;
public static int TextureWeightIndex = 4;
public static readonly float PositionTolerance = 1e-4f;
public static readonly float NormalTolerance = 1e-2f;
public static readonly float UvTolerance = 1e-4f;
public static readonly float PositionToleranceSqr = PositionTolerance * PositionTolerance;
public static readonly float NormalToleratnceSqr = NormalTolerance * NormalTolerance;
public static readonly float UvToleratnceSqr = UvTolerance * UvTolerance;
struct VertKey
{
public Vector3 Pos;
public Vector3 Norm;
public Vector2 Uv;
public override int GetHashCode()
{
int hash = Codec.Hash(Pos);
hash = Codec.HashConcat(hash, Norm);
hash = Codec.HashConcat(hash, Uv);
return hash;
}
public override bool Equals(object obj)
{
return
obj is VertKey other
&& (Pos - other.Pos).sqrMagnitude < PositionToleranceSqr + MathUtil.Epsilon
&& (Norm - other.Norm).sqrMagnitude < NormalToleratnceSqr + MathUtil.Epsilon
&& (Uv - other.Uv).sqrMagnitude < UvToleratnceSqr + MathUtil.Epsilon;
}
}
private static readonly Vector3[] s_aRenderBoxProxyVert =
{
new Vector3(-0.5f, -0.5f, -0.5f),
new Vector3( 0.5f, -0.5f, -0.5f),
new Vector3(-0.5f, 0.5f, -0.5f),
new Vector3( 0.5f, 0.5f, -0.5f),
new Vector3(-0.5f, -0.5f, 0.5f),
new Vector3( 0.5f, -0.5f, 0.5f),
new Vector3(-0.5f, 0.5f, 0.5f),
new Vector3( 0.5f, 0.5f, 0.5f),
};
private static readonly int[] s_aRenderBoxProxyIndexInverted =
{
0, 1, 3, 0, 3, 2,
0, 2, 6, 0, 6, 4,
0, 4, 5, 0, 5, 1,
7, 6, 2, 7, 2, 3,
7, 5, 4, 7, 4, 6,
7, 3, 1, 7, 1, 5,
};
private static readonly int[] s_aRenderBoxProxyIndex =
{
0, 3, 1, 0, 2,3,
0, 6, 2, 0, 4,6,
0, 5, 4, 0, 1,5,
7, 2, 6, 7, 3,2,
7, 4, 5, 7, 6,4,
7, 1, 3, 7, 5,1,
};
private static Vector2 Quantize(Vector2 v, float step)
{
Vector2 s = new Vector2(Mathf.Sign(v.x), Mathf.Sign(v.y));
v += 0.5f * step * Vector2.one;
v = VectorUtil.CompDiv(v, step * Vector3.one);
v = VectorUtil.Abs(v);
v = new Vector2(Mathf.Floor(v.x), Mathf.Floor(v.y));
v = VectorUtil.CompMul(s * step, v);
return v;
}
private static Vector3 Quantize(Vector3 v, float step)
{
Vector3 s = new Vector3(Mathf.Sign(v.x), Mathf.Sign(v.y), Mathf.Sign(v.z));
v += 0.5f * step * Vector3.one;
v = VectorUtil.CompDiv(v, step * Vector3.one);
v = VectorUtil.Abs(v);
v = new Vector3(Mathf.Floor(v.x), Mathf.Floor(v.y), Mathf.Floor(v.z));
v = VectorUtil.CompMul(s * step, v);
return v;
}
public static void Weld(Mesh mesh, int textureUvIndex = -1)
{
var aOldVert = mesh.vertices;
var aOldNorm = mesh.normals;
var aOldColor = mesh.colors;
var aOldBoneWeight = mesh.boneWeights;
var aOldBindPose = mesh.bindposes;
var aOldTextureUv = textureUvIndex >= 0 ? new List<Vector2>() : null;
var aOldEmissionHash = new List<Vector4>();
var aOldMetallicSmoothness = new List<Vector2>();
if (textureUvIndex >= 0)
mesh.GetUVs(0, aOldTextureUv);
mesh.GetUVs(EmissionHashUvIndex, aOldEmissionHash);
mesh.GetUVs(MetallicSmoothnessUvIndex, aOldMetallicSmoothness);
var aOldIndex = mesh.GetIndices(0);
bool useUv =
textureUvIndex >= 0
&& aOldTextureUv.Count == aOldIndex.Length;
//var vertToIndexMap = new Dictionary<int, int>();
var vertToIndexMap = new Dictionary<VertKey, int>();
var indexToIndexMap = new int[aOldVert.Length];
for (int i = 0; i < aOldIndex.Length; ++i)
{
int index = aOldIndex[i];
var key =
new VertKey
{
Pos = Quantize(aOldVert[index], PositionTolerance),
Norm = Quantize(aOldNorm[index], NormalTolerance),
Uv = useUv ? Quantize(aOldTextureUv[index], UvTolerance) : Vector2.zero
};
if (!vertToIndexMap.TryGetValue(key, out int newIndex))
{
newIndex = vertToIndexMap.Count;
vertToIndexMap.Add(key, newIndex);
// debugger-friendly duplicate code
indexToIndexMap[i] = newIndex;
}
else
{
// debugger-friendly duplicate code
indexToIndexMap[i] = newIndex;
}
}
int numUniqueVerts = vertToIndexMap.Count;
var aNewVert = new Vector3[numUniqueVerts];
var aNewNorm = new Vector3[numUniqueVerts];
var aNewColor = new Color[numUniqueVerts];
var aNewTextureUv = useUv ? new Vector2[numUniqueVerts] : null;
var aNewEmissionHash = new Vector4[numUniqueVerts];
var aNewMetallicSmoothness = new Vector2[numUniqueVerts];
var aNewBoneWeight = new BoneWeight[numUniqueVerts];
var aNewBindPose = aOldBindPose; // bind poses aren't changed
for (int oldIndex = 0; oldIndex < indexToIndexMap.Length; ++oldIndex)
{
int newIndex = indexToIndexMap[oldIndex];
aNewVert[newIndex] = aOldVert[oldIndex];
aNewNorm[newIndex] = aOldNorm[oldIndex];
aNewColor[newIndex] = aOldColor[oldIndex];
if (useUv)
aNewTextureUv[newIndex] = aOldTextureUv[oldIndex];
aNewEmissionHash[newIndex] = aOldEmissionHash[oldIndex];
aNewMetallicSmoothness[newIndex] = aOldMetallicSmoothness[oldIndex];
if (aOldBoneWeight != null && aOldBoneWeight.Length >= aOldVert.Length)
aNewBoneWeight[newIndex] = aOldBoneWeight[oldIndex];
}
var aNewIndex = new int[aOldIndex.Length];
for (int i = 0; i < aOldIndex.Length; ++i)
{
aNewIndex[i] = indexToIndexMap[aOldIndex[i]];
}
var topology = mesh.GetTopology(0);
mesh.Clear();
mesh.SetVertices(aNewVert);
mesh.SetNormals(aNewNorm);
mesh.SetColors(aNewColor);
if (aOldBoneWeight != null)
{
mesh.boneWeights = aNewBoneWeight;
mesh.bindposes = aNewBindPose;
}
if (textureUvIndex >= 0)
{
mesh.SetUVs(textureUvIndex, aNewTextureUv);
}
mesh.SetUVs(EmissionHashUvIndex, aNewEmissionHash);
mesh.SetUVs(MetallicSmoothnessUvIndex, aNewMetallicSmoothness);
mesh.SetIndices(aNewIndex, topology, 0);
}
private static Vector3[] s_aRenderBoxProxyVertBuffer;
public static void UpdateRenderBoxProxy(ref Mesh mesh, Aabb bounds)
{
if (mesh == null)
{
mesh = new Mesh();
}
if (s_aRenderBoxProxyVertBuffer == null
|| s_aRenderBoxProxyVertBuffer.Length != s_aRenderBoxProxyVert.Length)
{
s_aRenderBoxProxyVertBuffer = new Vector3[s_aRenderBoxProxyVert.Length];
}
Vector3 size = bounds.Size;
Vector3 center = bounds.Center;
for (int i = 0, n = s_aRenderBoxProxyVert.Length; i < n; ++i)
{
s_aRenderBoxProxyVertBuffer[i] = VectorUtil.CompMul(size, s_aRenderBoxProxyVert[i]) + center;
}
#if UNITY_2021_3_OR_NEWER
bool useInvertexIndices = (ResourcesUtil.RenderPipeline != ResourcesUtil.RenderPipelineEnum.HDRP);
#else
bool useInvertexIndices = true;
#endif
int[] aIndex = (useInvertexIndices ? s_aRenderBoxProxyIndexInverted : s_aRenderBoxProxyIndex);
mesh.vertices = s_aRenderBoxProxyVertBuffer;
mesh.SetIndices(aIndex, MeshTopology.Triangles, 0);
mesh.RecalculateBounds();
}
private static Mesh s_invertedUnitBoxMesh;
public static Mesh InvertedUnitBox
{
get
{
if (s_invertedUnitBoxMesh != null)
return s_invertedUnitBoxMesh;
s_invertedUnitBoxMesh = new Mesh();
Vector3[] aVert =
{
new Vector3(-0.5f, -0.5f, -0.5f),
new Vector3(-0.5f, 0.5f, -0.5f),
new Vector3( 0.5f, 0.5f, -0.5f),
new Vector3( 0.5f, -0.5f, -0.5f),
new Vector3(-0.5f, -0.5f, 0.5f),
new Vector3(-0.5f, 0.5f, 0.5f),
new Vector3( 0.5f, 0.5f, 0.5f),
new Vector3( 0.5f, -0.5f, 0.5f),
};
int[] aIndex =
{
0, 2, 1, 0, 3, 2,
3, 6, 2, 3, 7, 6,
7, 5, 6, 7, 4, 5,
4, 1, 5, 4, 0, 1,
1, 6, 5, 1, 2, 6,
0, 7, 3, 0, 4, 7,
};
s_invertedUnitBoxMesh.vertices = aVert;
s_invertedUnitBoxMesh.SetIndices(aIndex, MeshTopology.Triangles, 0);
return s_invertedUnitBoxMesh;
}
}
}
}