/** * Create a new BSPTree instance from @mesh using @transform * to transform all vertices, normals, and tangents. */ public static BSPTree FromMesh(Mesh mesh, Matrix4x4 transform) { BSPTree tree = new BSPTree(); // get a list of all triangles in @mesh List <Triangle> meshTriangles = GetMeshTriangles(mesh); // loop through each triangle and transform its vertices by @transform for (int i = 0; i < meshTriangles.Count; i++) { Triangle tri = meshTriangles[i]; for (int vi = 0; vi < 3; vi++) { Vertex vtx = TransformVertex(tri.GetVertexByIndex(vi), transform); tri.SetVertexByIndex(vi, vtx); } tri.RebuildOrientationPlane(); meshTriangles[i] = tri; } tree.AddTriangles(meshTriangles); return(tree); }
/** * Categorize or slice @triangle using @plane. Triangles existing completely on the front side of @plane * are placed in @greaterThan, triangles exisitng completely on the back side of @plane are placed in * @lessThan. Co-planar triangles go in @greaterThanPlanar if their normals are the same direction as * @plane's normal, otherwise they go in @lessThanPlanar. * * If a triangle spans @plane and has vertices on both sides, it is split into multiple triangles * along @plane. The resulting triangles are then categorized as specified above (in this case they are either * in front or back, they could not be co-planar). * * Determining the orientation of a given vertex involves taking the dot product of the vertex's position with * @plane's normal and comparing that to @plane's offset from the origin (plane.D). If the dot-product is greater * than that offset (plus some small epsilon), it is in front; if it is less than that offset (minus a small epsilon) * it is in back. Otherwise it is co-planar. * */ public static Orientation SliceTriangle(Triangle triangle, Plane plane, IList<Triangle> lessThan, IList<Triangle> greaterThan, IList<Triangle> lessThanPlanar, IList<Triangle> greaterThanPlanar) { Orientation[] vertOrientations = new Orientation[3]; Orientation triOrientation = Orientation.CoPlanar; int orientationsFound = 0; // loop through each vertex and categorize each based // on its position relative to @plane. for(int i =0; i < 3; i++) { Orientation currentOrientation = ClassifyVertexOrientation(triangle.GetVertexByIndex(i), plane); vertOrientations[i] = currentOrientation; orientationsFound |= (int)currentOrientation; } // classify @triangle's orientation relative to @plane based // on the orientations of all vertices. if (orientationsFound > (int)Orientation.GreaterThan) triOrientation = Orientation.Split; else triOrientation = (Orientation)orientationsFound; // place @triangle in the appropriate list based on its orientation, or // split it if necessary switch(triOrientation) { case Orientation.CoPlanar: // if @triangle's normal matches @plane's normal, i.e. dot product will be 1, // then we consider @triangle to be front-facing float planeTriOrientation = triangle.OrientationPlane.Normal.Dot(plane.Normal); if(planeTriOrientation > 0) greaterThanPlanar.Add(triangle); else lessThanPlanar.Add(triangle); break; case Orientation.LessThan: lessThan.Add(triangle); break; case Orientation.GreaterThan: greaterThan.Add(triangle); break; case Orientation.Split: List<Vertex> ltSplit = new List<Vertex>(); List<Vertex> gtSplit = new List<Vertex>(); // loop through each edge in @triangle. // @currentVertex = edge's first vertex. // @nextVertex = edge's second vertex. // @currentOrientation = orientation of the edge's first vertex. // @nextOrientation = orientation of the edge's second vertex. for(int i=0; i < 3; i++) { int currentIndex = i; int nextIndex = currentIndex < 2 ? currentIndex + 1 : 0; Orientation currentOrientation = vertOrientations[currentIndex]; Orientation nextOrientation = vertOrientations[nextIndex]; Vertex currentVertex = triangle.GetVertexByIndex(currentIndex); Vertex nextVertex = triangle.GetVertexByIndex(nextIndex); Vector3D currentEdge = nextVertex.Position.SubtractedBy(currentVertex.Position); // vertices are traversed in clock-wise order, which means @triangle's edges // are also traversed in clock-wise order. this means that as vertices are added to // @gtSplit and @ltSplit, their respective vertices will also be in clock-wise order. switch(currentOrientation) { case Orientation.CoPlanar: gtSplit.Add(currentVertex); ltSplit.Add(currentVertex); break; case Orientation.LessThan: ltSplit.Add(currentVertex); break; case Orientation.GreaterThan: gtSplit.Add(currentVertex); break; } // if we move from a "GreaterThan" orientation to a "LessThan" orientation or vice-versa, // then we have crossed @plane. this means we need to interpolate between the edge's vertices // to create a new vertex that is co-planar with @plane. if(currentOrientation == Orientation.GreaterThan && nextOrientation == Orientation.LessThan || currentOrientation == Orientation.LessThan && nextOrientation == Orientation.GreaterThan ) { float splitPortion = plane.D - plane.Normal.Dot(currentVertex.Position); float fullPortion = plane.Normal.Dot(currentEdge); float splitFraction = splitPortion / fullPortion; Vertex splitVertex = currentVertex.Lerped(nextVertex, splitFraction); gtSplit.Add(splitVertex); ltSplit.Add(splitVertex); } } // create 1 new triangle if @ltSplit contains 3 vertices. create 2 triangles if it // contains 4 if (ltSplit.Count >= 3) { lessThan.Add(new Triangle(ltSplit[0], ltSplit[1], ltSplit[2])); if(ltSplit.Count == 4) lessThan.Add(new Triangle(ltSplit[0], ltSplit[2], ltSplit[3])); } // create 1 new triangle if @gtSplit contains 3 vertices. create 2 triangles if it // contains 4 if (gtSplit.Count >= 3) { greaterThan.Add(new Triangle(gtSplit[0], gtSplit[1], gtSplit[2])); if(gtSplit.Count == 4) greaterThan.Add(new Triangle(gtSplit[0], gtSplit[2], gtSplit[3])); } break; } return triOrientation; }
public static Orientation SliceTriangle(Triangle triangle, Plane plane, IList <Triangle> lessThan, IList <Triangle> greaterThan, IList <Triangle> lessThanPlanar, IList <Triangle> greaterThanPlanar, bool usePlaneBounds = false) { /*if (usePlaneBounds) { * if (!plane.PlaneBounds.Intersects(triangle.OrientationPlane.PlaneBounds)) { * Orientation orientation = ClassifyVertexOrientation(triangle.A, plane); * switch (orientation) { * case Orientation.GreaterThan: * greaterThan.Add(triangle); * return Orientation.GreaterThan; * case Orientation.LessThan: * lessThan.Add(triangle); * return Orientation.LessThan; * } * } * }*/ Orientation[] vertOrientations = new Orientation[3]; Orientation triOrientation = Orientation.CoPlanar; int orientationsFound = 0; // loop through each vertex and categorize each based // on its position relative to @plane. for (int i = 0; i < 3; i++) { Orientation currentOrientation = ClassifyVertexOrientation(triangle.GetVertexByIndex(i), plane); vertOrientations[i] = currentOrientation; orientationsFound |= (int)currentOrientation; } // classify @triangle's orientation relative to @plane based // on the orientations of all vertices. if (orientationsFound > (int)Orientation.GreaterThan) { triOrientation = Orientation.Split; } else { triOrientation = (Orientation)orientationsFound; } // place @triangle in the appropriate list based on its orientation, or // split it if necessary switch (triOrientation) { case Orientation.CoPlanar: // if @triangle's normal matches @plane's normal, i.e. dot product will be 1, // then we consider @triangle to be front-facing float planeTriOrientation = triangle.OrientationPlane.Normal.Dot(plane.Normal); if (planeTriOrientation > 0) { greaterThanPlanar.Add(triangle); } else { lessThanPlanar.Add(triangle); } break; case Orientation.LessThan: lessThan.Add(triangle); break; case Orientation.GreaterThan: greaterThan.Add(triangle); break; case Orientation.Split: List <Vertex> ltSplit = new List <Vertex>(); List <Vertex> gtSplit = new List <Vertex>(); // loop through each edge in @triangle. // @currentVertex = edge's first vertex. // @nextVertex = edge's second vertex. // @currentOrientation = orientation of the edge's first vertex. // @nextOrientation = orientation of the edge's second vertex. for (int i = 0; i < 3; i++) { int currentIndex = i; int nextIndex = currentIndex < 2 ? currentIndex + 1 : 0; Orientation currentOrientation = vertOrientations[currentIndex]; Orientation nextOrientation = vertOrientations[nextIndex]; Vertex currentVertex = triangle.GetVertexByIndex(currentIndex); Vertex nextVertex = triangle.GetVertexByIndex(nextIndex); Vector3D currentEdge = nextVertex.Position.SubtractedBy(currentVertex.Position, tempVector3D); // vertices are traversed in clock-wise order, which means @triangle's edges // are also traversed in clock-wise order. this means that as vertices are added to // @gtSplit and @ltSplit, their respective vertices will also be in clock-wise order. switch (currentOrientation) { case Orientation.CoPlanar: gtSplit.Add(currentVertex); ltSplit.Add(currentVertex); break; case Orientation.LessThan: ltSplit.Add(currentVertex); break; case Orientation.GreaterThan: gtSplit.Add(currentVertex); break; } // if we move from a "GreaterThan" orientation to a "LessThan" orientation or vice-versa, // then we have crossed @plane. this means we need to interpolate between the edge's vertices // to create a new vertex that is co-planar with @plane. if (currentOrientation == Orientation.GreaterThan && nextOrientation == Orientation.LessThan || currentOrientation == Orientation.LessThan && nextOrientation == Orientation.GreaterThan) { float splitPortion = plane.D - plane.Normal.Dot(currentVertex.Position); float fullPortion = plane.Normal.Dot(currentEdge); float splitFraction = splitPortion / fullPortion; Vertex splitVertex = currentVertex.Lerped(nextVertex, splitFraction, tempVertex); gtSplit.Add(new Vertex(splitVertex)); ltSplit.Add(new Vertex(splitVertex)); } } // create 1 new triangle if @ltSplit contains 3 vertices. create 2 triangles if it // contains 4 if (ltSplit.Count >= 3) { lessThan.Add(new Triangle(ltSplit[0], ltSplit[1], ltSplit[2])); if (ltSplit.Count == 4) { lessThan.Add(new Triangle(ltSplit[0], ltSplit[2], ltSplit[3])); } } // create 1 new triangle if @gtSplit contains 3 vertices. create 2 triangles if it // contains 4 if (gtSplit.Count >= 3) { greaterThan.Add(new Triangle(gtSplit[0], gtSplit[1], gtSplit[2])); if (gtSplit.Count == 4) { greaterThan.Add(new Triangle(gtSplit[0], gtSplit[2], gtSplit[3])); } } break; } return(triOrientation); }