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); }
// public static void GetFaces(BspNode inNode, List <Face> outFaces) { while (inNode != null) { // if we are not destroyed if ((inNode.flags & BspNode.BspFlags_IsDestroyed) == 0) { // add to array list outFaces.Add(inNode.face); } if (inNode.front != null) { GetFaces(inNode.front, outFaces); } if (inNode.back != null) { GetFaces(inNode.back, outFaces); } // get to the next planar node inNode = inNode.planar; } }
public static void DumpTree(BspNode inNode) { Debug.Log("Node..."); Debug.Log("Planar Nodes"); BspNode planar = inNode.planar; while (planar != null) { Debug.Log("Planar Node"); planar = planar.planar; } if (inNode.front != null) { Debug.Log("FrontSide"); DumpTree(inNode.front); } if (inNode.back != null) { Debug.Log("BackSide"); DumpTree(inNode.back); } }
// private void PerformFaces(BspNode inRoot, List <Face> inFaces) { for (int i = 0; i < inFaces.Count; ++i) { OperationInfo info; InitializeOper(out info); PerformNode(inRoot, inFaces[i] as Face, SIDE_Outside, info); } }
void RouteOper(BspNode inNode, Face inFace, EPolySide inSide, OperationInfo info) { if (processState == EProcessState.Process_Master) { csgVisitor.ProcessMaster(this, inFace, inSide, info); } else { csgVisitor.ProcessSlave(this, inFace, inSide, info); } }
private void PerformTree(BspNode inNode, BspNode inOtherRoot) { while (inNode != null) { // if we are new, stop processing our node... if ((inNode.flags & BspNode.BspFlags_IsNew) != 0) { return; } // if we are not destroyed if ((inNode.flags & BspNode.BspFlags_IsDestroyed) == 0) { OperationInfo info; InitializeOper(out info); // save current node currentNode = inNode; // find last coplanar node BspNode lastPlanar = inNode; while (lastPlanar.planar != null) { lastPlanar = lastPlanar.planar; } // perform nodes face with other tree PerformNode(inOtherRoot, inNode.face, SIDE_Outside, info); // nobody removed us if ((inNode.flags & BspNode.BspFlags_IsDestroyed) == 0) { // remove all planar nodes added (if any) lastPlanar.planar = null; } } // process front node if (inNode.front != null) { PerformTree(inNode.front, inOtherRoot); } // process back node if (inNode.back != null) { PerformTree(inNode.back, inOtherRoot); } // process next planar node inNode = inNode.planar; } }
// public void Perform(ECsgOperation inOper, CSGObject inMaster, CSGObject inSlave) { // we are processing our slave faces processState = EProcessState.Process_Slave; // process faces against master tree PerformFaces(inMaster.rootNode, inSlave.faces); // process face from master tree processState = EProcessState.Process_Master; // perform master faces on slave bsp tree PerformTree(inMaster.rootNode, inSlave.rootNode); // check if how do we need to process generated faces if (inOper == ECsgOperation.CsgOper_Additive || inOper == ECsgOperation.CsgOper_Subtractive) { // add deferred faces to master tree... for (int i = 0; i < deferredFaces.Count; i++) { Face defFace = ((DeferredFace)deferredFaces[i]).face; BspNode startNode = ((DeferredFace)deferredFaces[i]).node; // testing startNode = inMaster.rootNode; // add node to master tree BspGen.AddNodeRecursive(startNode, defFace, BspNode.BspFlags_IsNew); } } else { // clear old faces list inMaster.faces.Clear(); // copy created faces for (int i = 0; i < deferredFaces.Count; i++) { inMaster.faces.Add(deferredFaces[i].face); } } // clear deferred faces deferredFaces.Clear(); }
public static void FacesFromNodes(BspNode inNode, List <Face> outFaces) { while (inNode != null) { if ((inNode.flags & BspNode.BspFlags_IsDestroyed) == 0) { outFaces.Add(inNode.face); } if (inNode.front != null) { FacesFromNodes(inNode.front, outFaces); } if (inNode.back != null) { FacesFromNodes(inNode.back, outFaces); } // inNode = inNode.planar; } }
// public BspNode GenerateBspTree(List <Face> inFaces) { if (inFaces == null) { Debug.Log("Not able to create Tree with no Faces"); return(null); } Debug.Log("Building Bsp Tree for " + inFaces.Count + " Faces"); // create root node BspNode root = new BspNode(); // partition all faces Partition(root, inFaces); // copy bsp faces back to array list (resulting faces...) GetFaces(root, inFaces); Debug.Log("Resulting in " + inFaces.Count + " Faces"); // result return(root); }
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); } }
public static BspNode AddNodeRecursive(BspNode inNode, Face inFace, int inFlags) { while (inNode != null) { Face.EPlaneSide planeSide = inFace.Side(inNode.plane); switch (planeSide) { case Face.EPlaneSide.Side_Front: if (inNode.front == null) { return(AddNode(inNode, BspNode.EBspLocation.BspLocation_Front, inFace, inFlags)); } inNode = inNode.front; break; case Face.EPlaneSide.Side_Back: if (inNode.back == null) { return(AddNode(inNode, BspNode.EBspLocation.BspLocation_Back, inFace, inFlags)); } inNode = inNode.back; break; case Face.EPlaneSide.Side_Planar: return(AddNode(inNode, BspNode.EBspLocation.BspLocation_Planar, inFace, inFlags)); case Face.EPlaneSide.Side_Split: Face frontFace, backFace; inFace.Split(inNode.plane, out frontFace, out backFace); if (inNode.front == null) { AddNode(inNode, BspNode.EBspLocation.BspLocation_Front, frontFace, inFlags); } else { AddNodeRecursive(inNode.front, frontFace, inFlags); } if (inNode.back == null) { AddNode(inNode, BspNode.EBspLocation.BspLocation_Back, inFace, inFlags); } else { AddNodeRecursive(inNode.back, backFace, inFlags); } inNode = null; break; } } // happens when face get splitted... return(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; } } }
/// <summary> /// Performs CSG Operation on this Object (Master) with given Slaves. /// </summary> /// <param name='inOper'> /// In oper. /// </param> /// <param name='inSlaves'> /// In slaves. /// </param> public void PerformCSG(CsgOperation.ECsgOperation inOper, CSGObject[] inSlaves) { // CreateFromMesh(); // create bsp generator BspGen gen = new BspGen(BooleanSettings.BspOptimization); rootNode = gen.GenerateBspTree(faces); List <Face> savedFaces = new List <Face>(); // foreach (var slave in inSlaves) { // if we have a csg object and we are not our self // and intersecting if (slave && slave.gameObject != gameObject && intersect(slave)) { Debug.Log(slave.gameObject.name); // slave.CreateFromMesh(); // .... BspGen genSlave = new BspGen(BooleanSettings.BspOptimization); slave.rootNode = genSlave.GenerateBspTree(slave.faces); CsgVisitor visitor = null; switch (inOper) { case CsgOperation.ECsgOperation.CsgOper_Additive: visitor = new UnionVisitor(); break; case CsgOperation.ECsgOperation.CsgOper_Subtractive: visitor = new SubtractiveVisitor(); break; case CsgOperation.ECsgOperation.CsgOper_Intersect: visitor = new IntersectVisitor(); break; case CsgOperation.ECsgOperation.CsgOper_DeIntersect: visitor = new DeIntersectVisitor(); break; default: visitor = null; break; } CsgOperation oper = new CsgOperation(visitor); oper.Perform(inOper, this, slave); // save faces savedFaces.AddRange(faces); } } // If we want to merge Coplanars after every Operation if (BooleanSettings.MergeCoplanars) { MergeFaces(); } // for additive or subtracte operation, built faces list from bsp tree // for others, use faces directly if (inOper == CsgOperation.ECsgOperation.CsgOper_Additive || inOper == CsgOperation.ECsgOperation.CsgOper_Subtractive) { // create new face list List <Face> newFaces = new List <Face>(); // create faces from bsp nodes BspHelper.FacesFromNodes(rootNode, newFaces); // copy to face list faces = newFaces; } else { // copy saved faces faces = savedFaces; } // copy to unity structure TransferFacesToMesh(); }