/// <summary> /// Chooses a split polygon by scoring the polygon choices. /// /// This algorithm is approximately O(N**2), so it's likely to take a while. /// /// This method runs as a coroutine so should be run until completion. /// </summary> /// <param name="polygons">The polygons to choose from</param> /// <param name="result">The resulting split polygon, along with classifications for other polygons in relation.</param> /// <returns></returns> private static IEnumerator ChooseSplitPolygonBestChoice(List <Polygon> polygons, ChooseSplitPolygonResult result) { int bestChoiceScore = int.MaxValue; foreach (var p1 in polygons) { if (p1.Used) { continue; } ChooseSplitPolygonResult potentialResult = new ChooseSplitPolygonResult(p1); potentialResult.Classify(polygons); int frontfaces = potentialResult.Front.Count - 1; // -1 because p1 is included. int backfaces = potentialResult.Back.Count; int splits = potentialResult.Spanning.Count; int score = Math.Abs(frontfaces - backfaces) + (splits * 8); if (score < bestChoiceScore) { bestChoiceScore = score; result.Assign(potentialResult); } yield return(null); } }
/// <summary> /// Chooses a split polygon at random (non-deterministic). Also classifies /// the rest of the polygons by whether they are in front, behind, spanning, or coincident. /// /// This runs as a coroutine, however, this specific version only iterates once. /// </summary> /// <param name="polygons">The polygons to choose from</param> /// <param name="result">The resulting polygons and polygon classification</param> /// <returns></returns> private static IEnumerator ChooseSplitPolygonRandom(List <Polygon> polygons, ChooseSplitPolygonResult result) { int index = Random.Range(0, polygons.Count); result.Assign(new ChooseSplitPolygonResult(polygons[index])); result.Classify(polygons); yield return(null); }
/// <summary> /// Copies the field values from another instance. /// </summary> /// <param name="cp"></param> public void Assign(ChooseSplitPolygonResult cp) { this.Polygon = cp.Polygon; this.Front = cp.Front; this.Back = cp.Back; this.Spanning = cp.Spanning; this.Coincident = cp.Coincident; }
/// <summary> /// Builds each node of the tree /// </summary> /// <param name="polygons"></param> /// <param name="convexNodes">A list to add convex leaf nodes to.</param> /// <param name="settings"></param> /// <returns></returns> private IEnumerator BuildTree(List <Polygon> polygons, List <SolidLeafBSPTree> convexNodes, BuildSettings settings) { ChooseSplitPolygonResult result = new ChooseSplitPolygonResult(); if (settings.HighQuality) { var it = ChooseSplitPolygonBestChoice(polygons, result); while (it.MoveNext()) { yield return(null); } } else { var it = ChooseSplitPolygonRandom(polygons, result); while (it.MoveNext()) { yield return(null); } } var splitPolygon = result.Polygon; splitPolygon.MarkUsed(); this.Plane = splitPolygon.Plane; //Classify the coincident polygons to either the front of back list. foreach (var polygon in result.Coincident) { var dp = Vector3.Dot(splitPolygon.Plane.normal, polygon.Plane.normal); if (dp > 0) { result.Front.Add(polygon); } else { result.Back.Add(polygon); } } //Split the spanning polygons and add them to front/back list. foreach (var polygon in result.Spanning) { var faces = SplitPolygon(polygon, Plane); result.Front.Add(faces.First()); result.Back.Add(faces.Last()); } //check if all frontfaces are used bool allUsed = result.Front.Count > 0 && result.Front.All(ff => ff.Used); if (allUsed) { this.Polygons = result.Front; this.IsConvexLeaf = true; convexNodes.Add(this); } else { if (result.Front.Count > 0) { FrontTree = new SolidLeafBSPTree(); var it = FrontTree.BuildTree(result.Front, convexNodes, settings); while (it.MoveNext()) { yield return(null); } } if (result.Back.Count > 0) { BackTree = new SolidLeafBSPTree(); var it = BackTree.BuildTree(result.Back, convexNodes, settings); while (it.MoveNext()) { yield return(null); } } } }