using UnityEngine;
using System;
using System.Linq;
using System.Collections.Generic;
using System.Collections;
using System.Text;
namespace UnityEngine.ProBuilder
{
///
/// Functions for generating mesh attributes and various other mesh utilities.
///
public static class MeshUtility
{
///
/// Create an array of @"UnityEngine.ProBuilder.Vertex" values that are ordered as individual triangles. This modifies the source mesh to match the new individual triangles format.
///
/// The mesh to extract vertices from, and apply per-triangle topology to.
/// A @"UnityEngine.ProBuilder.Vertex" array of the per-triangle vertices.
internal static Vertex[] GeneratePerTriangleMesh(Mesh mesh)
{
if (mesh == null)
throw new ArgumentNullException("mesh");
Vertex[] vertices = mesh.GetVertices();
int smc = mesh.subMeshCount;
Vertex[] tv = new Vertex[mesh.triangles.Length];
int[][] triangles = new int[smc][];
int triIndex = 0;
for (int s = 0; s < smc; s++)
{
triangles[s] = mesh.GetTriangles(s);
int tl = triangles[s].Length;
for (int i = 0; i < tl; i++)
{
tv[triIndex++] = new Vertex(vertices[triangles[s][i]]);
triangles[s][i] = triIndex - 1;
}
}
Vertex.SetMesh(mesh, tv);
mesh.subMeshCount = smc;
for (int s = 0; s < smc; s++)
mesh.SetTriangles(triangles[s], s);
return tv;
}
///
/// Generate tangents and apply them.
///
/// The UnityEngine.Mesh mesh target.
public static void GenerateTangent(Mesh mesh)
{
if (mesh == null)
throw new System.ArgumentNullException("mesh");
// http://answers.unity3d.com/questions/7789/calculating-tangents-vector4.html
// speed up math by copying the mesh arrays
int[] triangles = mesh.triangles;
Vector3[] vertices = mesh.vertices;
Vector2[] uv = mesh.uv;
Vector3[] normals = mesh.normals;
//variable definitions
int triangleCount = triangles.Length;
int vertexCount = vertices.Length;
Vector3[] tan1 = new Vector3[vertexCount];
Vector3[] tan2 = new Vector3[vertexCount];
Vector4[] tangents = new Vector4[vertexCount];
for (long a = 0; a < triangleCount; a += 3)
{
long i1 = triangles[a + 0];
long i2 = triangles[a + 1];
long i3 = triangles[a + 2];
Vector3 v1 = vertices[i1];
Vector3 v2 = vertices[i2];
Vector3 v3 = vertices[i3];
Vector2 w1 = uv[i1];
Vector2 w2 = uv[i2];
Vector2 w3 = uv[i3];
float x1 = v2.x - v1.x;
float x2 = v3.x - v1.x;
float y1 = v2.y - v1.y;
float y2 = v3.y - v1.y;
float z1 = v2.z - v1.z;
float z2 = v3.z - v1.z;
float s1 = w2.x - w1.x;
float s2 = w3.x - w1.x;
float t1 = w2.y - w1.y;
float t2 = w3.y - w1.y;
float r = 1.0f / (s1 * t2 - s2 * t1);
Vector3 sdir = new Vector3((t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r, (t2 * z1 - t1 * z2) * r);
Vector3 tdir = new Vector3((s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r, (s1 * z2 - s2 * z1) * r);
tan1[i1] += sdir;
tan1[i2] += sdir;
tan1[i3] += sdir;
tan2[i1] += tdir;
tan2[i2] += tdir;
tan2[i3] += tdir;
}
for (long a = 0; a < vertexCount; ++a)
{
Vector3 n = normals[a];
Vector3 t = tan1[a];
Vector3.OrthoNormalize(ref n, ref t);
tangents[a].x = t.x;
tangents[a].y = t.y;
tangents[a].z = t.z;
tangents[a].w = (Vector3.Dot(Vector3.Cross(n, t), tan2[a]) < 0.0f) ? -1.0f : 1.0f;
}
mesh.tangents = tangents;
}
///
/// Performs a deep copy of a mesh and returns a new mesh object.
///
/// The source mesh.
/// A new UnityEngine.Mesh object with the same values as source.
public static Mesh DeepCopy(Mesh source)
{
Mesh m = new Mesh();
CopyTo(source, m);
return m;
}
///
/// Copy source mesh values to destination mesh.
///
/// The mesh from which to copy attributes.
/// The destination mesh to copy attribute values to.
/// Throws if source or destination is null.
public static void CopyTo(Mesh source, Mesh destination)
{
if (source == null)
throw new System.ArgumentNullException("source");
if (destination == null)
throw new System.ArgumentNullException("destination");
Vector3[] v = new Vector3[source.vertices.Length];
int[][] t = new int[source.subMeshCount][];
Vector2[] u = new Vector2[source.uv.Length];
Vector2[] u2 = new Vector2[source.uv2.Length];
Vector4[] tan = new Vector4[source.tangents.Length];
Vector3[] n = new Vector3[source.normals.Length];
Color32[] c = new Color32[source.colors32.Length];
System.Array.Copy(source.vertices, v, v.Length);
for (int i = 0; i < t.Length; i++)
t[i] = source.GetTriangles(i);
System.Array.Copy(source.uv, u, u.Length);
System.Array.Copy(source.uv2, u2, u2.Length);
System.Array.Copy(source.normals, n, n.Length);
System.Array.Copy(source.tangents, tan, tan.Length);
System.Array.Copy(source.colors32, c, c.Length);
destination.Clear();
destination.name = source.name;
destination.vertices = v;
destination.subMeshCount = t.Length;
for (int i = 0; i < t.Length; i++)
destination.SetTriangles(t[i], i);
destination.uv = u;
destination.uv2 = u2;
destination.tangents = tan;
destination.normals = n;
destination.colors32 = c;
}
///
/// Get a mesh attribute from either the MeshFilter.sharedMesh or the MeshRenderer.additionalVertexStreams mesh. The additional vertex stream mesh has priority.
///
/// The type of the attribute to fetch.
/// The GameObject with the MeshFilter and (optional) MeshRenderer to search for mesh attributes.
/// The function used to extract mesh attribute.
/// A List of the mesh attribute values from the Additional Vertex Streams mesh if it exists and contains the attribute, or the MeshFilter.sharedMesh attribute values.
internal static T GetMeshChannel(GameObject gameObject, Func attributeGetter) where T : IList
{
if (gameObject == null)
throw new System.ArgumentNullException("gameObject");
if (attributeGetter == null)
throw new System.ArgumentNullException("attributeGetter");
MeshFilter mf = gameObject.GetComponent();
Mesh mesh = mf != null ? mf.sharedMesh : null;
T res = default(T);
if (mesh == null)
return res;
int vertexCount = mesh.vertexCount;
#if !UNITY_4_6 && !UNITY_4_7
MeshRenderer renderer = gameObject.GetComponent();
Mesh vertexStream = renderer != null ? renderer.additionalVertexStreams : null;
if (vertexStream != null)
{
res = attributeGetter(vertexStream);
if (res != null && res.Count == vertexCount)
return res;
}
#endif
res = attributeGetter(mesh);
return res != null && res.Count == vertexCount ? res : default(T);
}
///
/// Print a detailed string summary of the mesh attributes.
///
///
///
public static string Print(Mesh mesh)
{
if (mesh == null)
throw new ArgumentNullException("mesh");
System.Text.StringBuilder sb = new System.Text.StringBuilder();
sb.AppendLine(string.Format("vertices: {0}\ntriangles: {1}\nsubmeshes: {2}", mesh.vertexCount, mesh.triangles.Length, mesh.subMeshCount));
sb.AppendLine(string.Format(" {0,-28}{1,-28}{2,-28}{3,-28}{4,-28}{5,-28}{6,-28}{7,-28}",
"Positions",
"Normals",
"Colors",
"Tangents",
"UV0",
"UV2",
"UV3",
"UV4"));
Vector3[] positions = mesh.vertices;
Vector3[] normals = mesh.normals;
Color[] colors = mesh.colors;
Vector4[] tangents = mesh.tangents;
List uv0 = new List();
Vector2[] uv2 = mesh.uv2;
List uv3 = new List();
List uv4 = new List();
mesh.GetUVs(0, uv0);
mesh.GetUVs(2, uv3);
mesh.GetUVs(3, uv4);
if (positions != null && positions.Count() != mesh.vertexCount)
positions = null;
if (colors != null && colors.Count() != mesh.vertexCount)
colors = null;
if (tangents != null && tangents.Count() != mesh.vertexCount)
tangents = null;
if (uv0.Count() != mesh.vertexCount)
uv0 = null;
if (uv2.Count() != mesh.vertexCount)
uv2 = null;
if (uv3.Count() != mesh.vertexCount)
uv3 = null;
if (uv4.Count() != mesh.vertexCount)
uv4 = null;
for (int i = 0, c = mesh.vertexCount; i < c; i++)
{
sb.AppendLine(string.Format("{8,-5}{0,-28}{1,-28}{2,-28}{3,-28}{4,-28}{5,-28}{6,-28}{7,-28}",
positions == null ? "null" : string.Format("{0:F3}, {1:F3}, {2:F3}", positions[i].x, positions[i].y, positions[i].z),
normals == null ? "null" : string.Format("{0:F3}, {1:F3}, {2:F3}", normals[i].x, normals[i].y, normals[i].z),
colors == null ? "null" : string.Format("{0:F2}, {1:F2}, {2:F2}, {3:F2}", colors[i].r, colors[i].g, colors[i].b, colors[i].a),
tangents == null ? "null" : string.Format("{0:F2}, {1:F2}, {2:F2}, {3:F2}", tangents[i].x, tangents[i].y, tangents[i].z, tangents[i].w),
uv0 == null ? "null" : string.Format("{0:F2}, {1:F2}, {2:F2}, {3:F2}", uv0[i].x, uv0[i].y, uv0[i].z, uv0[i].w),
uv2 == null ? "null" : string.Format("{0:F2}, {1:F2}", uv2[i].x, uv2[i].y),
uv3 == null ? "null" : string.Format("{0:F2}, {1:F2}, {2:F2}, {3:F2}", uv3[i].x, uv3[i].y, uv3[i].z, uv3[i].w),
uv4 == null ? "null" : string.Format("{0:F2}, {1:F2}, {2:F2}, {3:F2}", uv4[i].x, uv4[i].y, uv4[i].z, uv4[i].w),
i));
}
for (int i = 0; i < mesh.triangles.Length; i += 3)
sb.AppendLine(string.Format("{0}, {1}, {2}", mesh.triangles[i], mesh.triangles[i + 1], mesh.triangles[i + 2]));
return sb.ToString();
}
///
/// Get the number of indexes this mesh contains.
///
/// The source mesh to sum submesh index counts from.
/// The count of all indexes contained within this meshes submeshes.
public static uint GetIndexCount(Mesh mesh)
{
uint sum = 0;
if (mesh == null)
return sum;
for (int i = 0, c = mesh.subMeshCount; i < c; i++)
sum += mesh.GetIndexCount(i);
return sum;
}
///
/// Get the number of triangles or quads this mesh contains. Other mesh topologies are not considered.
///
/// The source mesh to sum submesh primitive counts from.
/// The count of all triangles or quads contained within this meshes submeshes.
public static uint GetPrimitiveCount(Mesh mesh)
{
uint sum = 0;
if (mesh == null)
return sum;
for (int i = 0, c = mesh.subMeshCount; i < c; i++)
{
if (mesh.GetTopology(i) == MeshTopology.Triangles)
sum += mesh.GetIndexCount(i) / 3;
else if (mesh.GetTopology(i) == MeshTopology.Quads)
sum += mesh.GetIndexCount(i) / 4;
}
return sum;
}
///
/// Compile a UnityEngine.Mesh from a ProBuilderMesh.
///
/// The mesh source.
/// Destination UnityEngine.Mesh.
/// If specified, the function will try to create topology matching the reqested format (and falling back on triangles where necessary).
/// The resulting material array from the compiled faces array. This is suitable to assign to the MeshRenderer.sharedMaterials property.
public static void Compile(ProBuilderMesh probuilderMesh, Mesh targetMesh, MeshTopology preferredTopology = MeshTopology.Triangles)
{
if (probuilderMesh == null)
throw new ArgumentNullException("probuilderMesh");
if (targetMesh == null)
throw new ArgumentNullException("targetMesh");
targetMesh.Clear();
targetMesh.vertices = probuilderMesh.positionsInternal;
targetMesh.uv = probuilderMesh.texturesInternal;
if (probuilderMesh.HasArrays(MeshArrays.Texture2))
{
List uvChannel = new List();
probuilderMesh.GetUVs(2, uvChannel);
targetMesh.SetUVs(2, uvChannel);
}
if (probuilderMesh.HasArrays(MeshArrays.Texture3))
{
List uvChannel = new List();
probuilderMesh.GetUVs(3, uvChannel);
targetMesh.SetUVs(3, uvChannel);
}
targetMesh.normals = probuilderMesh.GetNormals();
targetMesh.tangents = probuilderMesh.GetTangents();
if (probuilderMesh.HasArrays(MeshArrays.Color))
targetMesh.colors = probuilderMesh.colorsInternal;
var materialCount = probuilderMesh.GetComponent().sharedMaterials.Length;
var submeshes = Submesh.GetSubmeshes(probuilderMesh.facesInternal, materialCount, preferredTopology);
targetMesh.subMeshCount = submeshes.Length;
for (int i = 0; i < targetMesh.subMeshCount; i++)
targetMesh.SetIndices(submeshes[i].m_Indexes, submeshes[i].m_Topology, i, false);
targetMesh.name = string.Format("pb_Mesh{0}", probuilderMesh.id);
}
///
/// Creates a new array of vertices with values from a UnityEngine.Mesh.
///
/// The source mesh.
/// An array of vertices.
public static Vertex[] GetVertices(this Mesh mesh)
{
if (mesh == null)
return null;
int vertexCount = mesh.vertexCount;
Vertex[] v = new Vertex[vertexCount];
Vector3[] positions = mesh.vertices;
Color[] colors = mesh.colors;
Vector3[] normals = mesh.normals;
Vector4[] tangents = mesh.tangents;
Vector2[] uv0s = mesh.uv;
Vector2[] uv2s = mesh.uv2;
List uv3s = new List();
List uv4s = new List();
mesh.GetUVs(2, uv3s);
mesh.GetUVs(3, uv4s);
bool _hasPositions = positions != null && positions.Count() == vertexCount;
bool _hasColors = colors != null && colors.Count() == vertexCount;
bool _hasNormals = normals != null && normals.Count() == vertexCount;
bool _hasTangents = tangents != null && tangents.Count() == vertexCount;
bool _hasUv0 = uv0s != null && uv0s.Count() == vertexCount;
bool _hasUv2 = uv2s != null && uv2s.Count() == vertexCount;
bool _hasUv3 = uv3s.Count() == vertexCount;
bool _hasUv4 = uv4s.Count() == vertexCount;
for (int i = 0; i < vertexCount; i++)
{
v[i] = new Vertex();
if (_hasPositions)
v[i].position = positions[i];
if (_hasColors)
v[i].color = colors[i];
if (_hasNormals)
v[i].normal = normals[i];
if (_hasTangents)
v[i].tangent = tangents[i];
if (_hasUv0)
v[i].uv0 = uv0s[i];
if (_hasUv2)
v[i].uv2 = uv2s[i];
if (_hasUv3)
v[i].uv3 = uv3s[i];
if (_hasUv4)
v[i].uv4 = uv4s[i];
}
return v;
}
///
/// Merge coincident vertices where possible, optimizing the vertex count of a UnityEngine.Mesh.
///
/// The mesh to optimize.
///
/// If provided these values are used in place of extracting attributes from the Mesh.
///
/// This is a performance optimization for when this array already exists. If not provided this array will be
/// automatically generated for you.
///
public static void CollapseSharedVertices(Mesh mesh, Vertex[] vertices = null)
{
if (mesh == null)
throw new System.ArgumentNullException("mesh");
if (vertices == null)
vertices = mesh.GetVertices();
int smc = mesh.subMeshCount;
List> subVertices = new List>();
int[][] tris = new int[smc][];
int subIndex = 0;
for (int i = 0; i < smc; ++i)
{
tris[i] = mesh.GetTriangles(i);
Dictionary newVertices = new Dictionary();
for (int n = 0; n < tris[i].Length; n++)
{
Vertex v = vertices[tris[i][n]];
int index;
if (newVertices.TryGetValue(v, out index))
{
tris[i][n] = index;
}
else
{
tris[i][n] = subIndex;
newVertices.Add(v, subIndex);
subIndex++;
}
}
subVertices.Add(newVertices);
}
Vertex[] collapsed = subVertices.SelectMany(x => x.Keys).ToArray();
Vertex.SetMesh(mesh, collapsed);
mesh.subMeshCount = smc;
for (int i = 0; i < smc; i++)
mesh.SetTriangles(tris[i], i);
}
internal static string SanityCheck(ProBuilderMesh mesh)
{
return SanityCheck(mesh.GetVertices());
}
///
/// Check mesh for invalid properties.
///
///
/// Returns true if mesh is valid, false if a problem was found.
internal static string SanityCheck(Mesh mesh)
{
return SanityCheck(mesh.GetVertices());
}
///
/// Check mesh for invalid properties.
///
/// Returns true if mesh is valid, false if a problem was found.
internal static string SanityCheck(IList vertices)
{
var sb = new StringBuilder();
for (int i = 0, c = vertices.Count; i < c; i++)
{
var vertex = vertices[i];
if (Math.IsNumber(vertex.position)
&& Math.IsNumber(vertex.color)
&& Math.IsNumber(vertex.uv0)
&& Math.IsNumber(vertex.normal)
&& Math.IsNumber(vertex.tangent)
&& Math.IsNumber(vertex.uv2)
&& Math.IsNumber(vertex.uv3)
&& Math.IsNumber(vertex.uv4))
continue;
sb.AppendFormat("vertex {0} contains invalid values:\n{1}\n\n", i, vertex.ToString());
}
return sb.ToString();
}
}
}