using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine.Serialization;
using System.Collections.ObjectModel;
namespace UnityEngine.ProBuilder
{
///
/// A face is composed of a set of triangles, and a material.
///
/// Triangle indexes may point to the same vertex index as long as the vertices are unique to the face. Ie, every vertex that a face references should only be used by that face's indices. To associate vertices that share common attributes (usually position), use the @"UnityEngine.ProBuilder.ProBuilderMesh.sharedIndexes" property.
///
/// ProBuilder automatically manages condensing common vertices in the EditorMeshUtility.Optimize function.
///
[Serializable]
public sealed class Face
{
[FormerlySerializedAs("_indices")]
[SerializeField]
int[] m_Indexes;
///
/// Adjacent faces sharing this smoothingGroup will have their abutting edge normals averaged.
///
[SerializeField]
[FormerlySerializedAs("_smoothingGroup")]
int m_SmoothingGroup;
///
/// If manualUV is false, these parameters determine how this face's vertices are projected to 2d space.
///
[SerializeField]
[FormerlySerializedAs("_uv")]
AutoUnwrapSettings m_Uv;
///
/// What material does this face use.
///
[SerializeField]
[FormerlySerializedAs("_mat")]
Material m_Material;
[SerializeField]
int m_SubmeshIndex;
[SerializeField]
[FormerlySerializedAs("manualUV")]
bool m_ManualUV;
///
/// If this face has had it's UV coordinates done by hand, don't update them with the auto unwrap crowd.
///
public bool manualUV
{
get { return m_ManualUV; }
set { m_ManualUV = value; }
}
///
/// UV element group. Used by the UV editor to group faces.
///
[SerializeField]
internal int elementGroup;
[SerializeField]
int m_TextureGroup;
///
/// What texture group this face belongs to. Used when projecting auto UVs.
///
public int textureGroup
{
get { return m_TextureGroup;}
set { m_TextureGroup = value; }
}
///
/// Return a reference to the triangle indexes that make up this face.
///
internal int[] indexesInternal
{
get { return m_Indexes; }
set
{
if (m_Indexes == null)
throw new ArgumentNullException("value");
if (m_Indexes.Length % 3 != 0)
throw new ArgumentException("Face indexes must be a multiple of 3.");
m_Indexes = value;
InvalidateCache();
}
}
///
/// The triangle indexes that make up this face.
///
public ReadOnlyCollection indexes
{
get { return new ReadOnlyCollection(m_Indexes); }
}
///
/// Set the triangles that compose this face.
///
/// The new triangle array.
public void SetIndexes(int[] array)
{
if (array == null)
throw new ArgumentNullException("array");
int len = array.Length;
if (len % 3 != 0)
throw new ArgumentException("Face indexes must be a multiple of 3.");
m_Indexes = new int[len];
Array.Copy(array, m_Indexes, len);
InvalidateCache();
}
[NonSerialized]
int[] m_DistinctIndexes;
[NonSerialized]
Edge[] m_Edges;
///
/// Returns a reference to the cached distinct indexes (each vertex index is only referenced once in m_DistinctIndexes).
///
internal int[] distinctIndexesInternal
{
get { return m_DistinctIndexes == null ? CacheDistinctIndexes() : m_DistinctIndexes; }
}
///
/// A collection of the vertex indexes that the indexes array references, made distinct.
///
public ReadOnlyCollection distinctIndexes
{
get { return new ReadOnlyCollection(distinctIndexesInternal); }
}
internal Edge[] edgesInternal
{
get { return m_Edges == null ? CacheEdges() : m_Edges; }
}
///
/// Get the perimeter edges that commpose this face.
///
public ReadOnlyCollection edges
{
get { return new ReadOnlyCollection(edgesInternal); }
}
///
/// What smoothing group this face belongs to, if any. This is used to calculate vertex normals.
///
public int smoothingGroup
{
get { return m_SmoothingGroup; }
set { m_SmoothingGroup = value; }
}
///
/// Get the material that face uses.
///
[Obsolete("Face.material is deprecated. Please use submeshIndex instead.")]
public Material material
{
get { return m_Material; }
set { m_Material = value; }
}
public int submeshIndex
{
get { return m_SubmeshIndex; }
set { m_SubmeshIndex = value; }
}
///
/// A reference to the Auto UV mapping parameters.
///
public AutoUnwrapSettings uv
{
get { return m_Uv; }
set { m_Uv = value; }
}
///
/// Accesses the indexes array.
///
///
public int this[int i]
{
get { return indexesInternal[i]; }
}
///
/// Default constructor creates a face with an empty triangles array.
///
public Face()
{
m_SubmeshIndex = 0;
}
///
/// Initialize a Face with a set of triangles and default values.
///
/// The new triangles array.
public Face(int[] array)
{
SetIndexes(array);
m_Uv = AutoUnwrapSettings.tile;
m_Material = BuiltinMaterials.defaultMaterial;
m_SmoothingGroup = Smoothing.smoothingGroupNone;
m_SubmeshIndex = 0;
textureGroup = -1;
elementGroup = 0;
}
[Obsolete("Face.material is deprecated. Please use \"submeshIndex\" instead.")]
internal Face(int[] triangles, Material m, AutoUnwrapSettings u, int smoothing, int texture, int element, bool manualUVs)
{
SetIndexes(triangles);
m_Uv = new AutoUnwrapSettings(u);
m_Material = m;
m_SmoothingGroup = smoothing;
textureGroup = texture;
elementGroup = element;
manualUV = manualUVs;
m_SubmeshIndex = 0;
}
internal Face(int[] triangles, int submeshIndex, AutoUnwrapSettings u, int smoothing, int texture, int element, bool manualUVs)
{
SetIndexes(triangles);
m_Uv = new AutoUnwrapSettings(u);
m_SmoothingGroup = smoothing;
textureGroup = texture;
elementGroup = element;
manualUV = manualUVs;
m_SubmeshIndex = submeshIndex;
}
///
/// Deep copy constructor.
///
/// The Face from which to copy properties.
public Face(Face other)
{
CopyFrom(other);
}
///
/// Copies values from other to this face.
///
/// The Face from which to copy properties.
public void CopyFrom(Face other)
{
if (other == null)
throw new ArgumentNullException("other");
int len = other.indexesInternal.Length;
m_Indexes = new int[len];
Array.Copy(other.indexesInternal, m_Indexes, len);
m_SmoothingGroup = other.smoothingGroup;
m_Uv = new AutoUnwrapSettings(other.uv);
#pragma warning disable 618
m_Material = other.material;
#pragma warning restore 618
manualUV = other.manualUV;
m_TextureGroup = other.textureGroup;
elementGroup = other.elementGroup;
m_SubmeshIndex = other.m_SubmeshIndex;
InvalidateCache();
}
internal void InvalidateCache()
{
m_Edges = null;
m_DistinctIndexes = null;
}
Edge[] CacheEdges()
{
if (m_Indexes == null)
return null;
HashSet dist = new HashSet();
List dup = new List();
for (int i = 0; i < indexesInternal.Length; i += 3)
{
Edge a = new Edge(indexesInternal[i + 0], indexesInternal[i + 1]);
Edge b = new Edge(indexesInternal[i + 1], indexesInternal[i + 2]);
Edge c = new Edge(indexesInternal[i + 2], indexesInternal[i + 0]);
if (!dist.Add(a)) dup.Add(a);
if (!dist.Add(b)) dup.Add(b);
if (!dist.Add(c)) dup.Add(c);
}
dist.ExceptWith(dup);
m_Edges = dist.ToArray();
return m_Edges;
}
int[] CacheDistinctIndexes()
{
if (m_Indexes == null)
return null;
m_DistinctIndexes = m_Indexes.Distinct().ToArray();
return distinctIndexesInternal;
}
///
/// Test if a triangle is contained within the triangles array of this face.
///
///
///
///
///
public bool Contains(int a, int b, int c)
{
for (int i = 0, cnt = indexesInternal.Length; i < cnt; i += 3)
{
if (a == indexesInternal[i + 0]
&& b == indexesInternal[i + 1]
&& c == indexesInternal[i + 2])
return true;
}
return false;
}
///
/// Is this face representable as quad?
///
///
public bool IsQuad()
{
return edgesInternal != null && edgesInternal.Length == 4;
}
///
/// Convert a 2 triangle face to a quad representation.
///
/// A quad (4 indexes), or null if indexes are not able to be represented as a quad.
public int[] ToQuad()
{
if (!IsQuad())
throw new InvalidOperationException("Face is not representable as a quad. Use Face.IsQuad to check for validity.");
int[] quad = new int[4] { edgesInternal[0].a, edgesInternal[0].b, -1, -1 };
if (edgesInternal[1].a == quad[1])
quad[2] = edgesInternal[1].b;
else if (edgesInternal[2].a == quad[1])
quad[2] = edgesInternal[2].b;
else if (edgesInternal[3].a == quad[1])
quad[2] = edgesInternal[3].b;
if (edgesInternal[1].a == quad[2])
quad[3] = edgesInternal[1].b;
else if (edgesInternal[2].a == quad[2])
quad[3] = edgesInternal[2].b;
else if (edgesInternal[3].a == quad[2])
quad[3] = edgesInternal[3].b;
return quad;
}
public override string ToString()
{
System.Text.StringBuilder sb = new System.Text.StringBuilder();
for (int i = 0; i < indexesInternal.Length; i += 3)
{
sb.Append("[");
sb.Append(indexesInternal[i]);
sb.Append(", ");
sb.Append(indexesInternal[i + 1]);
sb.Append(", ");
sb.Append(indexesInternal[i + 2]);
sb.Append("]");
if (i < indexesInternal.Length - 3)
sb.Append(", ");
}
return sb.ToString();
}
///
/// Add offset to each value in the indexes array.
///
/// The value to add to each index.
public void ShiftIndexes(int offset)
{
for (int i = 0, c = m_Indexes.Length; i < c; i++)
m_Indexes[i] += offset;
InvalidateCache();
}
///
/// Find the smallest value in the triangles array.
///
/// The smallest value in the indexes array.
int SmallestIndexValue()
{
int smallest = m_Indexes[0];
for (int i = 1; i < m_Indexes.Length; i++)
{
if (m_Indexes[i] < smallest)
smallest = m_Indexes[i];
}
return smallest;
}
///
/// Finds the smallest value in the indexes array, then offsets by subtracting that value from each index.
///
///
/// ```
/// // sets the indexes array to `{0, 1, 2}`.
/// new Face(3,4,5).ShiftIndexesToZero();
/// ```
///
public void ShiftIndexesToZero()
{
int offset = SmallestIndexValue();
for (int i = 0; i < m_Indexes.Length; i++)
m_Indexes[i] -= offset;
InvalidateCache();
}
///
/// Reverse the order of the triangle array. This has the effect of reversing the direction that this face renders.
///
public void Reverse()
{
Array.Reverse(m_Indexes);
InvalidateCache();
}
internal static void GetIndices(IEnumerable faces, List indices)
{
indices.Clear();
foreach (var face in faces)
{
for (int i = 0, c = face.indexesInternal.Length; i < c; ++i)
indices.Add(face.indexesInternal[i]);
}
}
internal static void GetDistinctIndices(IEnumerable faces, List indices)
{
indices.Clear();
foreach (var face in faces)
{
for (int i = 0, c = face.distinctIndexesInternal.Length; i < c; ++i)
indices.Add(face.distinctIndexesInternal[i]);
}
}
}
}