/// <summary> /// Insert a new vertex at the center of a face and connect the center of all edges to it. /// </summary> /// <param name="face"></param> /// <param name="edges"></param> /// <param name="vertices"></param> /// <returns></returns> static List <ConnectFaceRebuildData> ConnectEdgesInFace( Face face, List <WingedEdge> edges, List <Vertex> vertices) { List <Edge> perimeter = WingedEdge.SortEdgesByAdjacency(face); int splitCount = edges.Count; Vertex centroid = Vertex.Average(vertices, face.distinctIndexesInternal); List <List <Vertex> > n_vertices = ArrayUtility.Fill <List <Vertex> >(x => { return(new List <Vertex>()); }, splitCount); List <List <int> > n_indexes = ArrayUtility.Fill <List <int> >(x => { return(new List <int>()); }, splitCount); HashSet <Edge> edgesToSplit = new HashSet <Edge>(edges.Select(x => x.edge.local)); int index = 0; // creates two new polygon perimeter lines by stepping the current face perimeter and inserting new vertices where edges match for (int i = 0; i < perimeter.Count; i++) { n_vertices[index % splitCount].Add(vertices[perimeter[i].a]); if (edgesToSplit.Contains(perimeter[i])) { Vertex mix = Vertex.Mix(vertices[perimeter[i].a], vertices[perimeter[i].b], .5f); // split current poly line n_indexes[index].Add(n_vertices[index].Count); n_vertices[index].Add(mix); // add the centroid vertex n_indexes[index].Add(n_vertices[index].Count); n_vertices[index].Add(centroid); // advance the poly line index index = (index + 1) % splitCount; // then add the edge center vertex and move on n_vertices[index].Add(mix); } } List <ConnectFaceRebuildData> faces = new List <ConnectFaceRebuildData>(); for (int i = 0; i < n_vertices.Count; i++) { FaceRebuildData f = AppendElements.FaceWithVertices(n_vertices[i], false); if (f == null) { faces.Clear(); return(null); } faces.Add(new ConnectFaceRebuildData(f, n_indexes[i])); } return(faces); }
public static void TestComparison_IVEC3() { IntVec3 a = (IntVec3)RandVec3(); IntVec3 b = (IntVec3)(a.vec * 2.3f); IntVec3 c = (IntVec3) new Vector3(a.x, a.y + .001f, a.z); IntVec3 d = (IntVec3) new Vector3(a.x, a.y, a.z); IntVec3[] arr = ArrayUtility.Fill <IntVec3>(24, (i) => { return(i % 2 == 0 ? a : (IntVec3)RandVec3()); }); Assert.IsFalse(a == b); Assert.IsFalse(a == c); Assert.IsTrue(a == d); Assert.IsFalse(a.GetHashCode() == b.GetHashCode()); Assert.IsFalse(a.GetHashCode() == c.GetHashCode()); Assert.IsTrue(a.GetHashCode() == d.GetHashCode()); Assert.AreEqual(13, arr.Distinct().Count()); }
public static void TestComparison_EDGE() { Edge a = (Edge)RandEdge(); Edge b = (Edge)(a + 20); Edge c = (Edge) new Edge(a.a + 10, a.a); Edge d = (Edge) new Edge(a.a, a.b); Edge[] arr = ArrayUtility.Fill <Edge>(24, (i) => { return(i % 2 == 0 ? a : (Edge)RandEdge()); }); Assert.IsFalse(a == b, "a == b"); Assert.IsFalse(a == c, "a == c"); Assert.IsFalse(a.GetHashCode() == b.GetHashCode(), "a.GetHashCode() == b.GetHashCode()"); Assert.IsFalse(a.GetHashCode() == c.GetHashCode(), "a.GetHashCode() == c.GetHashCode()"); Assert.IsTrue(a.GetHashCode() == d.GetHashCode(), "a.GetHashCode() == d.GetHashCode()"); Assert.AreEqual(a, d, "Assert.AreEqual(a, d);"); Assert.IsTrue(a == d, "Assert.IsTrue(a != d);"); Assert.AreEqual(13, arr.Distinct().Count(), "Assert.AreEqual(13, arr.Distinct().Count());"); }
/// <summary> /// Reverses the orientation of the middle edge in a quad. /// <![CDATA[ /// ``` /// . _____ _____ /// . |\ | | /| /// . | \ | => | / | /// . |____\| |/____| /// ``` /// ]]> /// </summary> /// <param name="mesh">The mesh that face belongs to.</param> /// <param name="face">The target face.</param> /// <returns>True if successful, false if not. Operation will fail if face does not contain two triangles with exactly 2 shared vertices.</returns> public static bool FlipEdge(this ProBuilderMesh mesh, Face face) { if (mesh == null) { throw new ArgumentNullException("mesh"); } if (face == null) { throw new ArgumentNullException("face"); } int[] indexes = face.indexesInternal; if (indexes.Length != 6) { return(false); } int[] mode = ArrayUtility.Fill <int>(1, indexes.Length); for (int x = 0; x < indexes.Length - 1; x++) { for (int y = x + 1; y < indexes.Length; y++) { if (indexes[x] == indexes[y]) { mode[x]++; mode[y]++; } } } if (mode[0] + mode[1] + mode[2] != 5 || mode[3] + mode[4] + mode[5] != 5) { return(false); } int i0 = indexes[mode[0] == 1 ? 0 : mode[1] == 1 ? 1 : 2]; int i1 = indexes[mode[3] == 1 ? 3 : mode[4] == 1 ? 4 : 5]; int used = -1; if (mode[0] == 2) { used = indexes[0]; indexes[0] = i1; } else if (mode[1] == 2) { used = indexes[1]; indexes[1] = i1; } else if (mode[2] == 2) { used = indexes[2]; indexes[2] = i1; } if (mode[3] == 2 && indexes[3] != used) { indexes[3] = i0; } else if (mode[4] == 2 && indexes[4] != used) { indexes[4] = i0; } else if (mode[5] == 2 && indexes[5] != used) { indexes[5] = i0; } face.InvalidateCache(); return(true); }
/// <summary> /// Append 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">The new colors to add (must match positions length).</param> /// <param name="uvs">The new uvs to add (must match positions length).</param> /// <param name="face">A face with the new triangle indexes. The indexes should be 0 indexed.</param> /// <param name="common"></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); }
static List <ConnectFaceRebuildData> ConnectIndexesPerFace( Face face, List <int> indexes, List <Vertex> vertices, Dictionary <int, int> lookup, int sharedIndexOffset) { if (indexes.Count < 3) { return(null); } List <Edge> perimeter = WingedEdge.SortEdgesByAdjacency(face); int splitCount = indexes.Count; List <List <Vertex> > n_vertices = ArrayUtility.Fill <List <Vertex> >(x => { return(new List <Vertex>()); }, splitCount); List <List <int> > n_sharedIndexes = ArrayUtility.Fill <List <int> >(x => { return(new List <int>()); }, splitCount); List <List <int> > n_indexes = ArrayUtility.Fill <List <int> >(x => { return(new List <int>()); }, splitCount); Vertex center = Vertex.Average(vertices, indexes); Vector3 nrm = Math.Normal(vertices, face.indexesInternal); int index = 0; for (int i = 0; i < perimeter.Count; i++) { int cur = perimeter[i].a; n_vertices[index].Add(vertices[cur]); n_sharedIndexes[index].Add(lookup[cur]); if (indexes.Contains(cur)) { n_indexes[index].Add(n_vertices[index].Count); n_vertices[index].Add(center); n_sharedIndexes[index].Add(sharedIndexOffset); index = (index + 1) % splitCount; n_indexes[index].Add(n_vertices[index].Count); n_vertices[index].Add(vertices[cur]); n_sharedIndexes[index].Add(lookup[cur]); } } List <ConnectFaceRebuildData> faces = new List <ConnectFaceRebuildData>(); for (int i = 0; i < n_vertices.Count; i++) { if (n_vertices[i].Count < 3) { continue; } FaceRebuildData f = AppendElements.FaceWithVertices(n_vertices[i], false); f.sharedIndexes = n_sharedIndexes[i]; Vector3 fn = Math.Normal(n_vertices[i], f.face.indexesInternal); if (Vector3.Dot(nrm, fn) < 0) { f.face.Reverse(); } faces.Add(new ConnectFaceRebuildData(f, n_indexes[i])); } return(faces); }
public static void TestHashCollisions_IVEC3() { IntVec3[] ivec3 = ArrayUtility.Fill <IntVec3>(TestIterationCount, (i) => { return((IntVec3)RandVec3()); }); Assert.IsTrue(TestHashUtility.GetCollisionsCount(ivec3) < TestIterationCount * .05f); }
public static void TestHashCollisions_EDGE() { Edge[] edge = ArrayUtility.Fill <Edge>(TestIterationCount, (i) => { return(RandEdge()); }); Assert.IsTrue(TestHashUtility.GetCollisionsCount(edge) < TestIterationCount * .05f); }