void PopulateOctree(const WorldPoint& p0, const WorldPoint& p1, const Iter& begin, const Iter& end, bool canKeepAll = false) { m_p0 = p0; m_p1 = p1; // Only include those elements that have a portion in this tree. for(Iter iter = begin; iter != end; ++iter) { WorldPoint min = (*iter)->GetMinExtent(); WorldPoint max = (*iter)->GetMaxExtent(); if( BoxesIntersect(min, max, m_p0, m_p1) ) { m_objects.push_back(*iter); } } // If any child has the same number of objects as this level, then // we'll hit an infinite loop. if( !canKeepAll && this->GetNumObjects() == std::distance(begin, end) ) { return; } if( m_objects.size() > m_minObjects ) { PopulateSubTrees(); } }
//----------------------------------------------------------------------------- // Purpose: // Input : pObject - //----------------------------------------------------------------------------- void CCullTreeNode::AddCullTreeObjectRecurse(CMapClass *pObject) { // // If the object intersects this node, add it to this node and recurse, // testing each of our children in the same fashion. // Vector ObjMins; Vector ObjMaxs; pObject->GetCullBox(ObjMins, ObjMaxs); if (BoxesIntersect(ObjMins, ObjMaxs, bmins, bmaxs)) { int nChildCount = GetChildCount(); if (nChildCount != 0) { // dvs: we should split when appropriate! // otherwise the tree becomes less optimal over time. for (int nChild = 0; nChild < nChildCount; nChild++) { CCullTreeNode *pChild = GetCullTreeChild(nChild); pChild->AddCullTreeObjectRecurse(pObject); } } else { AddCullTreeObject(pObject); } } }
//----------------------------------------------------------------------------- // Purpose: // Input : pObject - The object whose bounding box has changed. //----------------------------------------------------------------------------- void CCullTreeNode::UpdateAllCullTreeObjectsRecurse(void) { int nChildCount = GetChildCount(); if (nChildCount != 0) { for (int nChild = 0; nChild < nChildCount; nChild++) { CCullTreeNode *pChild = GetCullTreeChild(nChild); pChild->UpdateAllCullTreeObjectsRecurse(); } } else { int nObjectCount = GetObjectCount(); for (int nObject = 0; nObject < nObjectCount; nObject++) { CMapClass *pObject = GetCullTreeObject(nObject); Vector mins; Vector maxs; pObject->GetCullBox(mins, maxs); if (!BoxesIntersect(mins, maxs, bmins, bmaxs)) { RemoveCullTreeObject(pObject); } } } }
//----------------------------------------------------------------------------- // Purpose: // Input : pObject - //----------------------------------------------------------------------------- void CCullTreeNode::UpdateCullTreeObject(CMapClass *pObject) { Vector mins; Vector maxs; pObject->GetCullBox(mins, maxs); if (!BoxesIntersect(mins, maxs, bmins, bmaxs)) { RemoveCullTreeObject(pObject); } else { AddCullTreeObject(pObject); } }
void CMapWorld::CullTree_SplitNode(CCullTreeNode *pNode) { Vector Mins; Vector Maxs; Vector Size; pNode->GetBounds(Mins, Maxs); VectorSubtract(Maxs, Mins, Size); if ((Size[0] > MIN_NODE_DIM) && (Size[1] > MIN_NODE_DIM) && (Size[2] > MIN_NODE_DIM)) { Vector Mids; int nChild; Mids[0] = (Mins[0] + Maxs[0]) / 2.0; Mids[1] = (Mins[1] + Maxs[1]) / 2.0; Mids[2] = (Mins[2] + Maxs[2]) / 2.0; for (nChild = 0; nChild < 8; nChild++) { Vector ChildMins; Vector ChildMaxs; // // Create a child and set its bounding box. // CCullTreeNode *pChild = new CCullTreeNode; if (nChild & 1) { ChildMins[0] = Mins[0]; ChildMaxs[0] = Mids[0]; } else { ChildMins[0] = Mids[0]; ChildMaxs[0] = Maxs[0]; } if (nChild & 2) { ChildMins[1] = Mins[1]; ChildMaxs[1] = Mids[1]; } else { ChildMins[1] = Mids[1]; ChildMaxs[1] = Maxs[1]; } if (nChild & 4) { ChildMins[2] = Mins[2]; ChildMaxs[2] = Mids[2]; } else { ChildMins[2] = Mids[2]; ChildMaxs[2] = Maxs[2]; } pChild->UpdateBounds(ChildMins, ChildMaxs); pNode->AddCullTreeChild(pChild); Vector mins1; Vector maxs1; pChild->GetBounds(mins1, maxs1); // // Check all objects in this node against the child's bounding box, adding the // objects that intersect to the child's object list. // int nObjectCount = pNode->GetObjectCount(); for (int nObject = 0; nObject < nObjectCount; nObject++) { CMapClass *pObject = pNode->GetCullTreeObject(nObject); Assert(pObject != NULL); Vector mins2; Vector maxs2; pObject->GetCullBox(mins2, maxs2); if (BoxesIntersect(mins1, maxs1, mins2, maxs2)) { pChild->AddCullTreeObject(pObject); } } } // // Remove all objects from this node's object list (since we are not a leaf). // pNode->RemoveAllCullTreeObjects(); // // Recurse into all children with at least two objects, splitting them. // int nChildCount = pNode->GetChildCount(); for (nChild = 0; nChild < nChildCount; nChild++) { CCullTreeNode *pChild = pNode->GetCullTreeChild(nChild); if (pChild->GetObjectCount() >= MIN_NODE_OBJECT_SPLIT) { CullTree_SplitNode(pChild); } } } }
int main(int argc, char* argv[]) { const int NUM_OBJECTS = 40; // Number of objects in test set, must be > FRAC_OBJECTS for this test const int FRAC_OBJECTS = 4; const float MAX_WORLDSIZE = 10.0f; const float FRAC_WORLDSIZE = MAX_WORLDSIZE / 2; // typedef the RTree useage just for conveniance with iteration typedef RTree<SomeThing*, float, 3> SomeThingTree; ASSERT( NUM_OBJECTS > FRAC_OBJECTS ); int index; // general purpose counter, declared here because MSVC 6 is not ansi compliant with 'for' loops. SomeThing* thingArray[NUM_OBJECTS * 2]; // Store objects in another container to test with, sized larger than we need memset(thingArray, 0, sizeof(thingArray)); // Nullify array, size is known here // Create intance of RTree SomeThingTree tree; // Add some nodes int counter = 0; for( index = 0; index < NUM_OBJECTS; ++index ) { SomeThing* newThing = new SomeThing; newThing->m_creationCounter = counter++; newThing->m_min = Vec3(RandFloat(-MAX_WORLDSIZE, MAX_WORLDSIZE), RandFloat(-MAX_WORLDSIZE, MAX_WORLDSIZE), RandFloat(-MAX_WORLDSIZE, MAX_WORLDSIZE)); Vec3 extent = Vec3(RandFloat(0, FRAC_WORLDSIZE), RandFloat(0, FRAC_WORLDSIZE), RandFloat(0, FRAC_WORLDSIZE)); newThing->m_max = newThing->m_min + extent; thingArray[counter-1] = newThing; tree.Insert(newThing->m_min.v, newThing->m_max.v, newThing); printf("inserting %d\n", newThing->m_creationCounter); } printf("tree count = %d\n", tree.Count()); int numToDelete = NUM_OBJECTS / FRAC_OBJECTS; int numToStep = FRAC_OBJECTS; // Delete some nodes for( index = 0; index < NUM_OBJECTS; index += numToStep ) { SomeThing* curThing = thingArray[index]; if(curThing) { tree.Remove(curThing->m_min.v, curThing->m_max.v, curThing); printf("removing %d\n", curThing->m_creationCounter); delete curThing; thingArray[index] = NULL; } } printf("tree count = %d\n", tree.Count()); // Add some more nodes for( index = 0; index < numToDelete; ++index ) { SomeThing* newThing = new SomeThing; newThing->m_creationCounter = counter++; newThing->m_min = Vec3(RandFloat(-MAX_WORLDSIZE, MAX_WORLDSIZE), RandFloat(-MAX_WORLDSIZE, MAX_WORLDSIZE), RandFloat(-MAX_WORLDSIZE, MAX_WORLDSIZE)); Vec3 extent = Vec3(RandFloat(0, FRAC_WORLDSIZE), RandFloat(0, FRAC_WORLDSIZE), RandFloat(0, FRAC_WORLDSIZE)); newThing->m_max = newThing->m_min + extent; thingArray[counter-1] = newThing; tree.Insert(newThing->m_min.v, newThing->m_max.v, newThing); printf("inserting %d\n", newThing->m_creationCounter); } printf("tree count = %d\n", tree.Count()); Vec3 searchMin(0,0,0); Vec3 searchMax(FRAC_WORLDSIZE, FRAC_WORLDSIZE, FRAC_WORLDSIZE); tree.Search(searchMin.v, searchMax.v, &QueryResultCallback, NULL); // NOTE: Even better than just dumping text, it would be nice to render the // tree contents and search result for visualization. // List values. Iterator is NOT delete safe SomeThingTree::Iterator it; for( tree.GetFirst(it); !tree.IsNull(it); tree.GetNext(it) ) { SomeThing* curThing = tree.GetAt(it); if(BoxesIntersect(searchMin, searchMax, curThing->m_min, curThing->m_max)) { printf("brute found %d\n", curThing->m_creationCounter); } } // Delete our nodes, NOTE, we are NOT deleting the tree nodes, just our data // of course the tree will now contain invalid pointers that must not be used any more. for( tree.GetFirst(it); !tree.IsNull(it); tree.GetNext(it) ) { SomeThing* removeElem = tree.GetAt(it); if(removeElem) { printf("deleting %d\n", removeElem->m_creationCounter); delete removeElem; } } // Remove all contents (This would have happened automatically during destructor) tree.RemoveAll(); if(SomeThing::s_outstandingAllocs > 0) { printf("Memory leak!\n"); printf("s_outstandingAllocs = %d\n", SomeThing::s_outstandingAllocs); } else { printf("No memory leaks detected by app\n"); } // Wait for keypress on exit so we can read console output getchar(); #ifdef WIN32 // Use CRT Debug facility to dump memory leaks on app exit SET_CRT_DEBUG_FIELD( _CRTDBG_LEAK_CHECK_DF ); #endif //WIN32 return 0; }
void CTriggerFX::CheckPlayersWithinTrigger() { if( m_cs.bLocked ) return; // Get a list of all the characters... CSpecialFXList *pList = g_pGameClientShell->GetSFXMgr()->GetFXList( SFX_CHARACTER_ID ); if( !pList ) return; int nListSize = pList->GetSize(); int nNumChars = pList->GetNumItems(); int nNumFoundChars = 0; int nNumPlayersFound = 0; uint32 dwLocalId = 0; g_pLTClient->GetLocalClientID( &dwLocalId ); LTVector vTrigPos, vPlayerPos, vPlayerDims, vPlayerMin, vPlayerMax; g_pLTClient->GetObjectPos( m_hServerObject, &vTrigPos ); // Setup the triggers box... LTVector vTrigMin = vTrigPos - m_cs.vDims; LTVector vTrigMax = vTrigPos + m_cs.vDims; bool bLocalPlayerIn = false; // Initialize our containers to zero. Don't call clear, since we'll be using // these vectors every frame and most likely they will have the same // number of elements across multiple frames. m_lstPlayersNotInTrigger.resize( 0 ); m_lstNewPlayersInTrigger.resize( 0 ); for( int i = 0; i < nListSize; ++i ) { // Try not to go through the entire list... if( nNumFoundChars == nNumChars ) break; if( (*pList)[i] ) { CCharacterFX *pChar = (CCharacterFX*)(*pList)[i]; if( !pChar ) continue; // Found another char.. ++nNumFoundChars; if( pChar->m_cs.bIsPlayer && pChar->m_cs.nClientID != ( uint8 )-1 ) { ++nNumPlayersFound; HOBJECT hPlayer = pChar->GetServerObj(); g_pLTClient->GetObjectPos( hPlayer, &vPlayerPos ); g_pPhysicsLT->GetObjectDims( hPlayer, &vPlayerDims ); vPlayerMin = vPlayerPos - vPlayerDims; vPlayerMax = vPlayerPos + vPlayerDims; // Check the current list of players in the trigger for this player... CharFXList::iterator iter; for( iter = m_lstCurPlayersInTrigger.begin(); iter != m_lstCurPlayersInTrigger.end(); ++iter ) { if( pChar == (*iter) ) break; } // Check if we are within the height of the trigger... bool bWithinHeight = false; if( vPlayerMax.y > vTrigMin.y && vPlayerMin.y < vTrigMax.y ) bWithinHeight = true; if( bWithinHeight && BoxesIntersect( vTrigMin, vTrigMax, vPlayerMin, vPlayerMax ) && !pChar->IsPlayerDead()) { if( dwLocalId == pChar->m_cs.nClientID ) bLocalPlayerIn = true; // If it wasn't in the list add it... if( iter == m_lstCurPlayersInTrigger.end() ) { m_lstCurPlayersInTrigger.push_back( pChar ); m_lstNewPlayersInTrigger.push_back( pChar ); } } else { if( iter != m_lstCurPlayersInTrigger.end() ) m_lstCurPlayersInTrigger.erase( iter ); m_lstPlayersNotInTrigger.push_back( pChar ); } } } } wchar_t wszBuffer[256]; if( (m_lstNewPlayersInTrigger.size() > 0) && (nNumPlayersFound > 1) ) { CClientInfoMgr *pInfoMgr = g_pInterfaceMgr->GetClientInfoMgr(); if( !pInfoMgr ) return; if( bLocalPlayerIn ) { // Display a general transmission and messages for each player you are waiting for... int nPlayersNotInTrig = m_lstPlayersNotInTrigger.size(); if( m_cs.nPlayerInsideID != (uint32)-1 ) { g_pTransmission->Show( StringIDFromIndex(m_cs.nPlayerInsideID) ); } else if( nPlayersNotInTrig > 1 ) { //sTransmission.Format( "You are waiting for %i players.", nPlayersNotInTrig ); FormatString( "IDS_EXIT_PLAYER_WAITING", wszBuffer, LTARRAYSIZE(wszBuffer), nPlayersNotInTrig ); g_pTransmission->Show( wszBuffer ); } else { //sTransmission.Format( "You are waiting for 1 player." ); FormatString( "IDS_EXIT_PLAYER_WAITING_1", wszBuffer, LTARRAYSIZE(wszBuffer) ); g_pTransmission->Show( wszBuffer ); } CharFXList::iterator iter; for( iter = m_lstPlayersNotInTrigger.begin(); iter != m_lstPlayersNotInTrigger.end(); ++iter ) { //sMessage.Format( "You are waiting for %s.", pInfoMgr->GetPlayerName( (*iter)->m_cs.nClientID )); FormatString( "IDS_EXIT_PLAYER_WAITING_NAME", wszBuffer, LTARRAYSIZE(wszBuffer), pInfoMgr->GetPlayerName( (*iter)->m_cs.nClientID) ); g_pGameMsgs->AddMessage( wszBuffer ); } } else { // Display a general transmission and messages for each player waiting for you... int nPlayersInTrig = m_lstCurPlayersInTrigger.size(); if( m_cs.nPlayerOutsideID != (uint32)-1 ) { g_pTransmission->Show( LoadString(m_cs.nPlayerOutsideID) ); } else if( nPlayersInTrig > 1 ) { // sTransmission.Format( "%i players are waiting for you",nPlayersInTrig ); FormatString( "IDS_EXIT_WAITING", wszBuffer, LTARRAYSIZE(wszBuffer), nPlayersInTrig ); g_pTransmission->Show( wszBuffer ); } else { // sTransmission.Format( "1 player is waiting for you." ); FormatString( "IDS_EXIT_WAITING_1", wszBuffer, LTARRAYSIZE(wszBuffer) ); g_pTransmission->Show( wszBuffer ); } CharFXList::iterator iter; for( iter = m_lstCurPlayersInTrigger.begin(); iter != m_lstCurPlayersInTrigger.end(); ++iter ) { FormatString( "IDS_EXIT_WAITING_NAME", wszBuffer, LTARRAYSIZE(wszBuffer), pInfoMgr->GetPlayerName( (*iter)->m_cs.nClientID) ); g_pGameMsgs->AddMessage( wszBuffer ); } } } }
void CTriggerFX::CalcLocalClientDistance() { m_fDistPercent = -1.0f; // Don't do anything if the trigger is locked or our distances are too small.. if( m_cs.bLocked || (m_cs.fHUDAlwaysOnDist <= 0.0f && m_cs.fHUDLookAtDist <= 0.0f) ) return; // See if the player is within the trigger... LTVector vTrigPos; g_pLTClient->GetObjectPos( m_hServerObject, &vTrigPos ); g_pLTClient->SetObjectPos( m_hDimsObject, vTrigPos ); HLOCALOBJ hPlayerObj = g_pLTClient->GetClientObject(); LTVector vPlayerPos, vPlayerDims; g_pLTClient->GetObjectPos( hPlayerObj, &vPlayerPos ); // Make sure we are within the display radius... float fMaxRadius = LTMAX( m_cs.fHUDAlwaysOnDist, m_cs.fHUDLookAtDist ); float fDistSqr = vTrigPos.DistSqr( vPlayerPos ); bool bWithinLookAtDist = (fDistSqr < m_cs.fHUDLookAtDist * m_cs.fHUDLookAtDist); bool bWithinAlwaysOnDist = (fDistSqr < m_cs.fHUDAlwaysOnDist * m_cs.fHUDAlwaysOnDist); if( !bWithinLookAtDist && !bWithinAlwaysOnDist ) { // We are not close enough... m_bWithinIndicatorRadius = false; return; } m_bWithinIndicatorRadius = true; g_pPhysicsLT->GetObjectDims( hPlayerObj, &vPlayerDims ); LTVector vTrigMin = vTrigPos - m_cs.vDims; LTVector vTrigMax = vTrigPos + m_cs.vDims; LTVector vPlayerMin = vPlayerPos - vPlayerDims; LTVector vPlayerMax = vPlayerPos + vPlayerDims; // Check if we are within the height of the trigger... bool bWithinHeight =false; if( vPlayerMax.y > vTrigMin.y && vPlayerMin.y < vTrigMax.y ) bWithinHeight = true; // See if we are inside the trigger at all... if( bWithinHeight && (BoxesIntersect( vTrigMin, vTrigMax, vPlayerMin, vPlayerMax ) || bWithinAlwaysOnDist)) { m_fDistPercent = 1.0f; } else { // We are within the height of the trigger, show how far from it we are... float fMinDist = (vPlayerDims.x + vPlayerDims.z) * 0.5f; float fMaxDist = 100000.0f; LTVector vDir; if( bWithinAlwaysOnDist ) { vDir = vTrigPos - vPlayerPos; vDir.Normalize(); } else { LTRotation const& rRot = g_pPlayerMgr->GetPlayerCamera()->GetCameraRotation( ); vDir = rRot.Forward(); } IntersectQuery IQuery; IntersectInfo IInfo; IQuery.m_From = vPlayerPos + (vDir * fMinDist); IQuery.m_To = IQuery.m_From + (vDir * fMaxDist); IQuery.m_Flags = INTERSECT_OBJECTS | INTERSECT_HPOLY | IGNORE_NONSOLID; // We need to recieve rayhits for this intersect call... g_pCommonLT->SetObjectFlags( m_hDimsObject, OFT_Flags, FLAG_RAYHIT, FLAG_RAYHIT ); if( g_pLTClient->IntersectSegment( IQuery, &IInfo )) { if( IInfo.m_hObject == m_hDimsObject ) { IInfo.m_Point.y = vPlayerPos.y; float fDist = vPlayerPos.Dist( IInfo.m_Point ); m_fDistPercent = 1.0f - (fDist / fMaxRadius); } } // No more rayhits... g_pCommonLT->SetObjectFlags( m_hDimsObject, OFT_Flags, 0, FLAG_RAYHIT ); } }
void Execute(scriptID id, ScriptStack& scriptState) { Script script = scripts[id]; Script::const_iterator ins = script.begin(); Script::const_iterator end = script.end(); for (; ins != end; ++ins) { switch(ins->GetCode()) { ///////////////////////////////////////////////////////////////////////////// case op_noop: break; case op_push: { const unsigned char* data = ins->GetData(); ptr size = *(ptr*)data; // SIZE MISMATCH, fix it ppeas Phil unsigned char* value = (unsigned char*)DataAtOffset(data, 1); scriptState.push(value, size); } break; ///////////////////////////////////////////////////////////////////////////// case op_end: return; break; ///////////////////////////////////////////////////////////////////////////// case op_forceout: { // figure out our overlap and how much we have. const unsigned char* data = ins->GetData(); Actor* target = GetActor(scriptState.get(*data)); Actor* source = GetActor( scriptState.get(*DataAtOffset(data, 1)) ); const Box* sourceBox = source->GetCollision(); const Box* targetBox = target->GetCollision(); if ( !BoxesIntersect(sourceBox, targetBox) ) { return; } float x1, x2, x3, x4, y1, y2, y3, y4; x1 = sourceBox->GetUpperLeft().x; x2 = targetBox->GetUpperRight().x; x3 = sourceBox->GetUpperRight().x; x4 = targetBox->GetUpperLeft().x; y1 = sourceBox->GetLowerLeft().y; y2 = targetBox->GetUpperLeft().y; y3 = sourceBox->GetUpperLeft().y; y4 = targetBox->GetLowerLeft().y; float bottomOverlap = y1-y2+COLLISION_PADDING; float topOverlap = y3-y4-COLLISION_PADDING; float leftOverlap = x1-x2-COLLISION_PADDING; float rightOverlap = x3-x4+COLLISION_PADDING; float moveY = abs(topOverlap) > abs(bottomOverlap) ? bottomOverlap : topOverlap; float moveX = abs(leftOverlap) > abs(rightOverlap) ? rightOverlap : leftOverlap; Vector2 moveFirst, moveSecond; if (abs(moveX) < abs(moveY)) { moveFirst.x = moveX; moveSecond.y = moveY; } else { moveFirst.y = moveY; moveSecond.x = moveX; } target->Move(moveFirst); if ( BoxesIntersect(sourceBox, targetBox) ) { target->Move(moveSecond); } } break; ///////////////////////////////////////////////////////////////////////////// case op_playanim: { const unsigned char* data = ins->GetData(); Actor* target = GetActor(scriptState.get(*data)); unsigned char animName = (unsigned char)*( DataAtOffset(data, 1) ); target->GetAnimComponent()->PlayAnim((char*)scriptState.get(animName)); } break; ///////////////////////////////////////////////////////////////////////////// case op_kill: const unsigned char* data = ins->GetData(); Actor* target = (Actor*) scriptState.get(*(unsigned int*)data); target->MarkForCleanup(); break; } // end switch(instruction) } // end for } // end Execute