// Essentially Kis only on non-triangular faces // Returns the original number of sides of each face to be used elsewhere // TODO Detect convex faces and use fan triangulation to save on a vertex? public List <int> KisTriangulate() { var faceRoles = new List <ConwayPoly.Roles>(); var vertexRoles = new List <ConwayPoly.Roles>(); var newVerts = _conwayPoly.Faces.Select(f => f.Centroid); var vertexPoints = Enumerable.Concat(_conwayPoly.Vertices.Select(v => v.Position), newVerts); vertexRoles.Concat(Enumerable.Repeat(ConwayPoly.Roles.Existing, vertexPoints.Count())); var originalFaceSides = new List <int>(); // vertex lookup var vlookup = new Dictionary <string, int>(); int n = _conwayPoly.Vertices.Count; for (int i = 0; i < n; i++) { vlookup.Add(_conwayPoly.Vertices[i].Name, i); } var faceIndices = new List <IEnumerable <int> >(); // faces as vertex indices for (int i = 0; i < _conwayPoly.Faces.Count; i++) { int faceSides = _conwayPoly.Faces[i].Sides; if (_conwayPoly.Faces[i].Sides <= 3) { faceIndices.Add(_conwayPoly.ListFacesByVertexIndices()[i]); originalFaceSides.Add(faceSides); faceRoles.Add(_conwayPoly.FaceRoles[i]); } else { foreach (var edge in _conwayPoly.Faces[i].GetHalfedges()) { // create new face from edge start, edge end and centroid faceIndices.Add( new[] { vlookup[edge.Prev.Vertex.Name], vlookup[edge.Vertex.Name], i + n } ); originalFaceSides.Add(faceSides); faceRoles.Add(_conwayPoly.FaceRoles[i]); } } } _conwayPoly = new ConwayPoly(vertexPoints, faceIndices, faceRoles, vertexRoles); return(originalFaceSides); }
// new Color(1.0f, 0.75f, 0.75f), // new Color(1.0f, 0.5f, 0.5f), // new Color(0.8f, 0.4f, 0.4f), // new Color(0.8f, 0.8f, 0.8f), // new Color(0.5f, 0.6f, 0.6f), // new Color(0.6f, 0.0f, 0.0f), // new Color(1.0f, 1.0f, 1.0f), // new Color(0.6f, 0.6f, 0.6f), // new Color(0.5f, 1.0f, 0.5f), // new Color(0.5f, 0.5f, 1.0f), // new Color(0.5f, 1.0f, 1.0f), // new Color(1.0f, 0.5f, 1.0f), public static Mesh BuildMeshFromConwayPoly( ConwayPoly conway, bool generateSubmeshes = false, Color[] colors = null, PolyHydraEnums.ColorMethods colorMethod = PolyHydraEnums.ColorMethods.ByRole, PolyHydraEnums.UVMethods uvMethod = PolyHydraEnums.UVMethods.FirstEdge, bool largeMeshFormat = true ) { Vector2 calcUV(Vector3 point, Vector3 xAxis, Vector3 yAxis) { float u, v; u = Vector3.Project(point, xAxis).magnitude; u *= Vector3.Dot(point, xAxis) > 0 ? 1 : -1; v = Vector3.Project(point, yAxis).magnitude; v *= Vector3.Dot(point, yAxis) > 0 ? 1 : -1; return(new Vector2(u, v)); } if (colors == null) { colors = DefaultFaceColors; } var target = new Mesh(); if (largeMeshFormat) { target.indexFormat = IndexFormat.UInt32; } var meshTriangles = new List <int>(); var meshVertices = new List <Vector3>(); var meshNormals = new List <Vector3>(); var meshColors = new List <Color32>(); var meshUVs = new List <Vector2>(); var edgeUVs = new List <Vector2>(); var barycentricUVs = new List <Vector3>(); var miscUVs1 = new List <Vector4>(); var miscUVs2 = new List <Vector4>(); List <ConwayPoly.Roles> uniqueRoles = null; List <int> uniqueSides = null; List <string> uniqueTags = null; var submeshTriangles = new List <List <int> >(); // TODO // var hasNaked = conway.HasNaked(); // Strip down to Face-Vertex structure var points = conway.ListVerticesByPoints(); var faceIndices = conway.ListFacesByVertexIndices(); // Add faces int index = 0; if (generateSubmeshes) { switch (colorMethod) { case PolyHydraEnums.ColorMethods.ByRole: uniqueRoles = new HashSet <ConwayPoly.Roles>(conway.FaceRoles).ToList(); for (int i = 0; i < uniqueRoles.Count; i++) { submeshTriangles.Add(new List <int>()); } break; case PolyHydraEnums.ColorMethods.BySides: for (int i = 0; i < colors.Length; i++) { submeshTriangles.Add(new List <int>()); } break; case PolyHydraEnums.ColorMethods.ByFaceDirection: for (int i = 0; i < colors.Length; i++) { submeshTriangles.Add(new List <int>()); } break; case PolyHydraEnums.ColorMethods.ByTags: var flattenedTags = conway.FaceTags.SelectMany(d => d.Select(i => i.Item1)); uniqueTags = new HashSet <string>(flattenedTags).ToList(); for (int i = 0; i < uniqueTags.Count + 1; i++) { submeshTriangles.Add(new List <int>()); } break; } } for (var i = 0; i < faceIndices.Length; i++) { var faceIndex = faceIndices[i]; var face = conway.Faces[i]; var faceNormal = face.Normal; var faceCentroid = face.Centroid; ConwayPoly.Roles faceRole = conway.FaceRoles[i]; // Axes for UV mapping Vector3 xAxis = Vector3.right; Vector3 yAxis = Vector3.up; switch (uvMethod) { case PolyHydraEnums.UVMethods.FirstEdge: xAxis = face.Halfedge.Vector; yAxis = Vector3.Cross(xAxis, faceNormal); break; case PolyHydraEnums.UVMethods.BestEdge: xAxis = face.GetBestEdge().Vector; yAxis = Vector3.Cross(xAxis, faceNormal); break; case PolyHydraEnums.UVMethods.FirstVertex: yAxis = face.Centroid - face.GetVertices()[0].Position; xAxis = Vector3.Cross(yAxis, faceNormal); break; case PolyHydraEnums.UVMethods.BestVertex: yAxis = face.Centroid - face.GetBestEdge().Vertex.Position; xAxis = Vector3.Cross(yAxis, faceNormal); break; case PolyHydraEnums.UVMethods.ObjectAligned: // Align towards the highest vertex or edge midpoint (measured in the y direction) Vertex chosenVert = face.GetVertices().OrderBy(vert => vert.Position.y).First(); Halfedge chosenEdge = face.GetHalfedges().OrderBy(edge => edge.Midpoint.y).First(); Vector3 chosenPoint; if (chosenVert.Position.y > chosenEdge.Midpoint.y + 0.01f) // favour edges slightly { chosenPoint = chosenVert.Position; } else { chosenPoint = chosenEdge.Midpoint; } yAxis = face.Centroid - chosenPoint; xAxis = Vector3.Cross(yAxis, faceNormal); break; } Color32 color = CalcFaceColor(conway, colors, colorMethod, i); float faceScale = 0; foreach (var v in face.GetVertices()) { faceScale += Vector3.Distance(v.Position, faceCentroid); } faceScale /= face.Sides; var miscUV1 = new Vector4(faceScale, face.Sides, faceCentroid.magnitude, ((float)i) / faceIndices.Length); var miscUV2 = new Vector4(faceCentroid.x, faceCentroid.y, faceCentroid.z, i); var faceTris = new List <int>(); if (face.Sides > 3) { for (var edgeIndex = 0; edgeIndex < faceIndex.Count; edgeIndex++) { meshVertices.Add(faceCentroid); meshUVs.Add(calcUV(meshVertices[index], xAxis, yAxis)); faceTris.Add(index++); edgeUVs.Add(new Vector2(0, 0)); barycentricUVs.Add(new Vector3(0, 0, 1)); meshVertices.Add(points[faceIndex[edgeIndex]]); meshUVs.Add(calcUV(meshVertices[index], xAxis, yAxis)); faceTris.Add(index++); edgeUVs.Add(new Vector2(1, 1)); barycentricUVs.Add(new Vector3(0, 1, 0)); meshVertices.Add(points[faceIndex[(edgeIndex + 1) % face.Sides]]); meshUVs.Add(calcUV(meshVertices[index], xAxis, yAxis)); faceTris.Add(index++); edgeUVs.Add(new Vector2(1, 1)); barycentricUVs.Add(new Vector3(1, 0, 0)); meshNormals.AddRange(Enumerable.Repeat(faceNormal, 3)); meshColors.AddRange(Enumerable.Repeat(color, 3)); miscUVs1.AddRange(Enumerable.Repeat(miscUV1, 3)); miscUVs2.AddRange(Enumerable.Repeat(miscUV2, 3)); } } else { meshVertices.Add(points[faceIndex[0]]); meshUVs.Add(calcUV(meshVertices[index], xAxis, yAxis)); faceTris.Add(index++); barycentricUVs.Add(new Vector3(0, 0, 1)); meshVertices.Add(points[faceIndex[1]]); meshUVs.Add(calcUV(meshVertices[index], xAxis, yAxis)); faceTris.Add(index++); barycentricUVs.Add(new Vector3(0, 1, 0)); meshVertices.Add(points[faceIndex[2]]); meshUVs.Add(calcUV(meshVertices[index], xAxis, yAxis)); faceTris.Add(index++); barycentricUVs.Add(new Vector3(1, 0, 0)); edgeUVs.AddRange(Enumerable.Repeat(new Vector2(1, 1), 3)); meshNormals.AddRange(Enumerable.Repeat(faceNormal, 3)); meshColors.AddRange(Enumerable.Repeat(color, 3)); miscUVs1.AddRange(Enumerable.Repeat(miscUV1, 3)); miscUVs2.AddRange(Enumerable.Repeat(miscUV2, 3)); } if (generateSubmeshes) { switch (colorMethod) { case PolyHydraEnums.ColorMethods.ByRole: int uniqueRoleIndex = uniqueRoles.IndexOf(faceRole); submeshTriangles[uniqueRoleIndex].AddRange(faceTris); break; case PolyHydraEnums.ColorMethods.BySides: submeshTriangles[face.Sides].AddRange(faceTris); break; case PolyHydraEnums.ColorMethods.ByFaceDirection: submeshTriangles[CalcDirectionIndex(face, colors.Length - 1)].AddRange(faceTris); break; case PolyHydraEnums.ColorMethods.ByTags: if (conway.FaceTags[i].Count > 0) { string htmlColor = conway.FaceTags[i].First(t => t.Item1.StartsWith("#")).Item1; int uniqueTagIndex = uniqueTags.IndexOf(htmlColor); submeshTriangles[uniqueTagIndex + 1].AddRange(faceTris); } else { submeshTriangles[0].AddRange(faceTris); } break; } } else { meshTriangles.AddRange(faceTris); } } target.vertices = meshVertices.Select(x => Jitter(x)).ToArray(); target.normals = meshNormals.ToArray(); if (generateSubmeshes) { target.subMeshCount = submeshTriangles.Count; for (var i = 0; i < submeshTriangles.Count; i++) { target.SetTriangles(submeshTriangles[i], i); } } else { target.triangles = meshTriangles.ToArray(); } target.colors32 = meshColors.ToArray(); target.SetUVs(0, meshUVs); target.SetUVs(1, edgeUVs); target.SetUVs(2, barycentricUVs); target.SetUVs(3, miscUVs1); target.SetUVs(4, miscUVs2); target.RecalculateTangents(); return(target); }
public void Generate() { switch (ShapeType) { case ShapeTypes.Wythoff: var wythoff = new WythoffPoly(PolyType, PrismP, PrismQ); wythoff.BuildFaces(); poly = new ConwayPoly(wythoff); break; case ShapeTypes.Johnson: poly = JohnsonPoly.Build(JohnsonPolyType, PrismP); break; case ShapeTypes.Grid: poly = Grids.Grids.MakeGrid(GridType, GridShape, PrismP, PrismQ); break; } if (ApplyOp) { var o1 = new OpParams { valueA = op1Amount1, valueB = op1Amount2, facesel = op1Facesel }; poly = poly.ApplyOp(op1, o1); } if (PreCanonicalize) { poly = poly.Canonicalize(0.01, 0.01); } if (Canonicalize) { poly = poly.Canonicalize(0.01, 0.01); } if (ApplyOp) { var o2 = new OpParams { valueA = op2Amount1, valueB = op2Amount2, facesel = op2Facesel }; poly = poly.ApplyOp(op2, o2); var o3 = new OpParams { valueA = op3Amount1, valueB = op3Amount2, facesel = op3Facesel }; poly = poly.ApplyOp(op3, o3); } var points = poly.ListVerticesByPoints().ToList(); if (JitterAmount > 0) { for (int i = 0; i < points.Count(); i++) { var point = points[i]; points[i] = new Vector3( point.x + Random.value * JitterAmount, point.y + Random.value * JitterAmount, point.z + Random.value * JitterAmount ); } } var faceIndices = poly.ListFacesByVertexIndices(); // This whole mess is because I can't find a way to regenerate // a probuilder mesh and therefore have to continually create/destroy gameobjects // (which is a mess in edit mode) // If anyone can explain how to simply take an existing probuilder object clear it // and pass in a list of Vector3's and lists of ordered indexes for faces // then please do. if (pbmesh != null && pbmesh.gameObject != null) { if (Application.isPlaying) { Destroy(pbmesh.gameObject); } else { var go = pbmesh.gameObject; UnityEditor.EditorApplication.delayCall += () => { DestroyImmediate(go); }; } } var colors = Enumerable.Range(0, 8).Select(x => Colors.Evaluate(((x / 8f) * ColorRange + ColorOffset) % 1)).ToArray(); pbmesh = ProBuilderMesh.Create(points, new List <Face>()); var faces = new List <PBFace>(); for (var i = 0; i < faceIndices.Length; i++) { var face = faceIndices[i]; var result = AppendElements.CreatePolygon(pbmesh, face, false); if (result != null) { pbmesh.SetFaceColor(result, colors[(int)poly.FaceRoles[i]]); faces.Add(result); } } if (faces.Count < 1 || pbmesh == null) { return; } pbmesh.SetMaterial(faces, material); pbmesh.ToMesh(); pbmesh.Refresh(); }