TurnBasedStrategyCourse/Library/PackageCache/com.unity.probuilder@5.1.0/Runtime/Core/ProBuilderMesh.cs

1019 lines
36 KiB
C#

using UnityEngine;
using System.Collections.Generic;
using System.Linq;
using UnityEngine.Serialization;
using System;
using System.Collections.ObjectModel;
using UnityEngine.Rendering;
namespace UnityEngine.ProBuilder
{
/// <summary>
/// Represents the ProBuilder MeshFilter component, which is responsible for storing all
/// the data necessary for editing and compiling <see cref="UnityEngine.Mesh" /> objects.
/// </summary>
// 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
/// <summary>
/// Stores the maximum number of UV channels that the ProBuilderMesh format supports.
/// </summary>
const int k_UVChannelCount = 4;
/// <summary>
/// Represents the current mesh format version. This is used to run expensive upgrade functions once in ToMesh().
/// </summary>
internal const int k_MeshFormatVersion = k_MeshFormatVersionAutoUVScaleOffset;
internal const int k_MeshFormatVersionSubmeshMaterialRefactor = 1;
internal const int k_MeshFormatVersionAutoUVScaleOffset = 2;
/// <summary>
/// The maximum number of vertices that a ProBuilderMesh can accomodate.
/// </summary>
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<int, int> m_SharedVertexLookup;
[SerializeField]
[FormerlySerializedAs("_sharedIndicesUV")]
SharedVertex[] m_SharedTextures;
[NonSerialized]
Dictionary<int, int> m_SharedTextureLookup;
[SerializeField]
[FormerlySerializedAs("_vertices")]
Vector3[] m_Positions;
[SerializeField]
[FormerlySerializedAs("_uv")]
Vector2[] m_Textures0;
[SerializeField]
[FormerlySerializedAs("_uv3")]
List<Vector4> m_Textures2;
[SerializeField]
[FormerlySerializedAs("_uv4")]
List<Vector4> m_Textures3;
[SerializeField]
[FormerlySerializedAs("_tangents")]
Vector4[] m_Tangents;
[NonSerialized]
Vector3[] m_Normals;
[SerializeField]
[FormerlySerializedAs("_colors")]
Color[] m_Colors;
/// <summary>
/// If false, ProBuilder will automatically create and scale colliders.
/// </summary>
public bool userCollisions { get; set; }
[FormerlySerializedAs("unwrapParameters")]
[SerializeField]
UnwrapParameters m_UnwrapParameters;
/// <summary>
/// UV2 generation parameters.
/// </summary>
public UnwrapParameters unwrapParameters
{
get { return m_UnwrapParameters; }
set { m_UnwrapParameters = value; }
}
[FormerlySerializedAs("dontDestroyMeshOnDelete")]
[SerializeField]
bool m_PreserveMeshAssetOnDestroy;
/// <summary>
/// If "Meshes are Assets" feature is enabled, this is used to relate pb_Objects to stored meshes.
/// </summary>
[SerializeField]
internal string assetGuid;
[SerializeField]
Mesh m_Mesh;
[NonSerialized]
MeshRenderer m_MeshRenderer;
#pragma warning disable 109
internal new MeshRenderer renderer
{
get
{
if (!gameObject.TryGetComponent<MeshRenderer>(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<MeshFilter>(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;
/// <summary>
/// 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.
/// </summary>
[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;
}
}
/// <summary>
/// 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.
/// </summary>
/// <returns>True to keep the mesh asset; false to destroy it.</returns>
public bool preserveMeshAssetOnDestroy
{
get { return m_PreserveMeshAssetOnDestroy; }
set { m_PreserveMeshAssetOnDestroy = value; }
}
/// <summary>
/// Tests whether the mesh contains the requested arrays.
/// </summary>
/// <param name="channels">A flag containing the array types that a ProBuilder mesh stores.</param>
/// <returns>True if all arrays in the flag are present, false if not.</returns>
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; }
}
/// <summary>
/// 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.
/// </summary>
/// <returns>A collection of the Face objects that make up this mesh.</returns>
public IList<Face> faces
{
get { return new ReadOnlyCollection<Face>(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<int, int>();
m_SharedVertexLookup.Clear();
m_CacheValid &= ~CacheValidState.SharedVertex;
}
internal void InvalidateSharedTextureLookup()
{
if (m_SharedTextureLookup == null)
m_SharedTextureLookup = new Dictionary<int, int>();
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();
}
}
/// <summary>
/// Gets or sets the sharedVertices for this ProBuilderMesh object.
///
/// ProBuilder makes the assumption that no <see cref="Face" /> 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 <see cref="UnityEngine.Mesh" /> from the ProBuilderMesh, it condenses
/// these vertices to a single vertex where possible.
/// </summary>
/// <returns>The shared (or common) index array for this mesh.</returns>
public IList<SharedVertex> sharedVertices
{
get { return new ReadOnlyCollection<SharedVertex>(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<int, int> sharedVertexLookup
{
get
{
if ((m_CacheValid & CacheValidState.SharedVertex) != CacheValidState.SharedVertex)
{
if (m_SharedVertexLookup == null)
m_SharedVertexLookup = new Dictionary<int, int>();
SharedVertex.GetSharedVertexLookup(m_SharedVertices, m_SharedVertexLookup);
m_CacheValid |= CacheValidState.SharedVertex;
}
return m_SharedVertexLookup;
}
}
/// <summary>
/// Sets the sharedIndexes array for this mesh with a lookup dictionary.
/// </summary>
/// <param name="indexes">The new sharedIndexes array.</param>
/// <seealso cref="sharedVertices"/>
internal void SetSharedVertices(IEnumerable<KeyValuePair<int, int>> 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<int, int> sharedTextureLookup
{
get
{
if ((m_CacheValid & CacheValidState.SharedTexture) != CacheValidState.SharedTexture)
{
m_CacheValid |= CacheValidState.SharedTexture;
if (m_SharedTextureLookup == null)
m_SharedTextureLookup = new Dictionary<int, int>();
SharedVertex.GetSharedVertexLookup(m_SharedTextures, m_SharedTextureLookup);
}
return m_SharedTextureLookup;
}
}
internal void SetSharedTextures(IEnumerable<KeyValuePair<int, int>> 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; }
}
/// <summary>
/// Gets or sets the vertex positions that compose this mesh.
/// </summary>
public IList<Vector3> positions
{
get { return new ReadOnlyCollection<Vector3>(m_Positions); }
set
{
if (value == null)
throw new ArgumentNullException("value");
m_Positions = value.ToArray();
}
}
/// <summary>
/// Creates a new array of vertices with values from a ProBuilderMesh component.
/// </summary>
/// <param name="indexes">An optional list of indices used to designate the subset of vertices values to retrieve from the mesh in the array.</param>
/// <returns>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. </returns>
public Vertex[] GetVertices(IList<int> 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<Vector4> uv3s = new List<Vector4>();
List<Vector4> uv4s = new List<Vector4>();
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;
}
/// <summary>
/// Returns a list of vertices from a ProBuilderMesh component.
/// </summary>
/// <param name="vertices">The list to populate.</param>
internal void GetVerticesInList(IList<Vertex> 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<Vector4> uv3s = new List<Vector4>();
List<Vector4> uv4s = new List<Vector4>();
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];
}
}
/// <summary>
/// Sets the vertex element arrays on this mesh.
/// </summary>
/// <param name="vertices">The new vertex array.</param>
/// <param name="applyMesh">
/// Optionally indicate whether to apply elements to the <see cref="UnityEngine.MeshFilter.sharedMesh" />.
/// 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
/// <see cref="UnityEditor.ProBuilder.EditorMeshUtility.Optimize" >optimized</see>).
/// </param>
public void SetVertices(IList<Vertex> 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<Vector4> uv3;
List<Vector4> 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();
}
}
/// <summary>
/// Gets the normals for this mesh.
/// </summary>
/// <see cref="Refresh"/>
/// <see cref="Normals.CalculateNormals"/>
/// <returns>A collection of normals for this mesh.</returns>
public IList<Vector3> normals
{
get { return m_Normals != null ? new ReadOnlyCollection<Vector3>(m_Normals) : null; }
}
internal Vector3[] normalsInternal
{
get { return m_Normals; }
set { m_Normals = value; }
}
/// <summary>
/// Gets the normals array for this mesh.
/// </summary>
/// <returns>An array of normals for this mesh.</returns>
public Vector3[] GetNormals()
{
if (!HasArrays(MeshArrays.Normal))
Normals.CalculateNormals(this);
return normals.ToArray();
}
internal Color[] colorsInternal
{
get { return m_Colors; }
set { m_Colors = value; }
}
/// <summary>
/// Gets or sets a collecton of the vertex colors for this mesh.
/// When setting, the value must match the length of the positions.
/// </summary>
/// <returns>A collection of colors for this mesh.</returns>
public IList<Color> colors
{
get { return m_Colors != null ? new ReadOnlyCollection<Color>(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();
}
}
/// <summary>
/// Returns the Color values from the mesh.
/// </summary>
/// <returns>
/// An array of colors for this mesh. If the mesh does not contain colors, it returns a new array
/// filled with the default value (<see cref="UnityEngine.Color.white" />).
/// </returns>
public Color[] GetColors()
{
if (HasArrays(MeshArrays.Color))
return colors.ToArray();
return ArrayUtility.Fill(Color.white, vertexCount);
}
/// <summary>
/// Gets or sets the array of tangents that the user explicitly set for this mesh.
/// </summary>
/// <returns>A collection of tangents for this mesh or null if the user hasn't set any tangents yet.</returns>
/// <remarks>
/// To get the generated tangents that are applied to the mesh through Refresh(), use GetTangents().
/// </remarks>
/// <seealso cref="GetTangents"/>
public IList<Vector4> tangents
{
get
{
return m_Tangents == null || m_Tangents.Length != vertexCount
? null
: new ReadOnlyCollection<Vector4>(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; }
}
/// <summary>
/// Returns the tangents applied to the mesh. If they haven't been initialized yet, it creates and caches them.
/// </summary>
/// <returns>
/// The tangents applied to <see cref="UnityEngine.MeshFilter.sharedMesh" />
/// or null if the tangents array length doesn't match the vertex count.
/// </returns>
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<Vector4> textures2Internal
{
get { return m_Textures2; }
set { m_Textures2 = value; }
}
internal List<Vector4> textures3Internal
{
get { return m_Textures3; }
set { m_Textures3 = value; }
}
/// <summary>
/// Gets or sets the UV0 channel.
/// </summary>
/// <returns>The list of texture UVs for this mesh or null if there are none.</returns>
/// <seealso cref="GetUVs"/>
public IList<Vector2> textures
{
get { return m_Textures0 != null ? new ReadOnlyCollection<Vector2>(m_Textures0) : null; }
set
{
if (value == null)
m_Textures0 = null;
else if (value.Count != vertexCount)
throw new ArgumentOutOfRangeException("value");
else
m_Textures0 = value.ToArray();
}
}
/// <summary>
/// Copies values from the specified UV channel to the list of texture UVs.
/// </summary>
/// <param name="channel">The index of the UV channel to fetch values from. The valid range is `{0, 1, 2, 3}`.</param>
/// <param name="uvs">The list of texture UVs to clear and populate with the copied UVs.</param>
public void GetUVs(int channel, List<Vector4> 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<Vector2> GetUVs(int channel)
{
if (channel == 0)
return new ReadOnlyCollection<Vector2>(m_Textures0);
if (channel == 1)
return new ReadOnlyCollection<Vector2>(mesh.uv2);
if (channel == 2)
return m_Textures2 == null ? null : new ReadOnlyCollection<Vector2>(m_Textures2.Cast<Vector2>().ToList());
if (channel == 3)
return m_Textures3 == null ? null : new ReadOnlyCollection<Vector2>(m_Textures3.Cast<Vector2>().ToList());
return null;
}
/// <summary>
/// Sets the mesh UVs per channel. Channels 0 and 1 are cast to Vector2, but channels 2 and 3 remain Vector4.
/// </summary>
/// <remarks>Does not apply to mesh (use Refresh to reflect changes after application).</remarks>
/// <param name="channel">The index of the UV channel to copy values to. The valid range is `{0, 1, 2, 3}`.</param>
/// <param name="uvs">The list of new UV values.</param>
public void SetUVs(int channel, List<Vector4> 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<Vector4>(uvs) : null;
break;
case 3:
m_Textures3 = uvs != null ? new List<Vector4>(uvs) : null;
break;
}
}
/// <summary>
/// Gets the number of faces that this mesh has.
/// </summary>
/// <returns>The number of faces on this mesh.</returns>
public int faceCount
{
get { return m_Faces == null ? 0 : m_Faces.Length; }
}
/// <summary>
/// Gets the number of vertices in the positions array.
/// </summary>
/// <returns>The number of vertex positions for this mesh.</returns>
public int vertexCount
{
get { return m_Positions == null ? 0 : m_Positions.Length; }
}
/// <summary>
/// Gets the number of edges that compose this mesh.
/// </summary>
/// <returns>The number of edges in this mesh.</returns>
public int edgeCount
{
get
{
int count = 0;
for (int i = 0, c = faceCount; i < c; i++)
count += facesInternal[i].edgesInternal.Length;
return count;
}
}
/// <summary>
/// Gets the number of vertex indices that compose this mesh.
/// </summary>
/// <returns>The number of vertices in this mesh.</returns>
public int indexCount
{
get { return m_Faces == null ? 0 : m_Faces.Sum(x => x.indexesInternal.Length); }
}
/// <summary>
/// Gets the number of triangles that compose this mesh.
/// </summary>
/// <returns>The number of triangles in this mesh.</returns>
public int triangleCount
{
get { return m_Faces == null ? 0 : m_Faces.Sum(x => x.indexesInternal.Length) / 3; }
}
/// <summary>
/// Invoked when this ProBuilderMesh is deleted.
///
/// In the Editor, when a ProBuilderMesh is destroyed it also destroys the
/// <see cref="UnityEngine.MeshFilter.sharedMesh" /> 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.
/// </summary>
/// <seealso cref="preserveMeshAssetOnDestroy"/>
/// <seealso cref="OnDestroy"/>
public static event Action<ProBuilderMesh> meshWillBeDestroyed;
/// <summary>
/// Mesh was rebuilt from Awake call.
/// </summary>
internal static event Action<ProBuilderMesh> meshWasInitialized;
/// <summary>
/// Invoked from ProBuilderMesh.OnDestroy before any cleanup is performed.
/// </summary>
internal static event Action<ProBuilderMesh> componentWillBeDestroyed;
/// <summary>
/// Invoked from ProBuilderMesh.Reset after component is rebuilt.
/// </summary>
internal static event Action<ProBuilderMesh> componentHasBeenReset;
/// <summary>
/// Invoked when the element selection changes on any ProBuilderMesh.
/// </summary>
/// <seealso cref="SetSelectedFaces"/>
/// <seealso cref="SetSelectedVertices"/>
/// <seealso cref="SetSelectedEdges"/>
public static event Action<ProBuilderMesh> 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();
/// <summary>
/// Gets a flag that indicates whether the <see cref="UnityEngine.Mesh" /> is in sync with the ProBuilderMesh.
/// </summary>
/// <returns>
/// A flag that describes the state of the synchronicity between the
/// <see cref="UnityEngine.MeshFilter.sharedMesh" /> and ProBuilderMesh components.
/// </returns>
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;
}
}