using UnityEngine;
using System.Collections.Generic;
using System.Linq;
using UnityEngine.Serialization;
using System;
using System.Collections.ObjectModel;
using UnityEngine.Rendering;
namespace UnityEngine.ProBuilder
{
///
/// Represents the ProBuilder MeshFilter component, which is responsible for storing all
/// the data necessary for editing and compiling objects.
///
// The double "//" sets this component as hidden in the menu, but is used by ObjectNames.cs to get the component name.
[AddComponentMenu("//ProBuilder MeshFilter")]
// Don't include MeshFilter in the required components because it gets registered with serialization before we have a
// chance to mark it with the correct HideFlags.
[RequireComponent(typeof(MeshRenderer))]
[DisallowMultipleComponent, ExecuteInEditMode, ExcludeFromPreset, ExcludeFromObjectFactory]
// [MonoBehaviourIcon("Packages/com.unity.probuilder/Content/Icons/Scripts/ProBuilderMesh@64.png")]
public sealed partial class ProBuilderMesh : MonoBehaviour
{
#if ENABLE_DRIVEN_PROPERTIES
internal const HideFlags k_MeshFilterHideFlags = HideFlags.DontSave | HideFlags.HideInInspector | HideFlags.NotEditable;
#else
internal const HideFlags k_MeshFilterHideFlags = HideFlags.HideInInspector | HideFlags.NotEditable;
#endif
///
/// Stores the maximum number of UV channels that the ProBuilderMesh format supports.
///
const int k_UVChannelCount = 4;
///
/// Represents the current mesh format version. This is used to run expensive upgrade functions once in ToMesh().
///
internal const int k_MeshFormatVersion = k_MeshFormatVersionAutoUVScaleOffset;
internal const int k_MeshFormatVersionSubmeshMaterialRefactor = 1;
internal const int k_MeshFormatVersionAutoUVScaleOffset = 2;
///
/// The maximum number of vertices that a ProBuilderMesh can accomodate.
///
public const uint maxVertexCount = ushort.MaxValue;
// MeshFormatVersion is used to deprecate and upgrade serialized data.
[SerializeField]
int m_MeshFormatVersion;
[SerializeField]
[FormerlySerializedAs("_quads")]
Face[] m_Faces;
[SerializeField]
[FormerlySerializedAs("_sharedIndices")]
[FormerlySerializedAs("m_SharedVertexes")]
SharedVertex[] m_SharedVertices;
[Flags]
enum CacheValidState : byte
{
SharedVertex = 1 << 0,
SharedTexture = 1 << 1
}
[NonSerialized]
CacheValidState m_CacheValid;
[NonSerialized]
Dictionary m_SharedVertexLookup;
[SerializeField]
[FormerlySerializedAs("_sharedIndicesUV")]
SharedVertex[] m_SharedTextures;
[NonSerialized]
Dictionary m_SharedTextureLookup;
[SerializeField]
[FormerlySerializedAs("_vertices")]
Vector3[] m_Positions;
[SerializeField]
[FormerlySerializedAs("_uv")]
Vector2[] m_Textures0;
[SerializeField]
[FormerlySerializedAs("_uv3")]
List m_Textures2;
[SerializeField]
[FormerlySerializedAs("_uv4")]
List m_Textures3;
[SerializeField]
[FormerlySerializedAs("_tangents")]
Vector4[] m_Tangents;
[NonSerialized]
Vector3[] m_Normals;
[SerializeField]
[FormerlySerializedAs("_colors")]
Color[] m_Colors;
///
/// If false, ProBuilder will automatically create and scale colliders.
///
public bool userCollisions { get; set; }
[FormerlySerializedAs("unwrapParameters")]
[SerializeField]
UnwrapParameters m_UnwrapParameters;
///
/// UV2 generation parameters.
///
public UnwrapParameters unwrapParameters
{
get { return m_UnwrapParameters; }
set { m_UnwrapParameters = value; }
}
[FormerlySerializedAs("dontDestroyMeshOnDelete")]
[SerializeField]
bool m_PreserveMeshAssetOnDestroy;
///
/// If "Meshes are Assets" feature is enabled, this is used to relate pb_Objects to stored meshes.
///
[SerializeField]
internal string assetGuid;
[SerializeField]
Mesh m_Mesh;
[NonSerialized]
MeshRenderer m_MeshRenderer;
#pragma warning disable 109
internal new MeshRenderer renderer
{
get
{
if (!gameObject.TryGetComponent(out m_MeshRenderer))
return null;
return m_MeshRenderer;
}
}
#pragma warning restore 109
[NonSerialized]
MeshFilter m_MeshFilter;
#pragma warning disable 109
internal new MeshFilter filter
{
get
{
if (m_MeshFilter == null)
{
if (!gameObject.TryGetComponent(out m_MeshFilter))
return null;
#if UNITY_EDITOR
m_MeshFilter.hideFlags = k_MeshFilterHideFlags;
#endif
}
return m_MeshFilter;
}
}
#pragma warning restore 109
internal const ushort k_UnitializedVersionIndex = 0;
///
/// Tracks each time the ToMesh() and Refresh() functions are called to modify the mesh.
/// This is a simple uint number used to check whether two versions of the ProBuilderMesh are the same or not.
///
[SerializeField]
ushort m_VersionIndex = k_UnitializedVersionIndex;
// k_NewMeshVersionIndex is a reserved value indicating that this is a new mesh instance. It is used to
// differentiate between mesh creation and duplication.
[NonSerialized]
ushort m_InstanceVersionIndex = k_UnitializedVersionIndex;
internal ushort versionIndex => m_VersionIndex;
internal ushort nonSerializedVersionIndex => m_InstanceVersionIndex;
internal struct NonVersionedEditScope : IDisposable
{
readonly ProBuilderMesh m_Mesh;
readonly ushort m_VersionIndex, m_InstanceVersionIndex;
public NonVersionedEditScope(ProBuilderMesh mesh)
{
m_Mesh = mesh;
m_VersionIndex = mesh.versionIndex;
m_InstanceVersionIndex = mesh.m_InstanceVersionIndex;
}
public void Dispose()
{
m_Mesh.m_VersionIndex = m_VersionIndex;
m_Mesh.m_InstanceVersionIndex = m_InstanceVersionIndex;
}
}
///
/// Gets or sets whether to destroy the mesh asset if this ProBuilderMesh is deleted.
///
/// In the Editor, when you delete a ProBuilderMesh you usually also want to destroy the mesh asset.
/// However, there are situations you'd want to keep the mesh around, like when stripping ProBuilder scripts.
///
/// True to keep the mesh asset; false to destroy it.
public bool preserveMeshAssetOnDestroy
{
get { return m_PreserveMeshAssetOnDestroy; }
set { m_PreserveMeshAssetOnDestroy = value; }
}
///
/// Tests whether the mesh contains the requested arrays.
///
/// A flag containing the array types that a ProBuilder mesh stores.
/// True if all arrays in the flag are present, false if not.
public bool HasArrays(MeshArrays channels)
{
bool missing = false;
int vc = vertexCount;
missing |= (channels & MeshArrays.Position) == MeshArrays.Position && m_Positions == null;
missing |= (channels & MeshArrays.Normal) == MeshArrays.Normal && (m_Normals == null || m_Normals.Length != vc);
missing |= (channels & MeshArrays.Texture0) == MeshArrays.Texture0 && (m_Textures0 == null || m_Textures0.Length != vc);
missing |= (channels & MeshArrays.Texture2) == MeshArrays.Texture2 && (m_Textures2 == null || m_Textures2.Count != vc);
missing |= (channels & MeshArrays.Texture3) == MeshArrays.Texture3 && (m_Textures3 == null || m_Textures3.Count != vc);
missing |= (channels & MeshArrays.Color) == MeshArrays.Color && (m_Colors == null || m_Colors.Length != vc);
missing |= (channels & MeshArrays.Tangent) == MeshArrays.Tangent && (m_Tangents == null || m_Tangents.Length != vc);
// UV2 is a special case. It is not stored in ProBuilderMesh, does not necessarily match the vertex count,
// and it has a cost to check.
if ((channels & MeshArrays.Texture1) == MeshArrays.Texture1 && mesh != null)
{
#if UNITY_2019_3_OR_NEWER
missing |= !mesh.HasVertexAttribute(VertexAttribute.TexCoord1);
#else
var m_Textures1 = m_Mesh.uv2;
missing |= (m_Textures1 == null || m_Textures1.Length < 3);
#endif
}
return !missing;
}
internal Face[] facesInternal
{
get { return m_Faces; }
set { m_Faces = value; }
}
///
/// Gets or sets the faces that ProBuilder uses to compile a mesh.
///
/// Meshes are composed of vertices and faces. Faces primarily contain triangles and material information.
///
/// A collection of the Face objects that make up this mesh.
public IList faces
{
get { return new ReadOnlyCollection(m_Faces); }
set
{
if (value == null)
throw new ArgumentNullException("value");
m_Faces = value.ToArray();
}
}
internal void InvalidateSharedVertexLookup()
{
if (m_SharedVertexLookup == null)
m_SharedVertexLookup = new Dictionary();
m_SharedVertexLookup.Clear();
m_CacheValid &= ~CacheValidState.SharedVertex;
}
internal void InvalidateSharedTextureLookup()
{
if (m_SharedTextureLookup == null)
m_SharedTextureLookup = new Dictionary();
m_SharedTextureLookup.Clear();
m_CacheValid &= ~CacheValidState.SharedTexture;
}
internal void InvalidateFaces()
{
if (m_Faces == null)
{
m_Faces = new Face[0];
return;
}
foreach (var face in faces)
face.InvalidateCache();
}
internal void InvalidateCaches()
{
InvalidateSharedVertexLookup();
InvalidateSharedTextureLookup();
InvalidateFaces();
m_SelectedCacheDirty = true;
}
internal SharedVertex[] sharedVerticesInternal
{
get { return m_SharedVertices; }
set
{
m_SharedVertices = value;
InvalidateSharedVertexLookup();
}
}
///
/// Gets or sets the sharedVertices for this ProBuilderMesh object.
///
/// ProBuilder makes the assumption that no references a vertex used by another.
/// However, ProBuilder needs to be able to associate vertices in the Editor for many operations.
/// These vertices are usually called [coincident](../manual/gloss.html#coincident), or "shared" vertices.
/// ProBuilder manages these associations with the sharedIndexes array.
///
/// Each array contains a list of triangles that points to vertices considered to be coincident.
/// When ProBuilder compiles a from the ProBuilderMesh, it condenses
/// these vertices to a single vertex where possible.
///
/// The shared (or common) index array for this mesh.
public IList sharedVertices
{
get { return new ReadOnlyCollection(m_SharedVertices); }
set
{
if (value == null)
throw new ArgumentNullException("value");
int len = value.Count;
m_SharedVertices = new SharedVertex[len];
for (var i = 0; i < len; i++)
m_SharedVertices[i] = new SharedVertex(value[i]);
InvalidateSharedVertexLookup();
}
}
internal Dictionary sharedVertexLookup
{
get
{
if ((m_CacheValid & CacheValidState.SharedVertex) != CacheValidState.SharedVertex)
{
if (m_SharedVertexLookup == null)
m_SharedVertexLookup = new Dictionary();
SharedVertex.GetSharedVertexLookup(m_SharedVertices, m_SharedVertexLookup);
m_CacheValid |= CacheValidState.SharedVertex;
}
return m_SharedVertexLookup;
}
}
///
/// Sets the sharedIndexes array for this mesh with a lookup dictionary.
///
/// The new sharedIndexes array.
///
internal void SetSharedVertices(IEnumerable> indexes)
{
if (indexes == null)
throw new ArgumentNullException("indexes");
m_SharedVertices = SharedVertex.ToSharedVertices(indexes);
InvalidateSharedVertexLookup();
}
internal SharedVertex[] sharedTextures
{
get { return m_SharedTextures; }
set
{
m_SharedTextures = value;
InvalidateSharedTextureLookup();
}
}
internal Dictionary sharedTextureLookup
{
get
{
if ((m_CacheValid & CacheValidState.SharedTexture) != CacheValidState.SharedTexture)
{
m_CacheValid |= CacheValidState.SharedTexture;
if (m_SharedTextureLookup == null)
m_SharedTextureLookup = new Dictionary();
SharedVertex.GetSharedVertexLookup(m_SharedTextures, m_SharedTextureLookup);
}
return m_SharedTextureLookup;
}
}
internal void SetSharedTextures(IEnumerable> indexes)
{
if (indexes == null)
throw new ArgumentNullException("indexes");
m_SharedTextures = SharedVertex.ToSharedVertices(indexes);
InvalidateSharedTextureLookup();
}
internal Vector3[] positionsInternal
{
get { return m_Positions; }
set { m_Positions = value; }
}
///
/// Gets or sets the vertex positions that compose this mesh.
///
public IList positions
{
get { return new ReadOnlyCollection(m_Positions); }
set
{
if (value == null)
throw new ArgumentNullException("value");
m_Positions = value.ToArray();
}
}
///
/// Creates a new array of vertices with values from a ProBuilderMesh component.
///
/// An optional list of indices used to designate the subset of vertices values to retrieve from the mesh in the array.
/// An array of vertices matching either the specified list in the `indexes` parameter. If the `indexes` parameter contains no list, this returns all mesh vertices.
public Vertex[] GetVertices(IList indexes = null)
{
int meshVertexCount = vertexCount;
int vc = indexes != null ? indexes.Count : vertexCount;
Vertex[] v = new Vertex[vc];
Vector3[] positions = positionsInternal;
Color[] colors = colorsInternal;
Vector2[] uv0s = texturesInternal;
Vector4[] tangents = GetTangents();
Vector3[] normals = GetNormals();
Vector2[] uv2s = mesh != null ? mesh.uv2 : null;
List uv3s = new List();
List uv4s = new List();
GetUVs(2, uv3s);
GetUVs(3, uv4s);
bool _hasPositions = positions != null && positions.Length == meshVertexCount;
bool _hasColors = colors != null && colors.Length == meshVertexCount;
bool _hasNormals = normals != null && normals.Length == meshVertexCount;
bool _hasTangents = tangents != null && tangents.Length == meshVertexCount;
bool _hasUv0 = uv0s != null && uv0s.Length == meshVertexCount;
bool _hasUv2 = uv2s != null && uv2s.Length == meshVertexCount;
bool _hasUv3 = uv3s.Count == meshVertexCount;
bool _hasUv4 = uv4s.Count == meshVertexCount;
for (int i = 0; i < vc; i++)
{
v[i] = new Vertex();
int ind = indexes == null ? i : indexes[i];
if (_hasPositions)
v[i].position = positions[ind];
if (_hasColors)
v[i].color = colors[ind];
if (_hasNormals)
v[i].normal = normals[ind];
if (_hasTangents)
v[i].tangent = tangents[ind];
if (_hasUv0)
v[i].uv0 = uv0s[ind];
if (_hasUv2)
v[i].uv2 = uv2s[ind];
if (_hasUv3)
v[i].uv3 = uv3s[ind];
if (_hasUv4)
v[i].uv4 = uv4s[ind];
}
return v;
}
///
/// Returns a list of vertices from a ProBuilderMesh component.
///
/// The list to populate.
internal void GetVerticesInList(IList vertices)
{
int vc = vertexCount;
vertices.Clear();
Vector3[] positions = positionsInternal;
Color[] colors = colorsInternal;
Vector2[] uv0s = texturesInternal;
Vector4[] tangents = GetTangents();
Vector3[] normals = GetNormals();
Vector2[] uv2s = mesh != null ? mesh.uv2 : null;
List uv3s = new List();
List uv4s = new List();
GetUVs(2, uv3s);
GetUVs(3, uv4s);
bool _hasPositions = positions != null && positions.Length == vc;
bool _hasColors = colors != null && colors.Length == vc;
bool _hasNormals = normals != null && normals.Length == vc;
bool _hasTangents = tangents != null && tangents.Length == vc;
bool _hasUv0 = uv0s != null && uv0s.Length == vc;
bool _hasUv2 = uv2s != null && uv2s.Length == vc;
bool _hasUv3 = uv3s.Count == vc;
bool _hasUv4 = uv4s.Count == vc;
for (int i = 0; i < vc; i++)
{
vertices.Add(new Vertex());
if (_hasPositions)
vertices[i].position = positions[i];
if (_hasColors)
vertices[i].color = colors[i];
if (_hasNormals)
vertices[i].normal = normals[i];
if (_hasTangents)
vertices[i].tangent = tangents[i];
if (_hasUv0)
vertices[i].uv0 = uv0s[i];
if (_hasUv2)
vertices[i].uv2 = uv2s[i];
if (_hasUv3)
vertices[i].uv3 = uv3s[i];
if (_hasUv4)
vertices[i].uv4 = uv4s[i];
}
}
///
/// Sets the vertex element arrays on this mesh.
///
/// The new vertex array.
///
/// Optionally indicate whether to apply elements to the .
/// Note that you should only use this when the mesh is in its original state, not optimized
/// (that is, when it won't affect triangles which can be
/// optimized).
///
public void SetVertices(IList vertices, bool applyMesh = false)
{
if (vertices == null)
throw new ArgumentNullException("vertices");
var first = vertices.FirstOrDefault();
if (first == null || !first.HasArrays(MeshArrays.Position))
{
Clear();
return;
}
Vector3[] position;
Color[] color;
Vector3[] normal;
Vector4[] tangent;
Vector2[] uv0;
Vector2[] uv2;
List uv3;
List uv4;
Vertex.GetArrays(vertices, out position, out color, out uv0, out normal, out tangent, out uv2, out uv3, out uv4);
m_Positions = position;
m_Colors = color;
m_Normals = normal;
m_Tangents = tangent;
m_Textures0 = uv0;
m_Textures2 = uv3;
m_Textures3 = uv4;
if (applyMesh)
{
Mesh umesh = mesh;
if (first.HasArrays(MeshArrays.Position))
umesh.vertices = position;
if (first.HasArrays(MeshArrays.Color))
umesh.colors = color;
if (first.HasArrays(MeshArrays.Texture0))
umesh.uv = uv0;
if (first.HasArrays(MeshArrays.Normal))
umesh.normals = normal;
if (first.HasArrays(MeshArrays.Tangent))
umesh.tangents = tangent;
if (first.HasArrays(MeshArrays.Texture1))
umesh.uv2 = uv2;
if (first.HasArrays(MeshArrays.Texture2))
umesh.SetUVs(2, uv3);
if (first.HasArrays(MeshArrays.Texture3))
umesh.SetUVs(3, uv4);
IncrementVersionIndex();
}
}
///
/// Gets the normals for this mesh.
///
///
///
/// A collection of normals for this mesh.
public IList normals
{
get { return m_Normals != null ? new ReadOnlyCollection(m_Normals) : null; }
}
internal Vector3[] normalsInternal
{
get { return m_Normals; }
set { m_Normals = value; }
}
///
/// Gets the normals array for this mesh.
///
/// An array of normals for this mesh.
public Vector3[] GetNormals()
{
if (!HasArrays(MeshArrays.Normal))
Normals.CalculateNormals(this);
return normals.ToArray();
}
internal Color[] colorsInternal
{
get { return m_Colors; }
set { m_Colors = value; }
}
///
/// Gets or sets a collecton of the vertex colors for this mesh.
/// When setting, the value must match the length of the positions.
///
/// A collection of colors for this mesh.
public IList colors
{
get { return m_Colors != null ? new ReadOnlyCollection(m_Colors) : null; }
set
{
if (value == null || value.Count == 0)
m_Colors = null;
else if (value.Count != vertexCount)
throw new ArgumentOutOfRangeException("value", "Array length must match vertex count.");
else
m_Colors = value.ToArray();
}
}
///
/// Returns the Color values from the mesh.
///
///
/// An array of colors for this mesh. If the mesh does not contain colors, it returns a new array
/// filled with the default value ().
///
public Color[] GetColors()
{
if (HasArrays(MeshArrays.Color))
return colors.ToArray();
return ArrayUtility.Fill(Color.white, vertexCount);
}
///
/// Gets or sets the array of tangents that the user explicitly set for this mesh.
///
/// A collection of tangents for this mesh or null if the user hasn't set any tangents yet.
///
/// To get the generated tangents that are applied to the mesh through Refresh(), use GetTangents().
///
///
public IList tangents
{
get
{
return m_Tangents == null || m_Tangents.Length != vertexCount
? null
: new ReadOnlyCollection(m_Tangents);
}
set
{
if (value == null)
m_Tangents = null;
else if (value.Count != vertexCount)
throw new ArgumentOutOfRangeException("value", "Tangent array length must match vertex count");
else
m_Tangents = value.ToArray();
}
}
internal Vector4[] tangentsInternal
{
get { return m_Tangents; }
set { m_Tangents = value; }
}
///
/// Returns the tangents applied to the mesh. If they haven't been initialized yet, it creates and caches them.
///
///
/// The tangents applied to
/// or null if the tangents array length doesn't match the vertex count.
///
public Vector4[] GetTangents()
{
if (!HasArrays(MeshArrays.Tangent))
Normals.CalculateTangents(this);
return tangents.ToArray();
}
internal Vector2[] texturesInternal
{
get { return m_Textures0; }
set { m_Textures0 = value; }
}
internal List textures2Internal
{
get { return m_Textures2; }
set { m_Textures2 = value; }
}
internal List textures3Internal
{
get { return m_Textures3; }
set { m_Textures3 = value; }
}
///
/// Gets or sets the UV0 channel.
///
/// The list of texture UVs for this mesh or null if there are none.
///
public IList textures
{
get { return m_Textures0 != null ? new ReadOnlyCollection(m_Textures0) : null; }
set
{
if (value == null)
m_Textures0 = null;
else if (value.Count != vertexCount)
throw new ArgumentOutOfRangeException("value");
else
m_Textures0 = value.ToArray();
}
}
///
/// Copies values from the specified UV channel to the list of texture UVs.
///
/// The index of the UV channel to fetch values from. The valid range is `{0, 1, 2, 3}`.
/// The list of texture UVs to clear and populate with the copied UVs.
public void GetUVs(int channel, List uvs)
{
if (uvs == null)
throw new ArgumentNullException("uvs");
if (channel < 0 || channel > 3)
throw new ArgumentOutOfRangeException("channel");
uvs.Clear();
switch (channel)
{
case 0:
for (int i = 0; i < vertexCount; i++)
uvs.Add((Vector4)m_Textures0[i]);
break;
case 1:
if (mesh != null && mesh.uv2 != null)
{
Vector2[] uv2 = mesh.uv2;
for (int i = 0; i < uv2.Length; i++)
uvs.Add((Vector4)uv2[i]);
}
break;
case 2:
if (m_Textures2 != null)
uvs.AddRange(m_Textures2);
break;
case 3:
if (m_Textures3 != null)
uvs.AddRange(m_Textures3);
break;
}
}
internal ReadOnlyCollection GetUVs(int channel)
{
if (channel == 0)
return new ReadOnlyCollection(m_Textures0);
if (channel == 1)
return new ReadOnlyCollection(mesh.uv2);
if (channel == 2)
return m_Textures2 == null ? null : new ReadOnlyCollection(m_Textures2.Cast().ToList());
if (channel == 3)
return m_Textures3 == null ? null : new ReadOnlyCollection(m_Textures3.Cast().ToList());
return null;
}
///
/// Sets the mesh UVs per channel. Channels 0 and 1 are cast to Vector2, but channels 2 and 3 remain Vector4.
///
/// Does not apply to mesh (use Refresh to reflect changes after application).
/// The index of the UV channel to copy values to. The valid range is `{0, 1, 2, 3}`.
/// The list of new UV values.
public void SetUVs(int channel, List uvs)
{
switch (channel)
{
case 0:
m_Textures0 = uvs != null ? uvs.Select(x => (Vector2)x).ToArray() : null;
break;
case 1:
mesh.uv2 = uvs != null ? uvs.Select(x => (Vector2)x).ToArray() : null;
break;
case 2:
m_Textures2 = uvs != null ? new List(uvs) : null;
break;
case 3:
m_Textures3 = uvs != null ? new List(uvs) : null;
break;
}
}
///
/// Gets the number of faces that this mesh has.
///
/// The number of faces on this mesh.
public int faceCount
{
get { return m_Faces == null ? 0 : m_Faces.Length; }
}
///
/// Gets the number of vertices in the positions array.
///
/// The number of vertex positions for this mesh.
public int vertexCount
{
get { return m_Positions == null ? 0 : m_Positions.Length; }
}
///
/// Gets the number of edges that compose this mesh.
///
/// The number of edges in this mesh.
public int edgeCount
{
get
{
int count = 0;
for (int i = 0, c = faceCount; i < c; i++)
count += facesInternal[i].edgesInternal.Length;
return count;
}
}
///
/// Gets the number of vertex indices that compose this mesh.
///
/// The number of vertices in this mesh.
public int indexCount
{
get { return m_Faces == null ? 0 : m_Faces.Sum(x => x.indexesInternal.Length); }
}
///
/// Gets the number of triangles that compose this mesh.
///
/// The number of triangles in this mesh.
public int triangleCount
{
get { return m_Faces == null ? 0 : m_Faces.Sum(x => x.indexesInternal.Length) / 3; }
}
///
/// Invoked when this ProBuilderMesh is deleted.
///
/// In the Editor, when a ProBuilderMesh is destroyed it also destroys the
/// that it finds with the parent GameObject.
/// To override the default behavior, subscribe to onDestroyObject. When onDestroyObject has a
/// subscriber, ProBuilder invokes it instead of cleaning up unused meshes by itself.
///
///
///
public static event Action meshWillBeDestroyed;
///
/// Mesh was rebuilt from Awake call.
///
internal static event Action meshWasInitialized;
///
/// Invoked from ProBuilderMesh.OnDestroy before any cleanup is performed.
///
internal static event Action componentWillBeDestroyed;
///
/// Invoked from ProBuilderMesh.Reset after component is rebuilt.
///
internal static event Action componentHasBeenReset;
///
/// Invoked when the element selection changes on any ProBuilderMesh.
///
///
///
///
public static event Action elementSelectionChanged;
internal Mesh mesh
{
get
{
if (m_Mesh == null && filter != null)
m_Mesh = filter.sharedMesh;
return m_Mesh;
}
set
{
m_Mesh = value;
#if UNITY_EDITOR
UnityEditor.EditorUtility.SetDirty(this);
UnityEditor.PrefabUtility.RecordPrefabInstancePropertyModifications(this);
#endif
}
}
[Obsolete("InstanceID is not used to track mesh references as of 2023/04/12")]
internal int id => gameObject.GetInstanceID();
///
/// Gets a flag that indicates whether the is in sync with the ProBuilderMesh.
///
///
/// A flag that describes the state of the synchronicity between the
/// and ProBuilderMesh components.
///
public MeshSyncState meshSyncState
{
get
{
if (mesh == null)
return MeshSyncState.Null;
// if the local version index is uninitialized, that means no edits have occurred since loading this
// mesh. it could mean a new mesh was created and ToMesh has not been called yet, or that this mesh was
// copy/pasted from an existing instance.
// in the latter case, it is handled by listening to the ObjectChangeEvent stream in HierarchyListener.
// this function only cares about the case where modifications have been made during the current session.
if (m_VersionIndex != m_InstanceVersionIndex && m_InstanceVersionIndex != k_UnitializedVersionIndex)
return MeshSyncState.NeedsRebuild;
return mesh.uv2 == null ? MeshSyncState.Lightmap : MeshSyncState.InSync;
}
}
internal int meshFormatVersion => m_MeshFormatVersion;
}
}