public static BspNode AddNode(BspNode inParent, BspNode.EBspLocation inLocation, Face inFace, int inFlags) { BspNode newNode = new BspNode(); newNode.plane = inFace.GetPlane(); newNode.face = inFace; newNode.front = newNode.back = newNode.planar = null; newNode.flags = inFlags; if (inLocation == BspNode.EBspLocation.BspLocation_Front) { // check that front node is null inParent.front = newNode; } else if (inLocation == BspNode.EBspLocation.BspLocation_Back) { // TODO: check that back node is null inParent.back = newNode; } else if (inLocation == BspNode.EBspLocation.BspLocation_Planar) { // go to the last planar node BspNode lastPlanar = inParent; while (lastPlanar.planar != null) { lastPlanar = lastPlanar.planar; } // add planar node lastPlanar.planar = newNode; } return(newNode); }
private void PerformNode(BspNode inNode, Face inFace, int nodeSide, OperationInfo info) { while (inNode != null) { Face.EPlaneSide side = inFace.Side(inNode.plane); switch (side) { case Face.EPlaneSide.Side_Front: // nodeSide = nodeSide | (inNode.IsCsg() ? 1 : 0); // leaf node if (inNode.front == null) { // set operation infos info.leafNode = inNode; info.leafLocation = BspNode.EBspLocation.BspLocation_Front; // we are done, process face ProcessFace(inFace, SIDE_Outside, info); } // get to next front node (if any) inNode = inNode.front; break; case Face.EPlaneSide.Side_Back: int backSide = inNode.IsCsg() ? 0 : 1; // nodeSide = nodeSide & backSide; // leaf node if (inNode.back == null) { // set leaf infos info.leafNode = inNode; info.leafLocation = BspNode.EBspLocation.BspLocation_Back; // we are done, process face ProcessFace(inFace, SIDE_Inside, info); } // get to next front node (if any) inNode = inNode.back; break; case Face.EPlaneSide.Side_Split: // split face and process front and back Face frontFace, backFace; // inFace.Split(inNode.plane, out frontFace, out backFace); // TODO: set polygon cutted flags frontFace.flags |= Face.FaceFlags_WasCutted; backFace.flags |= Face.FaceFlags_WasCutted; // front node is a leaf node if (inNode.front == null) { // info.leafNode = inNode; info.leafLocation = BspNode.EBspLocation.BspLocation_Front; ProcessFace(frontFace, SIDE_Outside, info); } else { PerformNode(inNode.front, frontFace, nodeSide, info); } // Prcess back node with back face if (inNode.back == null) { // info.leafNode = inNode; info.leafLocation = BspNode.EBspLocation.BspLocation_Back; ProcessFace(backFace, SIDE_Inside, info); } else { // process back node with new face PerformNode(inNode.back, backFace, nodeSide, info); } // stop loop inNode = null; break; case Face.EPlaneSide.Side_Planar: BspNode front, back; if (info.wasPlanar == true) { Debug.Log("Reentering Planar Nodes!"); } // set operation infos info.wasPlanar = true; info.backNode = null; info.processingBack = false; if (Vector3.Dot(inFace.GetPlane().normal, inNode.plane.normal) >= 0.0f) { // same order as we face in the same order front = inNode.front; back = inNode.back; // we are for now outside (as we are looking outside) info.planarSide = SIDE_Outside; } else { // reverse order as we are facing in the opposite direction front = inNode.back; back = inNode.front; // we are now inside as we are looking to the inside info.planarSide = SIDE_Inside; } // we are leaf node (coplanar face) if (front == null && back == null) { // set leaf stuff info.leafNode = inNode; info.leafLocation = BspNode.EBspLocation.BspLocation_Planar; // process node info.processingBack = true; // process face ProcessFace(inFace, InverseSide(info.planarSide), info); // stop loop inNode = null; } else if (front == null && back != null) { // only back nodes info.processingBack = true; // process back inNode = back; } else { // tread like we were on front side (maybe we do have a back node) info.processingBack = false; // remember back node info.backNode = back; // process front inNode = front; } break; } } }
// void Partition(BspNode inNode, List <Face> inFaces) { List <Face> frontFaces = new List <Face>(); List <Face> backFaces = new List <Face>(); Face nodeFace; // find best splitter plane for this bool ret = FindSplitter(inFaces, out nodeFace); // return has to be true!!! if (ret == false) { Debug.DebugBreak(); Debug.Log("Error processing Mesh!"); return; } // setup node inNode.front = null; inNode.back = null; inNode.planar = null; inNode.face = nodeFace; inNode.plane = nodeFace.GetPlane(); // split remaining faces into lists for (int i = 0; i < inFaces.Count; ++i) { // get face Face face = inFaces[i] as Face; // do not process our self if (face == nodeFace) { continue; } Face.EPlaneSide side = face.Side(inNode.plane); switch (side) { case Face.EPlaneSide.Side_Front: frontFaces.Add(face); break; case Face.EPlaneSide.Side_Back: backFaces.Add(face); break; case Face.EPlaneSide.Side_Planar: // get last planar node BspNode lastPlanar = inNode; while (lastPlanar.planar != null) { lastPlanar = lastPlanar.planar; } // create new planar node BspNode planar = lastPlanar.planar = new BspNode(); // setup planar node planar.front = null; planar.back = null; planar.planar = null; planar.face = face; planar.plane = face.GetPlane(); break; case Face.EPlaneSide.Side_Split: // TODO... Face front, back; // split face into two parts... ret = face.Split(inNode.plane, out front, out back); if (ret == false) { Debug.DebugBreak(); } // add to front and back frontFaces.Add(front); backFaces.Add(back); break; } } // optimizing a bit, clear in array list as we do not need it any more inFaces.Clear(); // process front faces if (frontFaces.Count > 0) { inNode.front = new BspNode(); // partition front faces Partition(inNode.front, frontFaces); } // process back faces if (backFaces.Count > 0) { inNode.back = new BspNode(); // partition back faces Partition(inNode.back, backFaces); } }
// bool FindSplitter(List <Face> inFaces, out Face outFace) { int increase = 1; int bestValue = 9999999; // reset out face... outFace = null; // setup optimization... switch (bspOptm) { case BspOptm_Worse: increase = Mathf.Max(1, inFaces.Count / 24); break; case BspOptm_Average: increase = Mathf.Max(1, inFaces.Count / 12); break; case BspOptm_Best: default: increase = 1; break; } // find best splitter plane for (int i = 0; i < inFaces.Count; i += increase) { // statistics int numSplits = 0, numFront = 0, numBack = 0, numPlanar = 0; // Face splitterFace = inFaces[i] as Face; // Plane splitterPlane = splitterFace.GetPlane(); // sort all faces to side where it lies... for (int j = 0; j < inFaces.Count; ++j) { Face.EPlaneSide side = (inFaces[j] as Face).Side(splitterPlane); switch (side) { case Face.EPlaneSide.Side_Front: numFront++; break; case Face.EPlaneSide.Side_Back: numBack++; break; case Face.EPlaneSide.Side_Planar: numPlanar++; break; case Face.EPlaneSide.Side_Split: numSplits++; break; default: //ERROR Debug.DebugBreak(); break; } } // int val = numSplits * 5 + Mathf.Abs(numFront - numBack) + numPlanar; if (val < bestValue) { bestValue = val; outFace = splitterFace; } } // if we have a face found, return true return(outFace != null); }
public static void MergeCoplanars(BspNode inNode) { // proceed front nodes if (inNode.front != null) { MergeCoplanars(inNode.front); } // proceed back nodes if (inNode.back != null) { MergeCoplanars(inNode.back); } // first try to merge other co planar nodes if (inNode.planar != null) { MergeCoplanars(inNode.planar); } // bool tryToMerge = (inNode.flags & BspNode.BspFlags_IsDestroyed) == 0; while (tryToMerge) { // get planar node BspNode planarNode = inNode.planar; // assume we are done tryToMerge = false; // get through all planar nodes while (planarNode != null) { // if we are destroyed, proceed to next if ((planarNode.flags & BspNode.BspFlags_IsDestroyed) != 0) { // proceed to next planarNode = planarNode.planar; continue; } Face thisFace = inNode.face; Face otherFace = planarNode.face; // are we facing the same direction if (Vector3.Dot(thisFace.GetPlane().normal, otherFace.GetPlane().normal) > 0.995f) { // result Face merged; // if (thisFace.Merge(otherFace, out merged)) { // replace this face with merged thisFace = merged; // set other node as destroyed planarNode.flags |= BspNode.BspFlags_IsDestroyed; // retry to merge tryToMerge = true; } } // proceed to next planarNode = planarNode.planar; } } }