Rasagar/Library/PackageCache/com.unity.probuilder/Runtime/MeshOperations/AppendElements.cs
2024-08-26 23:07:20 +03:00

1547 lines
63 KiB
C#

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine.ProBuilder;
using System;
namespace UnityEngine.ProBuilder.MeshOperations
{
/// <summary>
/// Contains functions for appending elements to meshes.
/// </summary>
public static class AppendElements
{
/// <summary>
/// Appends a new face to the ProBuilderMesh.
/// </summary>
/// <param name="mesh">The mesh target.</param>
/// <param name="positions">The new vertex positions to add.</param>
/// <param name="colors">An array of new colors to add (the length of this array must match positions length).</param>
/// <param name="uv0s">An array of new UV0s to add (the length of this array must match positions length).</param>
/// <param name="uv2s">An array of new UV2s to add (the length of this array must match positions length).</param>
/// <param name="uv3s">An array of new UV3s to add (the length of this array must match positions length).</param>
/// <param name="face">The face with the new triangle indices. The indices should be 0 indexed.</param>
/// <param name="common">An array of the vertex indices that are shared.</param>
/// <returns>The new face as referenced on the mesh.</returns>
internal static Face AppendFace(
this ProBuilderMesh mesh,
Vector3[] positions,
Color[] colors,
Vector2[] uv0s,
Vector4[] uv2s,
Vector4[] uv3s,
Face face,
int[] common)
{
if (mesh == null)
throw new ArgumentNullException("mesh");
if (positions == null)
throw new ArgumentNullException("positions");
if (face == null)
throw new ArgumentNullException("face");
int faceVertexCount = positions.Length;
if (common == null)
{
common = new int[faceVertexCount];
for (int i = 0; i < faceVertexCount; i++)
common[i] = -1;
}
int vertexCount = mesh.vertexCount;
var mc = mesh.HasArrays(MeshArrays.Color);
var fc = colors != null;
var mt0 = mesh.HasArrays(MeshArrays.Texture0);
var ft0 = uv0s != null;
var mt2 = mesh.HasArrays(MeshArrays.Texture2);
var ft2 = uv2s != null;
var mt3 = mesh.HasArrays(MeshArrays.Texture3);
var ft3 = uv3s != null;
Vector3[] newPositions = new Vector3[vertexCount + faceVertexCount];
Color[] newColors = (mc || fc) ? new Color[vertexCount + faceVertexCount] : null;
Vector2[] newTexture0s = (mt0 || ft0) ? new Vector2[vertexCount + faceVertexCount] : null;
List<Vector4> newTexture2s = (mt2 || ft2) ? new List<Vector4>() : null;
List<Vector4> newTexture3s = (mt3 || ft3) ? new List<Vector4>() : null;
List<Face> faces = new List<Face>(mesh.facesInternal);
Array.Copy(mesh.positionsInternal, 0, newPositions, 0, vertexCount);
Array.Copy(positions, 0, newPositions, vertexCount, faceVertexCount);
if (mc || fc)
{
Array.Copy(mc ? mesh.colorsInternal : ArrayUtility.Fill(Color.white, vertexCount), 0, newColors, 0,
vertexCount);
Array.Copy(fc ? colors : ArrayUtility.Fill(Color.white, faceVertexCount), 0, newColors, vertexCount,
colors.Length);
}
if (mt0 || ft0)
{
Array.Copy(mt0 ? mesh.texturesInternal : ArrayUtility.Fill(Vector2.zero, vertexCount), 0, newTexture0s, 0,
vertexCount);
Array.Copy(ft0 ? uv0s : ArrayUtility.Fill(Vector2.zero, faceVertexCount), 0, newTexture0s,
mesh.texturesInternal.Length, faceVertexCount);
}
if (mt2 || ft2)
{
newTexture2s.AddRange(mt2 ? mesh.textures2Internal : new Vector4[vertexCount].ToList());
newTexture2s.AddRange(ft2 ? uv2s : new Vector4[faceVertexCount]);
}
if (mt3 || ft3)
{
newTexture3s.AddRange(mt3 ? mesh.textures3Internal : new Vector4[vertexCount].ToList());
newTexture3s.AddRange(ft3 ? uv3s : new Vector4[faceVertexCount]);
}
face.ShiftIndexesToZero();
face.ShiftIndexes(vertexCount);
faces.Add(face);
for (int i = 0; i < common.Length; i++)
{
if (common[i] < 0)
mesh.AddSharedVertex(new SharedVertex(new int[] {i + vertexCount}));
else
mesh.AddToSharedVertex(common[i], i + vertexCount);
}
mesh.positions = newPositions;
mesh.colors = newColors;
mesh.textures = newTexture0s;
mesh.faces = faces;
mesh.textures2Internal = newTexture2s;
mesh.textures3Internal = newTexture3s;
return face;
}
/// <summary>
/// Appends a group of new faces to the ProBuilderMesh.
/// </summary>
/// <param name="mesh">The source mesh to append new faces to.</param>
/// <param name="positions">An array of position arrays, where the indices correspond to the `faces` parameter.</param>
/// <param name="colors">An array of colors arrays, where the indices correspond to the `faces` parameter.</param>
/// <param name="uvs">An array of UVs arrays, where the indices correspond to the `faces` parameter.</param>
/// <param name="faces">An array of Face arrays, which contain the triangle winding information for each new face. Face index values are 0 indexed.</param>
/// <param name="shared">
/// An optional mapping of each new vertex's common index. Common index refers to a triangle's index in the <see cref="ProBuilderMesh.sharedVertices"/> array.
/// If you provide this value, include entries for each vertex position. For example, if there are four vertices in this face, there must be shared index entries for `{ 0, 1, 2, 3 }`.
/// </param>
/// <returns>An array of the new faces that this method successfully appended to the mesh; null if it failed.</returns>
public static Face[] AppendFaces(
this ProBuilderMesh mesh,
Vector3[][] positions,
Color[][] colors,
Vector2[][] uvs,
Face[] faces,
int[][] shared)
{
if (mesh == null)
throw new ArgumentNullException("mesh");
if (positions == null)
throw new ArgumentNullException("positions");
if (colors == null)
throw new ArgumentNullException("colors");
if (uvs == null)
throw new ArgumentNullException("uvs");
if (faces == null)
throw new ArgumentNullException("faces");
var newPositions = new List<Vector3>(mesh.positionsInternal);
var newColors = new List<Color>(mesh.colorsInternal);
var newTextures = new List<Vector2>(mesh.texturesInternal);
var newFaces = new List<Face>(mesh.facesInternal);
var lookup = mesh.sharedVertexLookup;
int vc = mesh.vertexCount;
for (int i = 0; i < faces.Length; i++)
{
newPositions.AddRange(positions[i]);
newColors.AddRange(colors[i]);
newTextures.AddRange(uvs[i]);
faces[i].ShiftIndexesToZero();
faces[i].ShiftIndexes(vc);
newFaces.Add(faces[i]);
if (shared != null && positions[i].Length != shared[i].Length)
{
Debug.LogError("Append Face failed because shared array does not match new vertex array.");
return null;
}
var hasCommon = shared != null;
for (int j = 0; j < shared[i].Length; j++)
lookup.Add(j + vc, hasCommon ? shared[i][j] : -1);
vc = newPositions.Count;
}
mesh.positions = newPositions;
mesh.colors = newColors;
mesh.textures = newTextures;
mesh.faces = newFaces;
mesh.SetSharedVertices(lookup);
return faces;
}
/// <summary>
/// Creates a new face that connects existing vertices.
/// </summary>
/// <param name="mesh">The source mesh.</param>
/// <param name="indexes">The indices of the vertices to join with the new polygon.</param>
/// <param name="unordered">
/// False if the indices in an ordered path; true if not.
/// For unordered indices, this function treats the polygon as a convex shape.
/// ProBuilder allows concave shapes when triangulating ordered paths.
/// </param>
/// <returns>The new face that this action successfully created; null if action failed.</returns>
public static Face CreatePolygon(this ProBuilderMesh mesh, IList<int> indexes, bool unordered)
{
if (mesh == null)
throw new ArgumentNullException("mesh");
SharedVertex[] sharedIndexes = mesh.sharedVerticesInternal;
Dictionary<int, int> lookup = mesh.sharedVertexLookup;
HashSet<int> common = mesh.GetSharedVertexHandles(indexes);
List<Vertex> vertices = new List<Vertex>(mesh.GetVertices());
List<Vertex> appendVertices = new List<Vertex>();
foreach (int i in common)
{
int index = sharedIndexes[i][0];
appendVertices.Add(new Vertex(vertices[index]));
}
FaceRebuildData data = FaceWithVertices(appendVertices, unordered);
if (data != null)
{
data.sharedIndexes = common.ToList();
List<Face> faces = new List<Face>(mesh.facesInternal);
FaceRebuildData.Apply(new FaceRebuildData[] {data}, vertices, faces, lookup, null);
mesh.SetVertices(vertices);
mesh.faces = faces;
mesh.SetSharedVertices(lookup);
return data.face;
}
const string insufficientPoints = "Too Few Unique Points Selected";
const string badWinding = "Points not ordered correctly";
Log.Info(unordered ? insufficientPoints : badWinding);
return null;
}
/// <summary>
/// Creates a new face by connecting existing vertices.
/// </summary>
/// <param name="mesh">The source mesh.</param>
/// <param name="indexes">The indexes of the vertices to join with the new polygon.</param>
/// <param name="holes">A list of indices defining holes.</param>
/// <returns>The new face that this action successfully created; null if action failed.</returns>
public static Face CreatePolygonWithHole(this ProBuilderMesh mesh, IList<int> indexes, IList<IList<int>> holes)
{
if (mesh == null)
throw new ArgumentNullException("mesh");
SharedVertex[] sharedIndexes = mesh.sharedVerticesInternal;
Dictionary<int, int> lookup = mesh.sharedVertexLookup;
List<Vertex> vertices = new List<Vertex>(mesh.GetVertices());
HashSet<int> commonVertices = mesh.GetSharedVertexHandles(indexes);
List<Vertex> appendVertices = new List<Vertex>();
foreach (int i in commonVertices)
{
int index = sharedIndexes[i][0];
appendVertices.Add(new Vertex(vertices[index]));
}
HashSet<int> common = commonVertices;
List< HashSet<int> > commonHoles = new List<HashSet<int>>();
List< List<Vertex> > appendHoles = new List<List<Vertex>>();
for (int i = 0; i < holes.Count; i++)
{
commonHoles.Add(mesh.GetSharedVertexHandles(holes[i]));
List<Vertex> currentHole = new List<Vertex>();
appendHoles.Add(currentHole);
foreach (int j in commonHoles[i])
{
common.Add(j);
int index = sharedIndexes[j][0];
currentHole.Add(new Vertex(vertices[index]));
}
}
FaceRebuildData data = FaceWithVerticesAndHole(appendVertices, appendHoles);
if (data != null)
{
data.sharedIndexes = common.ToList();
List<Face> faces = new List<Face>(mesh.facesInternal);
FaceRebuildData.Apply(new FaceRebuildData[] { data }, vertices, faces, lookup, null);
mesh.SetVertices(vertices);
mesh.faces = faces;
mesh.SetSharedVertices(lookup);
return data.face;
}
return null;
}
/// <summary>
/// Creates a custom polygon shape from a set of points on a plane. The points must be ordered.
/// </summary>
/// <param name="poly">The <see cref="PolyShape"/> component to rebuild.</param>
/// <returns>An action result indicating the status of the operation.</returns>
public static ActionResult CreateShapeFromPolygon(this PolyShape poly)
{
return poly.mesh.CreateShapeFromPolygon(poly.m_Points, poly.extrude, poly.flipNormals);
}
/// <summary>
/// Clear and refresh mesh in case of failure to create a shape.
/// </summary>
/// <param name="mesh"></param>
internal static void ClearAndRefreshMesh(this ProBuilderMesh mesh)
{
mesh.Clear();
mesh.ToMesh();
mesh.Refresh();
}
/// <summary>
/// Rebuilds a mesh from an ordered set of points.
/// </summary>
/// <param name="mesh">The target mesh. This method clears and repopulates the mesh values with the shape extruded from points.</param>
/// <param name="points">A path of points to triangulate and extrude.</param>
/// <param name="extrude">The distance to extrude.</param>
/// <param name="flipNormals">True to invert the faces when creating the <see cref="PolyShape"/>.</param>
/// <returns>An ActionResult with the status of the operation.</returns>
public static ActionResult CreateShapeFromPolygon(this ProBuilderMesh mesh, IList<Vector3> points,
float extrude, bool flipNormals)
{
return CreateShapeFromPolygon(mesh, points, extrude, flipNormals, null);
}
/// <summary>
/// Rebuilds a mesh from an ordered set of points.
/// </summary>
/// <param name="mesh">The target mesh. Clears and repopulates the mesh values with the shape extruded from points.</param>
/// <param name="points">A path of points to triangulate and extrude.</param>
/// <param name="extrude">The distance to extrude.</param>
/// <param name="flipNormals">True to invert the faces when creating them.</param>
/// <param name="cameraLookAt">This argument is ignored.</param>
/// <param name="holePoints">Holes in the polygon.</param>
/// <returns>An ActionResult with the status of the operation.</returns>
[Obsolete("Face.CreateShapeFromPolygon is deprecated as it no longer relies on camera look at.")]
public static ActionResult CreateShapeFromPolygon(this ProBuilderMesh mesh, IList<Vector3> points,
float extrude, bool flipNormals, Vector3 cameraLookAt, IList<IList<Vector3>> holePoints = null)
{
return CreateShapeFromPolygon(mesh, points, extrude, flipNormals, null);
}
/// <summary>
/// Rebuilds a mesh from an ordered set of points.
/// </summary>
/// <param name="mesh">The target mesh. Clears and repopulates the mesh values with the shape extruded from points.</param>
/// <param name="points">A path of points to triangulate and extrude.</param>
/// <param name="extrude">The distance to extrude.</param>
/// <param name="flipNormals">True to invert the faces when creating them.</param>
/// <param name="holePoints">Holes in the polygon. Specify null if you want this method to ignore this value.</param>
/// <returns>An ActionResult with the status of the operation.</returns>
public static ActionResult CreateShapeFromPolygon(this ProBuilderMesh mesh, IList<Vector3> points,
float extrude, bool flipNormals, IList<IList<Vector3>> holePoints)
{
if (mesh == null)
throw new ArgumentNullException("mesh");
if (points == null || points.Count < 3)
{
ClearAndRefreshMesh(mesh);
return new ActionResult(ActionResult.Status.NoChange, "Too Few Points");
}
Vector3[] vertices = points.ToArray();
Vector3[][] holeVertices = null;
if (holePoints != null && holePoints.Count > 0)
{
holeVertices = new Vector3[holePoints.Count][];
for (int i = 0; i < holePoints.Count; i++)
{
if (holePoints[i] == null || holePoints[i].Count < 3)
{
ClearAndRefreshMesh(mesh);
return new ActionResult(ActionResult.Status.NoChange, "Too Few Points in hole " + i);
}
holeVertices[i] = holePoints[i].ToArray();
}
}
List<int> triangles;
Log.PushLogLevel(LogLevel.Error);
if (Triangulation.TriangulateVertices(vertices, out triangles, holeVertices))
{
Vector3[] combinedVertices = null;
if (holeVertices != null)
{
combinedVertices = new Vector3[vertices.Length + holeVertices.Sum(arr => arr.Length)];
Array.Copy(vertices, combinedVertices, vertices.Length);
int destinationIndex = vertices.Length;
foreach (var hole in holeVertices)
{
Array.ConstrainedCopy(hole, 0, combinedVertices, destinationIndex, hole.Length);
destinationIndex += hole.Length;
}
}
else
{
combinedVertices = vertices;
}
int[] indexes = triangles.ToArray();
if (Math.PolygonArea(combinedVertices, indexes) < Mathf.Epsilon)
{
ClearAndRefreshMesh(mesh);
Log.PopLogLevel();
return new ActionResult(ActionResult.Status.Failure, "Polygon Area < Epsilon");
}
mesh.Clear();
mesh.positionsInternal = combinedVertices;
var newFace = new Face(indexes);
mesh.facesInternal = new[] {newFace};
mesh.sharedVerticesInternal = SharedVertex.GetSharedVerticesWithPositions(combinedVertices);
mesh.InvalidateCaches();
// check that all points are represented in the triangulation
if (newFace.distinctIndexesInternal.Length != combinedVertices.Length)
{
ClearAndRefreshMesh(mesh);
Log.PopLogLevel();
return new ActionResult(ActionResult.Status.Failure, "Triangulation missing points");
}
Vector3 nrm = Math.Normal(mesh, mesh.facesInternal[0]);
nrm = mesh.gameObject.transform.TransformDirection(nrm);
if ((flipNormals
? Vector3.Dot(mesh.gameObject.transform.up, nrm) > 0f
: Vector3.Dot(mesh.gameObject.transform.up, nrm) < 0f))
{
mesh.facesInternal[0].Reverse();
}
if (extrude != 0.0f)
{
mesh.DuplicateAndFlip(mesh.facesInternal);
mesh.Extrude(new Face[] {(flipNormals ? mesh.facesInternal[1] : mesh.facesInternal[0])},
ExtrudeMethod.IndividualFaces, extrude);
if ((extrude < 0f && !flipNormals) || (extrude > 0f && flipNormals))
{
foreach (var face in mesh.facesInternal)
face.Reverse();
}
}
mesh.ToMesh();
mesh.Refresh();
}
else
{
// clear mesh instead of showing an invalid one
ClearAndRefreshMesh(mesh);
Log.PopLogLevel();
return new ActionResult(ActionResult.Status.Failure, "Failed Triangulating Points");
}
Log.PopLogLevel();
return new ActionResult(ActionResult.Status.Success, "Create Polygon Shape");
}
/// <summary>
/// Create a new face given a set of unordered vertices (or ordered, if unordered param is set to false).
/// </summary>
/// <param name="vertices"></param>
/// <param name="unordered"></param>
/// <returns></returns>
internal static FaceRebuildData FaceWithVertices(List<Vertex> vertices, bool unordered = true)
{
List<int> triangles;
if (Triangulation.TriangulateVertices(vertices, out triangles, unordered))
{
FaceRebuildData data = new FaceRebuildData();
data.vertices = vertices;
data.face = new Face(triangles);
return data;
}
return null;
}
/// <summary>
/// Create a new face given a set of ordered vertices and vertices making holes in the face.
/// </summary>
/// <param name="vertices"></param>
/// <param name="holes"></param>
/// <returns></returns>
internal static FaceRebuildData FaceWithVerticesAndHole(List<Vertex> borderVertices, List<List<Vertex>> holes)
{
List<int> triangles;
Vector3[] verticesV3 = borderVertices.Select(v => v.position).ToArray();
Vector3[][] holesV3 = new Vector3[holes.Count][];
for (int i = 0; i < holesV3.Length; i++)
{
holesV3[i] = holes[i].Select(v => v.position).ToArray();
}
if (Triangulation.TriangulateVertices(verticesV3, out triangles, holesV3))
{
List<Vertex> vertices = new List<Vertex>();
vertices.AddRange(borderVertices);
foreach (var hole in holes)
{
vertices.AddRange(hole);
}
FaceRebuildData data = new FaceRebuildData();
data.vertices = vertices;
data.face = new Face(triangles);
return data;
}
return null;
}
/// <summary>
/// Given a path of vertices, inserts a new vertex in the center inserts triangles along the path.
/// </summary>
/// <param name="path"></param>
/// <returns></returns>
internal static List<FaceRebuildData> TentCapWithVertices(List<Vertex> path)
{
int count = path.Count;
Vertex center = Vertex.Average(path);
List<FaceRebuildData> faces = new List<FaceRebuildData>();
for (int i = 0; i < count; i++)
{
List<Vertex> vertices = new List<Vertex>()
{
path[i],
center,
path[(i + 1) % count]
};
FaceRebuildData data = new FaceRebuildData();
data.vertices = vertices;
data.face = new Face(new int[] {0, 1, 2});
faces.Add(data);
}
return faces;
}
/// <summary>
/// Duplicates and reverses the winding direction for each face.
/// </summary>
/// <param name="mesh">The target mesh.</param>
/// <param name="faces">The faces to duplicate, reverse triangle winding order, and append to mesh.</param>
public static void DuplicateAndFlip(this ProBuilderMesh mesh, Face[] faces)
{
if (mesh == null)
throw new ArgumentNullException("mesh");
if (faces == null)
throw new ArgumentNullException("faces");
List<FaceRebuildData> rebuild = new List<FaceRebuildData>();
List<Vertex> vertices = new List<Vertex>(mesh.GetVertices());
Dictionary<int, int> lookup = mesh.sharedVertexLookup;
foreach (Face face in faces)
{
FaceRebuildData data = new FaceRebuildData();
data.vertices = new List<Vertex>();
data.face = new Face(face);
data.sharedIndexes = new List<int>();
Dictionary<int, int> map = new Dictionary<int, int>();
int len = data.face.indexesInternal.Length;
for (int i = 0; i < len; i++)
{
if (map.ContainsKey(face.indexesInternal[i]))
continue;
map.Add(face.indexesInternal[i], map.Count);
data.vertices.Add(vertices[face.indexesInternal[i]]);
data.sharedIndexes.Add(lookup[face.indexesInternal[i]]);
}
int[] tris = new int[len];
for (var i = 0; i < len; i++)
tris[len - (i + 1)] = map[data.face[i]];
data.face.SetIndexes(tris);
rebuild.Add(data);
}
FaceRebuildData.Apply(rebuild, mesh, vertices);
}
/// <summary>
/// Inserts a face between two edges.
///
/// This is the equivalent of the [Bridge Edges](../manual/Edge_Bridge.html) action.
/// </summary>
/// <param name="mesh">The source mesh.</param>
/// <param name="a">First edge.</param>
/// <param name="b">Second edge</param>
/// <param name="allowNonManifoldGeometry">If true, this function will allow edges to be bridged that create overlapping (non-manifold) faces.</param>
/// <returns>The new face, or null of the action failed.</returns>
public static Face Bridge(this ProBuilderMesh mesh, Edge a, Edge b, bool allowNonManifoldGeometry = false)
{
if (mesh == null)
throw new ArgumentNullException("mesh");
SharedVertex[] sharedVertices = mesh.sharedVerticesInternal;
Dictionary<int, int> lookup = mesh.sharedVertexLookup;
// Check to see if a face already exists
if (!allowNonManifoldGeometry)
{
if (ElementSelection.GetNeighborFaces(mesh, a).Count > 1 ||
ElementSelection.GetNeighborFaces(mesh, b).Count > 1)
{
return null;
}
}
foreach (Face face in mesh.facesInternal)
{
if (mesh.IndexOf(face.edgesInternal, a) >= 0 && mesh.IndexOf(face.edgesInternal, b) >= 0)
{
Log.Warning("Face already exists between these two edges!");
return null;
}
}
Vector3[] positions = mesh.positionsInternal;
bool hasColors = mesh.HasArrays(MeshArrays.Color);
Color[] colors = hasColors ? mesh.colorsInternal : null;
Vector3[] v;
Color[] c;
int[] s;
AutoUnwrapSettings uvs = AutoUnwrapSettings.tile;
int submeshIndex = 0;
// Get material and UV stuff from the first edge face
SimpleTuple<Face, Edge> faceAndEdge;
if (EdgeUtility.ValidateEdge(mesh, a, out faceAndEdge) ||
EdgeUtility.ValidateEdge(mesh, b, out faceAndEdge))
{
uvs = new AutoUnwrapSettings(faceAndEdge.item1.uv);
submeshIndex = faceAndEdge.item1.submeshIndex;
}
// Bridge will form a triangle
if (a.Contains(b.a, lookup) || a.Contains(b.b, lookup))
{
v = new Vector3[3];
c = new Color[3];
s = new int[3];
bool axbx = Array.IndexOf(sharedVertices[mesh.GetSharedVertexHandle(a.a)].arrayInternal, b.a) > -1;
bool axby = Array.IndexOf(sharedVertices[mesh.GetSharedVertexHandle(a.a)].arrayInternal, b.b) > -1;
bool aybx = Array.IndexOf(sharedVertices[mesh.GetSharedVertexHandle(a.b)].arrayInternal, b.a) > -1;
bool ayby = Array.IndexOf(sharedVertices[mesh.GetSharedVertexHandle(a.b)].arrayInternal, b.b) > -1;
if (axbx)
{
v[0] = positions[a.a];
if (hasColors) c[0] = colors[a.a];
s[0] = mesh.GetSharedVertexHandle(a.a);
v[1] = positions[a.b];
if (hasColors) c[1] = colors[a.b];
s[1] = mesh.GetSharedVertexHandle(a.b);
v[2] = positions[b.b];
if (hasColors) c[2] = colors[b.b];
s[2] = mesh.GetSharedVertexHandle(b.b);
}
else if (axby)
{
v[0] = positions[a.a];
if (hasColors) c[0] = colors[a.a];
s[0] = mesh.GetSharedVertexHandle(a.a);
v[1] = positions[a.b];
if (hasColors) c[1] = colors[a.b];
s[1] = mesh.GetSharedVertexHandle(a.b);
v[2] = positions[b.a];
if (hasColors) c[2] = colors[b.a];
s[2] = mesh.GetSharedVertexHandle(b.a);
}
else if (aybx)
{
v[0] = positions[a.b];
if (hasColors) c[0] = colors[a.b];
s[0] = mesh.GetSharedVertexHandle(a.b);
v[1] = positions[a.a];
if (hasColors) c[1] = colors[a.a];
s[1] = mesh.GetSharedVertexHandle(a.a);
v[2] = positions[b.b];
if (hasColors) c[2] = colors[b.b];
s[2] = mesh.GetSharedVertexHandle(b.b);
}
else if (ayby)
{
v[0] = positions[a.b];
if (hasColors) c[0] = colors[a.b];
s[0] = mesh.GetSharedVertexHandle(a.b);
v[1] = positions[a.a];
if (hasColors) c[1] = colors[a.a];
s[1] = mesh.GetSharedVertexHandle(a.a);
v[2] = positions[b.a];
if (hasColors) c[2] = colors[b.a];
s[2] = mesh.GetSharedVertexHandle(b.a);
}
return mesh.AppendFace(
v,
hasColors ? c : null,
new Vector2[v.Length],
new Vector4[v.Length],
new Vector4[v.Length],
new Face(axbx || axby ? new int[3] {2, 1, 0} : new int[3] {0, 1, 2}, submeshIndex, uvs, 0, -1, -1,
false),
s);
}
// Else, bridge will form a quad
v = new Vector3[4];
c = new Color[4];
s = new int[4]; // shared indexes index to add to
v[0] = positions[a.a];
if (hasColors)
c[0] = mesh.colorsInternal[a.a];
s[0] = mesh.GetSharedVertexHandle(a.a);
v[1] = positions[a.b];
if (hasColors)
c[1] = mesh.colorsInternal[a.b];
s[1] = mesh.GetSharedVertexHandle(a.b);
Vector3 nrm = Vector3.Cross(positions[b.a] - positions[a.a], positions[a.b] - positions[a.a]).normalized;
Vector2[] planed =
Projection.PlanarProject(
new Vector3[4] {positions[a.a], positions[a.b], positions[b.a], positions[b.b]}, null, nrm);
Vector2 ipoint = Vector2.zero;
bool intersects = Math.GetLineSegmentIntersect(planed[0], planed[2], planed[1], planed[3], ref ipoint);
if (!intersects)
{
v[2] = positions[b.a];
if (hasColors)
c[2] = mesh.colorsInternal[b.a];
s[2] = mesh.GetSharedVertexHandle(b.a);
v[3] = positions[b.b];
if (hasColors)
c[3] = mesh.colorsInternal[b.b];
s[3] = mesh.GetSharedVertexHandle(b.b);
}
else
{
v[2] = positions[b.b];
if (hasColors)
c[2] = mesh.colorsInternal[b.b];
s[2] = mesh.GetSharedVertexHandle(b.b);
v[3] = positions[b.a];
if (hasColors)
c[3] = mesh.colorsInternal[b.a];
s[3] = mesh.GetSharedVertexHandle(b.a);
}
return mesh.AppendFace(
v,
hasColors ? c : null,
new Vector2[v.Length],
new Vector4[v.Length],
new Vector4[v.Length],
new Face(new int[6] {2, 1, 0, 2, 3, 1}, submeshIndex, uvs, 0, -1, -1, false),
s);
}
// backwards compatibility prevents us from just using insertOnEdge as an optional parameter
/// <summary>
/// Adds a set of points to a face and re-triangulates. Points are added to the nearest edge.
/// </summary>
/// <param name="mesh">The source mesh.</param>
/// <param name="face">The face to append points to.</param>
/// <param name="points">Points to add to the face.</param>
/// <returns>The face created by appending the points.</returns>
public static Face AppendVerticesToFace(this ProBuilderMesh mesh, Face face, Vector3[] points)
{
return AppendVerticesToFace(mesh, face, points, true);
}
/// <summary>
/// Adds a set of points to a face and re-triangulates.
/// </summary>
/// <param name="mesh">The source mesh.</param>
/// <param name="face">The face to append points to.</param>
/// <param name="points">Points to add to the face.</param>
/// <param name="insertOnEdge">True to force new points to snap to edges.</param>
/// <returns>The face created by appending the points.</returns>
public static Face AppendVerticesToFace(this ProBuilderMesh mesh, Face face, Vector3[] points, bool insertOnEdge)
{
if (mesh == null)
throw new ArgumentNullException("mesh");
if (face == null)
throw new ArgumentNullException("face");
if (points == null)
throw new ArgumentNullException("points");
List<Vertex> vertices = mesh.GetVertices().ToList();
List<Face> faces = new List<Face>(mesh.facesInternal);
Dictionary<int, int> lookup = mesh.sharedVertexLookup;
Dictionary<int, int> lookupUV = null;
if (mesh.sharedTextures != null)
{
lookupUV = new Dictionary<int, int>();
SharedVertex.GetSharedVertexLookup(mesh.sharedTextures, lookupUV);
}
List<Edge> wound = WingedEdge.SortEdgesByAdjacency(face);
List<Vertex> n_vertices = new List<Vertex>();
List<int> n_shared = new List<int>();
List<int> n_sharedUV = lookupUV != null ? new List<int>() : null;
for (int i = 0; i < wound.Count; i++)
{
n_vertices.Add(vertices[wound[i].a]);
n_shared.Add(lookup[wound[i].a]);
if (lookupUV != null)
{
int uv;
if (lookupUV.TryGetValue(wound[i].a, out uv))
n_sharedUV.Add(uv);
else
n_sharedUV.Add(-1);
}
}
if (insertOnEdge)
{
// now insert the new points on the nearest edge
for (int i = 0; i < points.Length; i++)
{
int index = -1;
float best = Mathf.Infinity;
Vector3 p = points[i];
int vc = n_vertices.Count;
for (int n = 0; n < vc; n++)
{
Vector3 v = n_vertices[n].position;
Vector3 w = n_vertices[(n + 1) % vc].position;
float dist = Math.DistancePointLineSegment(p, v, w);
if (dist < best)
{
best = dist;
index = n;
}
}
Vertex left = n_vertices[index], right = n_vertices[(index + 1) % vc];
float x = (p - left.position).sqrMagnitude;
float y = (p - right.position).sqrMagnitude;
Vertex insert = Vertex.Mix(left, right, x / (x + y));
n_vertices.Insert((index + 1) % vc, insert);
n_shared.Insert((index + 1) % vc, -1);
if (n_sharedUV != null) n_sharedUV.Insert((index + 1) % vc, -1);
}
}
else
{
for (int i = 0; i < points.Length; i++)
{
int index = -1;
Vector3 p = points[i];
int vc = n_vertices.Count;
Vertex insert = new Vertex();//Vertex.Mix(left, right, x / (x + y));
insert.position = p;
n_vertices.Insert((index + 1) % vc, insert);
n_shared.Insert((index + 1) % vc, -1);
if (n_sharedUV != null) n_sharedUV.Insert((index + 1) % vc, -1);
}
}
List<int> triangles;
try
{
Triangulation.TriangulateVertices(n_vertices, out triangles, true);
}
catch
{
Debug.Log("Failed triangulating face after appending vertices.");
return null;
}
FaceRebuildData data = new FaceRebuildData();
data.face = new Face(triangles.ToArray(), face.submeshIndex, new AutoUnwrapSettings(face.uv),
face.smoothingGroup, face.textureGroup, -1, face.manualUV);
data.vertices = n_vertices;
data.sharedIndexes = n_shared;
data.sharedIndexesUV = n_sharedUV;
FaceRebuildData.Apply(new List<FaceRebuildData>() {data},
vertices,
faces,
lookup,
lookupUV);
var newFace = data.face;
mesh.SetVertices(vertices);
mesh.faces = faces;
mesh.SetSharedVertices(lookup);
mesh.SetSharedTextures(lookupUV);
// check old normal and make sure this new face is pointing the same direction
Vector3 oldNrm = Math.Normal(mesh, face);
Vector3 newNrm = Math.Normal(mesh, newFace);
if (Vector3.Dot(oldNrm, newNrm) < 0)
newFace.Reverse();
mesh.DeleteFace(face);
return newFace;
}
/// <summary>
/// Inserts a number of new points on an edge. Points are evenly spaced out along the edge.
/// </summary>
/// <param name="mesh">The source mesh.</param>
/// <param name="edge">The edge to split with points.</param>
/// <param name="count">The number of new points to insert. Must be greater than 0.</param>
/// <returns>The new edges created by inserting points.</returns>
public static List<Edge> AppendVerticesToEdge(this ProBuilderMesh mesh, Edge edge, int count)
{
return AppendVerticesToEdge(mesh, new Edge[] {edge}, count);
}
/// <summary>
/// Inserts a number of new points on each edge in the specified set of edges. Points are evenly spaced out along the edge.
/// </summary>
/// <param name="mesh">The source mesh.</param>
/// <param name="edges">The edges to split with points.</param>
/// <param name="count">The number of new points to insert. Must be greater than 0.</param>
/// <returns>The new edges created by inserting points.</returns>
public static List<Edge> AppendVerticesToEdge(this ProBuilderMesh mesh, IList<Edge> edges, int count)
{
if (mesh == null)
throw new ArgumentNullException("mesh");
if (edges == null)
throw new ArgumentNullException("edges");
if (count < 1 || count > 512)
{
Log.Error("New edge vertex count is less than 1 or greater than 512.");
return null;
}
List<Vertex> vertices = new List<Vertex>(mesh.GetVertices());
Dictionary<int, int> lookup = mesh.sharedVertexLookup;
Dictionary<int, int> lookupUV = mesh.sharedTextureLookup;
List<int> indexesToDelete = new List<int>();
IEnumerable<Edge> commonEdges = EdgeUtility.GetSharedVertexHandleEdges(mesh, edges);
List<Edge> distinctEdges = commonEdges.Distinct().ToList();
Dictionary<Face, FaceRebuildData> modifiedFaces = new Dictionary<Face, FaceRebuildData>();
int originalSharedIndexesCount = lookup.Count;
int sharedIndexesCount = originalSharedIndexesCount;
foreach (Edge edge in distinctEdges)
{
Edge localEdge = EdgeUtility.GetEdgeWithSharedVertexHandles(mesh, edge);
// Generate the new vertices that will be inserted on this edge
List<Vertex> verticesToAppend = new List<Vertex>(count);
for (int i = 0; i < count; i++)
verticesToAppend.Add(Vertex.Mix(vertices[localEdge.a], vertices[localEdge.b],
(i + 1) / ((float) count + 1)));
List<SimpleTuple<Face, Edge>> adjacentFaces = ElementSelection.GetNeighborFaces(mesh, localEdge);
Edge edgeLookUp = new Edge(lookup[localEdge.a], lookup[localEdge.b]);
Edge e = new Edge();
// foreach face attached to common edge, append vertices
foreach (SimpleTuple<Face, Edge> tup in adjacentFaces)
{
Face face = tup.item1;
FaceRebuildData data;
if (!modifiedFaces.TryGetValue(face, out data))
{
data = new FaceRebuildData();
data.face = new Face(new int[0], face.submeshIndex, new AutoUnwrapSettings(face.uv),
face.smoothingGroup, face.textureGroup, -1, face.manualUV);
data.vertices =
new List<Vertex>(ArrayUtility.ValuesWithIndexes(vertices, face.distinctIndexesInternal));
data.sharedIndexes = new List<int>();
data.sharedIndexesUV = new List<int>();
foreach (int i in face.distinctIndexesInternal)
{
int shared;
if (lookup.TryGetValue(i, out shared))
data.sharedIndexes.Add(shared);
if (lookupUV.TryGetValue(i, out shared))
data.sharedIndexesUV.Add(shared);
}
indexesToDelete.AddRange(face.distinctIndexesInternal);
modifiedFaces.Add(face, data);
//Ordering vertices in the new face
List<Vertex> orderedVertices = new List<Vertex>();
List<int> orderedSharedIndexes = new List<int>();
List<int> orderedSharedUVIndexes = new List<int>();
List<Edge> peripheralEdges = WingedEdge.SortEdgesByAdjacency(face);
for (int i = 0; i < peripheralEdges.Count; i++)
{
e.a = peripheralEdges[i].a;
e.b = peripheralEdges[i].b;
orderedVertices.Add(vertices[e.a]);
int shared;
if (lookup.TryGetValue(e.a, out shared))
orderedSharedIndexes.Add(shared);
if (lookupUV.TryGetValue(i, out shared))
data.sharedIndexesUV.Add(shared);
if (edgeLookUp.a == lookup[e.a] && edgeLookUp.b == lookup[e.b])
{
for (int j = 0; j < count; j++)
{
orderedVertices.Add(verticesToAppend[j]);
orderedSharedIndexes.Add(sharedIndexesCount + j);
orderedSharedUVIndexes.Add(-1);
}
}
else if (edgeLookUp.a == lookup[e.b] && edgeLookUp.b == lookup[e.a])
{
for (int j = count - 1; j >= 0; j--)
{
orderedVertices.Add(verticesToAppend[j]);
orderedSharedIndexes.Add(sharedIndexesCount + j);
orderedSharedUVIndexes.Add(-1);
}
}
}
data.vertices = orderedVertices;
data.sharedIndexes = orderedSharedIndexes;
data.sharedIndexesUV = orderedSharedUVIndexes;
}
else
{
//Get ordered vertices in the existing face and add new ones
List<Vertex> orderedVertices = data.vertices;
List<int> orderedSharedIndexes = data.sharedIndexes;
List<int> orderedSharedUVIndexes = data.sharedIndexesUV;
for (int i = 0; i < orderedVertices.Count; i++)
{
Vertex edgeStart = orderedVertices[i];
int edgeStartIndex = vertices.IndexOf(edgeStart);
Vertex edgeEnd = orderedVertices[(i + 1) % orderedVertices.Count];
int edgeEndIndex = vertices.IndexOf(edgeEnd);
if (edgeStartIndex == -1 || edgeEndIndex == -1)
continue;
if (lookup[edgeStartIndex] == lookup[localEdge.a] &&
lookup[edgeEndIndex] == lookup[localEdge.b])
{
orderedVertices.InsertRange(i + 1, verticesToAppend);
for (int j = 0; j < count; j++)
{
orderedSharedIndexes.Insert(i + j + 1, sharedIndexesCount + j);
orderedSharedUVIndexes.Add(-1);
}
}
else if (lookup[edgeStartIndex] == lookup[localEdge.b] &&
lookup[edgeEndIndex] == lookup[localEdge.a])
{
verticesToAppend.Reverse();
orderedVertices.InsertRange(i + 1, verticesToAppend);
for (int j = count - 1; j >= 0; j--)
{
orderedSharedIndexes.Insert(i + 1, sharedIndexesCount + j);
orderedSharedUVIndexes.Add(-1);
}
}
}
data.vertices = orderedVertices;
data.sharedIndexes = orderedSharedIndexes;
data.sharedIndexesUV = orderedSharedUVIndexes;
}
}
sharedIndexesCount += count;
}
// now apply the changes
List<Face> dic_face = modifiedFaces.Keys.ToList();
List<FaceRebuildData> dic_data = modifiedFaces.Values.ToList();
List<EdgeLookup> appendedEdges = new List<EdgeLookup>();
for (int i = 0; i < dic_face.Count; i++)
{
Face face = dic_face[i];
FaceRebuildData data = dic_data[i];
int vertexCount = vertices.Count;
// triangulate and set new face indexes to end of current vertex list
List<int> triangles;
if (Triangulation.TriangulateVertices(data.vertices, out triangles, false))
data.face = new Face(triangles);
else
continue;
//Keep submesh index when rebuilding to maintain material references
data.face.submeshIndex = face.submeshIndex;
data.face.ShiftIndexes(vertexCount);
face.CopyFrom(data.face);
for (int n = 0; n < data.vertices.Count; n++)
lookup.Add(vertexCount + n, data.sharedIndexes[n]);
if (data.sharedIndexesUV.Count == data.vertices.Count)
{
for (int n = 0; n < data.vertices.Count; n++)
lookupUV.Add(vertexCount + n, data.sharedIndexesUV[n]);
}
vertices.AddRange(data.vertices);
foreach (Edge e in face.edgesInternal)
{
EdgeLookup el = new EdgeLookup(new Edge(lookup[e.a], lookup[e.b]), e);
if (el.common.a >= originalSharedIndexesCount || el.common.b >= originalSharedIndexesCount)
appendedEdges.Add(el);
}
}
indexesToDelete = indexesToDelete.Distinct().ToList();
int delCount = indexesToDelete.Count;
var newEdges = appendedEdges.Distinct().Select(x => x.local - delCount).ToList();
mesh.SetVertices(vertices);
mesh.SetSharedVertices(lookup);
mesh.SetSharedTextures(lookupUV);
mesh.DeleteVertices(indexesToDelete);
return newEdges;
}
/// <summary>
/// Adds a set of points to a face and retriangulates. Points are added to the nearest edge.
///
/// This is the equivalent of the [Connect Vertices](../manual/Vert_Connect.html) action.
/// </summary>
/// <param name="mesh">The source mesh.</param>
/// <param name="face">The face to append points to.</param>
/// <param name="point">Point to add to the face.</param>
/// <returns>The face created by appending the points.</returns>
public static Face[] InsertVertexInFace(this ProBuilderMesh mesh, Face face, Vector3 point)
{
if (mesh == null)
throw new ArgumentNullException("mesh");
if (face == null)
throw new ArgumentNullException("face");
if (point == null)
throw new ArgumentNullException("point");
List<Vertex> vertices = mesh.GetVertices().ToList();
List<Face> faces = new List<Face>(mesh.facesInternal);
Dictionary<int, int> lookup = mesh.sharedVertexLookup;
Dictionary<int, int> lookupUV = null;
if (mesh.sharedTextures != null)
{
lookupUV = new Dictionary<int, int>();
SharedVertex.GetSharedVertexLookup(mesh.sharedTextures, lookupUV);
}
List<Edge> wound = WingedEdge.SortEdgesByAdjacency(face);
List<FaceRebuildData> newFacesData = new List<FaceRebuildData>();
Vertex newVertex = new Vertex();
newVertex.position = point;
for (int i = 0; i < wound.Count; i++)
{
List<Vertex> n_vertices = new List<Vertex>();
List<int> n_shared = new List<int>();
List<int> n_sharedUV = lookupUV != null ? new List<int>() : null;
n_vertices.Add(vertices[wound[i].a]);
n_vertices.Add(vertices[wound[i].b]);
n_vertices.Add(newVertex);
n_shared.Add(lookup[wound[i].a]);
n_shared.Add(lookup[wound[i].b]);
n_shared.Add(vertices.Count);
if (lookupUV != null)
{
int uv;
lookupUV.Clear();
if (lookupUV.TryGetValue(wound[i].a, out uv))
n_sharedUV.Add(uv);
else
n_sharedUV.Add(-1);
if (lookupUV.TryGetValue(wound[i].b, out uv))
n_sharedUV.Add(uv);
else
n_sharedUV.Add(-1);
n_sharedUV.Add(vertices.Count);
}
List<int> triangles;
try
{
Triangulation.TriangulateVertices(n_vertices, out triangles, true);
}
catch
{
Debug.Log("Failed triangulating face after appending vertices.");
return null;
}
FaceRebuildData data = new FaceRebuildData();
data.face = new Face(triangles.ToArray(), face.submeshIndex, new AutoUnwrapSettings(face.uv), face.smoothingGroup, face.textureGroup, -1, face.manualUV);
data.vertices = n_vertices;
data.sharedIndexes = n_shared;
data.sharedIndexesUV = n_sharedUV;
newFacesData.Add(data);
}
FaceRebuildData.Apply(newFacesData,
vertices,
faces,
lookup,
lookupUV);
mesh.SetVertices(vertices);
mesh.faces = faces;
mesh.SetSharedVertices(lookup);
mesh.SetSharedTextures(lookupUV);
Face[] newFaces = newFacesData.Select(f => f.face).ToArray();
foreach (FaceRebuildData data in newFacesData)
{
var newFace = data.face;
// check old normal and make sure this new face is pointing the same direction
Vector3 oldNrm = UnityEngine.ProBuilder.Math.Normal(mesh, face);
Vector3 newNrm = UnityEngine.ProBuilder.Math.Normal(mesh, newFace);
if (Vector3.Dot(oldNrm, newNrm) < 0)
newFace.Reverse();
}
mesh.DeleteFace(face);
return newFaces;
}
/// <summary>
/// Inserts a new point on an edge. Points are evenly spaced out along the edge.
///
/// This is the equivalent of the [Subdivide Edges](../manual/Edge_Subdivide.html) action.
/// </summary>
/// <param name="mesh">The source mesh.</param>
/// <param name="originalEdge">The edge to add the point to.</param>
/// <param name="point">The point to insert on the edge.</param>
/// <returns>The new Vertex created.</returns>//
public static Vertex InsertVertexOnEdge(this ProBuilderMesh mesh, Edge originalEdge, Vector3 point)
{
if (mesh == null)
throw new ArgumentNullException("mesh");
if (originalEdge == null)
throw new ArgumentNullException("edge");
List<Vertex> vertices = new List<Vertex>(mesh.GetVertices());
Dictionary<int, int> lookup = mesh.sharedVertexLookup;
Dictionary<int, int> lookupUV = mesh.sharedTextureLookup;
List<int> indexesToDelete = new List<int>();
Dictionary<Face, FaceRebuildData> modifiedFaces = new Dictionary<Face, FaceRebuildData>();
int originalSharedIndexesCount = lookup.Count;
//Ensure the new point is on the edge
//Using Scalar projection
Vector3 a = point -
vertices[originalEdge.a].position;
Vector3 b = vertices[originalEdge.b].position -
vertices[originalEdge.a].position;
float weight = Vector3.Magnitude(a) * Mathf.Cos(Vector3.Angle(b, a) * Mathf.Deg2Rad) / Vector3.Magnitude(b);
Vertex newVertex = Vertex.Mix(vertices[originalEdge.a], vertices[originalEdge.b], weight);
List<SimpleTuple<Face, Edge>> adjacentFaces = ElementSelection.GetNeighborFaces(mesh, originalEdge);
Edge uni = new Edge(lookup[originalEdge.a], lookup[originalEdge.b]);
Edge e = new Edge();
// foreach face attached to common edge, append vertices
foreach (SimpleTuple<Face, Edge> tup in adjacentFaces)
{
Face face = tup.item1;
FaceRebuildData data;
if (!modifiedFaces.TryGetValue(face, out data))
{
data = new FaceRebuildData();
data.face = new Face(new int[0], face.submeshIndex, new AutoUnwrapSettings(face.uv), face.smoothingGroup, face.textureGroup, -1, face.manualUV);
data.vertices = new List<Vertex>(ArrayUtility.ValuesWithIndexes(vertices, face.distinctIndexesInternal));
data.sharedIndexes = new List<int>();
data.sharedIndexesUV = new List<int>();
foreach (int i in face.distinctIndexesInternal)
{
int shared;
if (lookup.TryGetValue(i, out shared))
data.sharedIndexes.Add(shared);
if (lookupUV.TryGetValue(i, out shared))
data.sharedIndexesUV.Add(shared);
}
indexesToDelete.AddRange(face.distinctIndexesInternal);
modifiedFaces.Add(face, data);
}
data.vertices.Add(newVertex);
data.sharedIndexes.Add(originalSharedIndexesCount);
data.sharedIndexesUV.Add(-1);
List<Vertex> orderedVertices = new List<Vertex>();
List<int> orderedSharedIndexes = new List<int>();
List<Edge> peripheralEdges = WingedEdge.SortEdgesByAdjacency(face);
bool canAdd = true;
for (int i = 0; i < peripheralEdges.Count; i++)
{
e.a = peripheralEdges[i].a;
e.b = peripheralEdges[i].b;
orderedVertices.Add(vertices[e.a]);
int shared;
if (lookup.TryGetValue(e.a, out shared))
orderedSharedIndexes.Add(shared);
if (canAdd &&
(uni.a == lookup[e.a] && uni.b == lookup[e.b]) ||
(uni.a == lookup[e.b] && uni.b == lookup[e.a]))
{
canAdd = false;
orderedVertices.Add(data.vertices[data.vertices.Count-1]);
orderedSharedIndexes.Add(originalSharedIndexesCount);
}
}
data.vertices = orderedVertices;
data.sharedIndexes = orderedSharedIndexes;
}
// now apply the changes
List<Face> dic_face = modifiedFaces.Keys.ToList();
List<FaceRebuildData> dic_data = modifiedFaces.Values.ToList();
for (int i = 0; i < dic_face.Count; i++)
{
Face face = dic_face[i];
FaceRebuildData data = dic_data[i];
int vertexCount = vertices.Count;
// triangulate and set new face indexes to end of current vertex list
List<int> triangles;
if (Triangulation.TriangulateVertices(data.vertices, out triangles, false))
data.face = new Face(triangles);
else
continue;
//Keep submesh index when rebuilding to maintain material references
data.face.submeshIndex = face.submeshIndex;
data.face.ShiftIndexes(vertexCount);
face.CopyFrom(data.face);
for (int n = 0; n < data.vertices.Count; n++)
lookup.Add(vertexCount + n, data.sharedIndexes[n]);
if (data.sharedIndexesUV.Count == data.vertices.Count)
{
for (int n = 0; n < data.vertices.Count; n++)
lookupUV.Add(vertexCount + n, data.sharedIndexesUV[n]);
}
vertices.AddRange(data.vertices);
}
indexesToDelete = indexesToDelete.Distinct().ToList();
mesh.SetVertices(vertices);
mesh.SetSharedVertices(lookup);
mesh.SetSharedTextures(lookupUV);
mesh.DeleteVertices(indexesToDelete);
return newVertex;
}
/// <summary>
/// Adds a point to a mesh.
/// </summary>
/// <param name="mesh">The source mesh.</param>
/// <param name="point">Point to add to the face.</param>
/// <param name="normal">The inserted point's normal.</param>
/// <returns>The new inserted Vertex.</returns>
public static Vertex InsertVertexInMesh(this ProBuilderMesh mesh, Vector3 point, Vector3 normal)
{
if (mesh == null)
throw new ArgumentNullException("mesh");
if (point == null)
throw new ArgumentNullException("point");
List<Vertex> vertices = mesh.GetVertices().ToList();
Dictionary<int, int> lookup = mesh.sharedVertexLookup;
Dictionary<int, int> lookupUV = null;
// List<int> indexesToDelete = new List<int>();
int originalSharedIndexesCount = lookup.Count;
if (mesh.sharedTextures != null)
{
lookupUV = new Dictionary<int, int>();
SharedVertex.GetSharedVertexLookup(mesh.sharedTextures, lookupUV);
}
Vertex newVertex = new Vertex();
newVertex.position = point;
newVertex.normal = normal.normalized;
vertices.Add(newVertex);
lookup.Add(originalSharedIndexesCount,originalSharedIndexesCount);
lookupUV.Add(originalSharedIndexesCount,-1);
mesh.SetVertices(vertices);
mesh.SetSharedVertices(lookup);
mesh.SetSharedTextures(lookupUV);
return newVertex;
}
}
}