int16_t plCullTree::IMakeHoleSubTree(const plCullPoly& poly) const { if( fCapturePolys ) IVisPoly(poly, true); int firstNode = fNodeList.GetCount(); int16_t iNode = -1; int i; for( i = 0; i < poly.fVerts.GetCount()-1; i++ ) { if( !poly.fClipped.IsBitSet(i) ) { int16_t child = IMakePolyNode(poly, i, i+1); if( iNode >= 0 ) IGetNode(iNode)->fOuterChild = child; iNode = child; } } if( !poly.fClipped.IsBitSet(i) ) { int16_t child = IMakePolyNode(poly, i, 0); if( iNode >= 0 ) IGetNode(iNode)->fOuterChild = child; iNode = child; } plCullNode* child = fNodeList.Push(); child->Init(this, poly.fNorm, poly.fDist); if( iNode >= 0 ) IGetNode(iNode)->fOuterChild = fNodeList.GetCount()-1; return firstNode; }
void plCullNode::ISetPointersRecur() const { if( fInnerPtr = IGetNode(fInnerChild) ) fInnerPtr->ISetPointersRecur(); if( fOuterPtr = IGetNode(fOuterChild) ) fOuterPtr->ISetPointersRecur(); }
void plAnimCompProc::IUpdateNodeButton(HWND hWnd, IParamBlock2* pb) { HWND hButton = GetDlgItem(hWnd, fNodeButtonID); plComponentBase* comp = IGetComp(pb); if (!comp) { SetWindowText(hButton, "(none)"); EnableWindow(hButton, FALSE); return; } // If this is an anim grouped component you can't pick a target if (comp->ClassID() == ANIM_GROUP_COMP_CID) { IClearNode(pb); SetWindowText(hButton, "(none)"); EnableWindow(hButton, FALSE); return; } EnableWindow(hButton, TRUE); // Make sure the node is actually in the components target list plMaxNode* node = IGetNode(pb); if (comp->IsTarget((plMaxNodeBase*)node)) SetWindowText(hButton, node->GetName()); else SetWindowText(hButton, "(none)"); }
void plCullTree::AddPoly(const plCullPoly& poly) { const plCullPoly* usePoly = &poly; hsVector3 cenToEye(&fViewPos, &poly.fCenter); hsFastMath::NormalizeAppr(cenToEye); float camDist = cenToEye.InnerProduct(poly.fNorm); plConst(float) kTol(0.1f); hsBool backFace = camDist < -kTol; if( !backFace && (camDist < kTol) ) return; plCullPoly scratchPoly; if( poly.IsHole() ) { if( !backFace ) return; } else if( backFace ) { plConst(hsBool) kAllowTwoSided(true); if( !kAllowTwoSided || !poly.IsTwoSided() ) return; scratchPoly.Flip(poly); usePoly = &scratchPoly; } if( !SphereVisible(usePoly->GetCenter(), usePoly->GetRadius()) ) return; usePoly->fClipped.Clear(); usePoly->Validate(); // Make sure we have enough scratch polys. Each node // can potentially split this poly, so... ISetupScratch(fNodeList.GetCount()); #if 1 if( IGetRoot() && IGetNode(IGetRoot()->fOuterChild) ) { IAddPolyRecur(*usePoly, IGetRoot()->fOuterChild); } else #endif { fRoot = IAddPolyRecur(*usePoly, fRoot); } #ifdef DEBUG_POINTERS if( IGetRoot() ) IGetRoot()->ISetPointersRecur(); #endif // DEBUG_POINTERS }
bool plAnimCompProc::GetCompAndNode(IParamBlock2* pb, plComponentBase*& comp, plMaxNode*& node) { comp = IGetComp(pb); if (comp) { node = IGetNode(pb); // If it's an anim group component (don't need a node), or we have a node // and the component is attached to it, we're ok. if (comp->ClassID() == ANIM_GROUP_COMP_CID || (node && comp->IsTarget((plMaxNodeBase*)node))) return true; } return false; }
////////////////////////////////////////////////////////////////////// // Harvest culling section. // These are the functions used on a built tree ////////////////////////////////////////////////////////////////////// plCullNode::plCullStatus plCullNode::ITestBoundsRecur(const hsBounds3Ext& bnd) const { plCullNode::plCullStatus retVal = TestBounds(bnd); // No Children, what we say goes. if( (fOuterChild < 0) && (fInnerChild < 0) ) return retVal; // No innerchild. If we cull, it's culled, else we // hope our outerchild culls it. if( fInnerChild < 0 ) { if( retVal == kCulled ) return kCulled; return IGetNode(fOuterChild)->ITestBoundsRecur(bnd); } // No outerchild. If we say it's clear, it's clear (or split), but if // it's culled, we have to pass it to innerchild, who may pronounce it clear if( fOuterChild < 0 ) { if( retVal == kClear ) return kClear; if( retVal == kSplit ) return kSplit; return IGetNode(fInnerChild)->ITestBoundsRecur(bnd); } // We've got both children to feed. // We pass the clear ones to the inner child, culled to outer, // and split to both. Remember, a both children have to agree to cull a split. if( retVal == kClear ) return IGetNode(fOuterChild)->ITestBoundsRecur(bnd); if( retVal == kCulled ) return IGetNode(fInnerChild)->ITestBoundsRecur(bnd); // Here's the split, to be culled, both children have to // say its culled. if( kCulled != IGetNode(fOuterChild)->ITestBoundsRecur(bnd) ) return kSplit; if( kCulled != IGetNode(fInnerChild)->ITestBoundsRecur(bnd) ) return kSplit; return kCulled; }
int16_t plCullTree::IAddPolyRecur(const plCullPoly& poly, int16_t iNode) { if( poly.fVerts.GetCount() < 3 ) return iNode; if( iNode < 0 ) return IMakePolySubTree(poly); hsBool addInner = (IGetNode(iNode)->fInnerChild >= 0) || ((iNode > 5) && poly.IsHole()); hsBool addOuter = !poly.IsHole() || (IGetNode(iNode)->fOuterChild >= 0); plCullPoly* innerPoly = nil; plCullPoly* outerPoly = nil; plCullNode::plCullStatus test = IGetNode(iNode)->ISplitPoly(poly, innerPoly, outerPoly); switch( test ) { case plCullNode::kClear: if( addOuter ) { int child = IAddPolyRecur(poly, IGetNode(iNode)->fOuterChild); IGetNode(iNode)->fOuterChild = child; } break; case plCullNode::kCulled: if( addInner ) { int child = IAddPolyRecur(poly, IGetNode(iNode)->fInnerChild); IGetNode(iNode)->fInnerChild = child; } break; case plCullNode::kSplit: hsAssert(innerPoly && outerPoly, "Poly should have been split into inner and outer in SplitPoly"); if( addOuter ) { int child = IAddPolyRecur(*outerPoly, IGetNode(iNode)->fOuterChild); IGetNode(iNode)->fOuterChild = child; } if( addInner ) { int child = IAddPolyRecur(*innerPoly, IGetNode(iNode)->fInnerChild); IGetNode(iNode)->fInnerChild = child; } break; } return iNode; }
// Cycle through the Cull Nodes, paring down the list of who to test (through ITestNode above). // We reclaim the scratch indices in clear and split when we're done (SetCount(0)), but we can't // reclaim the culled, because our caller may be looking at who all we culled. See below in split. // If a node is disabled, we can just ignore we ever got called. void plCullNode::ITestNode(const plSpaceTree* space, int16_t who, hsBitVector& totList, hsBitVector& outList) const { if( space->IsDisabled(who) ) return; uint32_t myClearStart = ScratchClear().GetCount(); uint32_t mySplitStart = ScratchSplit().GetCount(); uint32_t myCullStart = ScratchCulled().GetCount(); if( kPureSplit == ITestNode(space, who, ScratchClear(), ScratchSplit(), ScratchCulled()) ) ScratchSplit().Append(who); uint32_t myClearEnd = ScratchClear().GetCount(); uint32_t mySplitEnd = ScratchSplit().GetCount(); uint32_t myCullEnd = ScratchCulled().GetCount(); int i; // If there's no OuterChild, everything in clear and split is visible. Everything in culled // goes to innerchild (if any). if( fOuterChild < 0 ) { plProfile_IncCount(HarvestNodes, myClearEnd - myClearStart + mySplitEnd - mySplitStart); // Replace these with a memcopy or something!!!! for( i = myClearStart; i < myClearEnd; i++ ) { space->HarvestLeaves(ScratchClear()[i], totList, outList); } for( i = mySplitStart; i < mySplitEnd; i++ ) { space->HarvestLeaves(ScratchSplit()[i], totList, outList); } if( fInnerChild >= 0 ) { for( i = myCullStart; i < myCullEnd; i++ ) { IGetNode(fInnerChild)->ITestNode(space, ScratchCulled()[i], totList, outList); } } ScratchClear().SetCount(myClearStart); ScratchSplit().SetCount(mySplitStart); ScratchCulled().SetCount(myCullStart); return; } // There is an OuterChild, so whether there's an InnerChild or not, // everything in ClearList is visible soley on the discretion of OuterChild. for( i = myClearStart; i < myClearEnd; i++ ) { IGetNode(fOuterChild)->ITestNode(space, ScratchClear()[i], totList, outList); } // If there's no InnerChild, then the SplitList is also visible soley // on the discretion of OuterChild. if( fInnerChild < 0 ) { for( i = mySplitStart; i < mySplitEnd; i++ ) { IGetNode(fOuterChild)->ITestNode(space, ScratchSplit()[i], totList, outList); } ScratchClear().SetCount(myClearStart); ScratchSplit().SetCount(mySplitStart); ScratchCulled().SetCount(myCullStart); return; } // There is an inner child. Everything in culled list is visible // soley on its discretion. for( i = myCullStart; i < myCullEnd; i++ ) { IGetNode(fInnerChild)->ITestNode(space, ScratchCulled()[i], totList, outList); } // Okay, here's the rub. // Everyone in the split list needs to be tested against InnerChild and OuterChild. // If either child says it's okay (puts it in OutList), then it's okay. // The problem is that if both children say it's okay, it will wind up in outList twice. // This is complicated by the fact that outList is still subTrees at this point, // so InnerChild adding a subTree and OuterChild adding a child of that subTree isn't // even appending the same value to the list. // Sooooo. // What we do is keep track of every node (interior and leaf) that gets harvested. // When we go to harvest a subtree, we check in totList for its bit being set. Bits // set in totList are ENTIRE SUBTREE IS HARVESTED. SpaceTree understands this too in // its HarvestLeaves. Seems obvious now, but I didn't hear you suggest it. for( i = mySplitStart; i < mySplitEnd; i++ ) { IGetNode(fOuterChild)->ITestNode(space, ScratchSplit()[i], totList, outList); } for( i = mySplitStart; i < mySplitEnd; i++ ) { if( !totList.IsBitSet(ScratchSplit()[i]) ) IGetNode(fInnerChild)->ITestNode(space, ScratchSplit()[i], totList, outList); } ScratchClear().SetCount(myClearStart); ScratchSplit().SetCount(mySplitStart); ScratchCulled().SetCount(myCullStart); }