/// <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 CsgPolygon[] polygons = new CsgPolygon[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(v[1][0], v[1][1], 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 CsgPolygon(verts); } return(CSG.fromPolygons(polygons)); }
/// <summary> /// Creates a Polygon from an array of points. /// </summary> /// <param name="points"></param> /// <param name="shared"></param> /// <returns></returns> public static CsgPolygon 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)); } CsgPolygon polygon = new CsgPolygon(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 <CsgPolygon> extrudePolygon(CsgPolygon 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 <CsgPolygon> polygons = new List <CsgPolygon>(); 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); CsgPolygon poly = new CsgPolygon(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 CsgPolygon(top, polygon.shared)); polygons.Add(new CsgPolygon(bot, polygon.shared)); return(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(CsgPolygon polygon, ref List <CsgPolygon> coplanarFront, ref List <CsgPolygon> coplanarBack, ref List <CsgPolygon> front, ref List <CsgPolygon> 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; double 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: 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 CsgPolygon(f, polygon.shared)); } if (b.Count >= 3) { back.Add(new CsgPolygon(b, polygon.shared)); } break; } }
public Mesh toMesh() { List <CsgPolygon> trisFromPolygons = new List <CsgPolygon>(); // triangulate polygons for (int i = this.polygons.Count - 1; i >= 0; i--) { if (this.polygons[i].vertices.Length > 3) { 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 CsgPolygon(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++) { CsgPolygon tri = this.polygons[pi]; 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 (MathHelper.AlmostEqual(vertices[i].pos, vertex.pos) && MathHelper.AlmostEqual(vertices[i].normal, 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; } } 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.Faces = tris; //m.RecalculateBounds(); //m.RecalculateNormals(); //m.Optimize(); return(m); }