/// <summary> /// Creates a Polygon from an array of points. /// </summary> /// <param name="points"></param> /// <param name="shared"></param> /// <returns></returns> public static Polygon createPolygon(Vector3[] points, System.Object shared = null) { if (points.Length < 2) { return null; } List<IVertex> vertices = new List<IVertex>(); foreach (Vector3 pos in points) { vertices.Add(new Vertex(pos)); } Polygon polygon = new Polygon(vertices, shared); return polygon; }
/// <summary> /// Extrudes a polygon. /// </summary> /// <param name="polygon">The polygon to extrude</param> /// <param name="distance">Extrusion distance</param> /// <param name="normal">Optional normal to extrude along, default is polygon normal</param> /// <returns></returns> public static List<Polygon> extrudePolygon(Polygon polygon, float distance, Vector3? normal = null) { normal = normal != null ? normal : polygon.plane.normal; Vector3 du = normal.GetValueOrDefault(); IVertex[] vertices = polygon.vertices; List<IVertex> top = new List<IVertex>(); List<IVertex> bot = new List<IVertex>(); List<Polygon> polygons = new List<Polygon>(); Vector3 invNormal = normal.GetValueOrDefault(); du *= distance; invNormal *= -1f; for (int i = 0; i < vertices.Length; i++) { int j = (i + 1) % vertices.Length; Vector3 p1 = vertices[i].pos; Vector3 p2 = vertices[j].pos; Vector3 p3 = p2 + du; Vector3 p4 = p1 + du; Plane plane = Plane.fromPoints(p1, p2, p3); Vertex v1 = new Vertex(p1, plane.normal); Vertex v2 = new Vertex(p2, plane.normal); Vertex v3 = new Vertex(p3, plane.normal); Vertex v4 = new Vertex(p4, plane.normal); Polygon poly = new Polygon(new List<IVertex>(new IVertex[] { v1, v2, v3, v4 }), polygon.shared); polygons.Add(poly); top.Add(new Vertex(p4, normal.GetValueOrDefault())); bot.Insert(0, new Vertex(p1, invNormal)); } polygons.Add(new Polygon(top, polygon.shared)); polygons.Add(new Polygon(bot, polygon.shared)); return polygons; }
/// <summary> /// Create CSG from array, does not clone the polygons /// </summary> /// <param name="polygon"></param> /// <returns></returns> private static CSG fromPolygons(Polygon[] polygons) { //TODO: Optimize polygons to share vertices CSG csg = new CSG(); csg.polygons.AddRange(polygons); return csg; }
/// <summary> /// Cube function, Untested but compiles /// </summary> /// <param name="center">world space center of the cube</param> /// <param name="radius">size of the cube created at center</param> /// <returns></returns> public static CSG cube(Vector3? center, Vector3? radius) { Vector3 c = center.GetValueOrDefault(Vector3.zero); Vector3 r = radius.GetValueOrDefault(Vector3.one); //TODO: Test if this works Polygon[] polygons = new Polygon[6]; int[][][] data = new int[][][] { new int[][]{new int[]{0, 4, 6, 2}, new int[]{-1, 0, 0}}, new int[][]{new int[]{1, 3, 7, 5}, new int[]{1, 0, 0}}, new int[][]{new int[]{0, 1, 5, 4}, new int[]{0, -1, 0}}, new int[][]{new int[]{2, 6, 7, 3}, new int[]{0, 1, 0}}, new int[][]{new int[]{0, 2, 3, 1}, new int[]{0, 0, -1}}, new int[][]{new int[]{4, 5, 7, 6}, new int[]{0, 0, 1}} }; for (int x = 0; x < 6; x++) { int[][] v = data[x]; Vector3 normal = new Vector3((float)v[1][0], (float)v[1][1], (float)v[1][2]); IVertex[] verts = new IVertex[4]; for (int i = 0; i < 4; i++) { verts[i] = new Vertex( new Vector3( c.x + (r.x * (2 * (((v[0][i] & 1) > 0) ? 1 : 0) - 1)), c.y + (r.y * (2 * (((v[0][i] & 2) > 0) ? 1 : 0) - 1)), c.z + (r.z * (2 * (((v[0][i] & 4) > 0) ? 1 : 0) - 1))), normal ); } polygons[x] = new Polygon(verts); } return CSG.fromPolygons(polygons); }
/// <summary> /// Split `polygon` by this plane if needed, then put the polygon or polygon /// fragments in the appropriate lists. Coplanar polygons go into either /// `coplanarFront` or `coplanarBack` depending on their orientation with /// respect to this plane. Polygons in front or in back of this plane go into /// either `front` or `back` /// </summary> /// <param name="polygon"></param> /// <param name="coplanarFront"></param> /// <param name="coplanarBack"></param> /// <param name="front"></param> /// <param name="back"></param> public void splitPolygon(Polygon polygon, ref List <Polygon> coplanarFront, ref List <Polygon> coplanarBack, ref List <Polygon> front, ref List <Polygon> back) { //Debug.Log("splitPolygon: " + polygon.vertices[0].pos + ", " + polygon.vertices[1].pos + ", " + polygon.vertices[2].pos); IVertex[] vertices = polygon.vertices; int polygonType = 0; List <int> types = new List <int>(); int type; float t; int i; for (i = 0; i < vertices.Length; i++) { t = Vector3.Dot(this.normal, vertices[i].pos) - this.w; if (t < -Plane.EPSILON) { type = BACK; } else if (t > Plane.EPSILON) { type = FRONT; } else { type = COPLANAR; } polygonType |= type; types.Add(type); } // Put the polygon in the correct list, splitting it when necessary. switch (polygonType) { case COPLANAR: if (Vector3.Dot(this.normal, polygon.plane.normal) > 0) { coplanarFront.Add(polygon); } else { coplanarBack.Add(polygon); } break; case FRONT: front.Add(polygon); break; case BACK: back.Add(polygon); break; default: case SPANNING: if (polygonType != SPANNING) { Debug.Log("Defaulting to spanning"); } List <IVertex> f = new List <IVertex>(); List <IVertex> b = new List <IVertex>(); for (i = 0; i < vertices.Length; i++) { int j = (i + 1) % vertices.Length; int ti = types[i]; int tj = types[j]; IVertex vi = vertices[i]; IVertex vj = vertices[j]; if (ti != BACK) { f.Add(vi); } if (ti != FRONT) { b.Add(ti != BACK ? vi.clone() : vi); } if ((ti | tj) == SPANNING) { t = (this.w - Vector3.Dot(this.normal, vi.pos)) / Vector3.Dot(this.normal, vj.pos - vi.pos); IVertex v = vi.interpolate(vj, t); f.Add(v); b.Add(v.clone()); } } if (f.Count >= 3) { front.Add(new Polygon(f, polygon.shared)); } if (b.Count >= 3) { back.Add(new Polygon(b, polygon.shared)); } break; } }
/// <summary> /// Split `polygon` by this plane if needed, then put the polygon or polygon /// fragments in the appropriate lists. Coplanar polygons go into either /// `coplanarFront` or `coplanarBack` depending on their orientation with /// respect to this plane. Polygons in front or in back of this plane go into /// either `front` or `back` /// </summary> /// <param name="polygon"></param> /// <param name="coplanarFront"></param> /// <param name="coplanarBack"></param> /// <param name="front"></param> /// <param name="back"></param> public void splitPolygon(Polygon polygon, ref List<Polygon> coplanarFront, ref List<Polygon> coplanarBack, ref List<Polygon> front, ref List<Polygon> back) { //Debug.Log("splitPolygon: " + polygon.vertices[0].pos + ", " + polygon.vertices[1].pos + ", " + polygon.vertices[2].pos); IVertex[] vertices = polygon.vertices; int polygonType = 0; List<int> types = new List<int>(); int type; float t; int i; for (i = 0; i < vertices.Length; i++) { t = Vector3.Dot(this.normal, vertices[i].pos) - this.w; if (t < -Plane.EPSILON) { type = BACK; } else if (t > Plane.EPSILON) { type = FRONT; } else { type = COPLANAR; } polygonType |= type; types.Add(type); } // Put the polygon in the correct list, splitting it when necessary. switch (polygonType) { case COPLANAR: if (Vector3.Dot(this.normal, polygon.plane.normal) > 0) { coplanarFront.Add(polygon); } else { coplanarBack.Add(polygon); } break; case FRONT: front.Add(polygon); break; case BACK: back.Add(polygon); break; default: case SPANNING: if (polygonType != SPANNING) Debug.Log("Defaulting to spanning"); List<IVertex> f = new List<IVertex>(); List<IVertex> b = new List<IVertex>(); for (i = 0; i < vertices.Length; i++) { int j = (i + 1) % vertices.Length; int ti = types[i]; int tj = types[j]; IVertex vi = vertices[i]; IVertex vj = vertices[j]; if (ti != BACK) f.Add(vi); if (ti != FRONT) b.Add(ti != BACK ? vi.clone() : vi); if ((ti | tj) == SPANNING) { t = (this.w - Vector3.Dot(this.normal, vi.pos)) / Vector3.Dot(this.normal, vj.pos - vi.pos); IVertex v = vi.interpolate(vj, t); f.Add(v); b.Add(v.clone()); } } if (f.Count >= 3) front.Add(new Polygon(f, polygon.shared)); if (b.Count >= 3) back.Add(new Polygon(b, polygon.shared)); break; } }
public Mesh toMesh() { List <Polygon> trisFromPolygons = new List <Polygon>(); // triangulate polygons for (int i = this.polygons.Count - 1; i >= 0; i--) { if (this.polygons[i].vertices.Length > 3) { //Debug.Log("!!! Poly to Tri (order): " + this.polygons[i].vertices.Length); for (int vi = 1; vi < this.polygons[i].vertices.Length - 1; vi++) { IVertex[] tri = new IVertex[] { this.polygons[i].vertices[0], this.polygons[i].vertices[vi], this.polygons[i].vertices[vi + 1] }; trisFromPolygons.Add(new Polygon(tri)); } // the original polygon is replaced by a set of triangles this.polygons.RemoveAt(i); } } this.polygons.AddRange(trisFromPolygons); // TODO: Simplify mesh - the boolean CSG algorithm leaves lots of coplanar // polygons that share an edge that could be simplified. // At this point, we have a soup of triangles without regard for shared vertices. // We index these to combine any vertices with identical positions & normals // (and maybe later UVs & vertex colors) List <Vertex> vertices = new List <Vertex>(); int[] tris = new int[this.polygons.Count * 3]; for (int pi = 0; pi < this.polygons.Count; pi++) { Polygon tri = this.polygons[pi]; if (tri.vertices.Length > 3) { Debug.LogError("Polygon should be a triangle, but isn't !!"); } for (int vi = 0; vi < 3; vi++) { Vertex vertex = tri.vertices[vi] as Vertex; bool equivalentVertexAlreadyInList = false; for (int i = 0; i < vertices.Count; i++) { if (vertices[i].pos.ApproximatelyEqual(vertex.pos) && vertices[i].normal.ApproximatelyEqual(vertex.normal)) { equivalentVertexAlreadyInList = true; vertex.index = vertices[i].index; } } if (!equivalentVertexAlreadyInList) { vertices.Add(vertex); vertex.index = vertices.Count - 1; } tris[(pi * 3) + vi] = vertex.index; } //Debug.Log(string.Format("Added tri {0},{1},{2}: {3},{4},{5}", pi, pi+1, pi+2, tris[pi], tris[pi+1], tris[pi+2])); } Vector3[] verts = new Vector3[this.polygons.Count * 3]; Vector3[] normals = new Vector3[this.polygons.Count * 3]; Mesh m = new Mesh(); for (int i = 0; i < vertices.Count; i++) { verts[i] = vertices[i].pos; normals[i] = vertices[i].normal; } m.vertices = verts; m.normals = normals; m.triangles = tris; //m.RecalculateBounds(); //m.RecalculateNormals(); //m.Optimize(); //Debug.Log("toMesh verts, normals, tris: " + m.vertices.Length + ", " +m.normals.Length+", "+m.triangles.Length); return(m); }