sceneZoneSpaceManager.cpp 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2012 GarageGames, LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to
  6. // deal in the Software without restriction, including without limitation the
  7. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. // sell copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. // IN THE SOFTWARE.
  21. //-----------------------------------------------------------------------------
  22. #include "platform/platform.h"
  23. #include "scene/zones/sceneZoneSpaceManager.h"
  24. #include "platform/profiler.h"
  25. #include "platform/platformMemory.h"
  26. #include "scene/zones/sceneRootZone.h"
  27. #include "scene/zones/sceneZoneSpace.h"
  28. #include "scene/sceneContainer.h"
  29. // Uncomment to enable verification code for debugging. This slows the
  30. // manager down significantly but will allow to find zoning state corruption
  31. // much quicker.
  32. //#define DEBUG_VERIFY
  33. //#define DEBUG_SPEW
  34. //-----------------------------------------------------------------------------
  35. SceneZoneSpaceManager::SceneZoneSpaceManager( SceneContainer* container )
  36. : mRootZone( new SceneRootZone() ),
  37. mContainer( container ),
  38. mNumTotalAllocatedZones( 0 ),
  39. mNumActiveZones( 0 ),
  40. mDirtyArea( Box3F::Invalid )
  41. {
  42. VECTOR_SET_ASSOCIATION( mZoneSpaces );
  43. VECTOR_SET_ASSOCIATION( mZoneLists );
  44. VECTOR_SET_ASSOCIATION( mZoneSpacesQueryList );
  45. VECTOR_SET_ASSOCIATION( mDirtyObjects );
  46. VECTOR_SET_ASSOCIATION( mDirtyZoneSpaces );
  47. }
  48. //-----------------------------------------------------------------------------
  49. SceneZoneSpaceManager::~SceneZoneSpaceManager()
  50. {
  51. // Delete root zone.
  52. SAFE_DELETE( mRootZone );
  53. mNumTotalAllocatedZones = 0;
  54. mNumActiveZones = 0;
  55. }
  56. //-----------------------------------------------------------------------------
  57. void SceneZoneSpaceManager::registerZones( SceneZoneSpace* object, U32 numZones )
  58. {
  59. AssertFatal( _getZoneSpaceIndex( object ) == -1, "SceneZoneSpaceManager::registerZones - Object already registered" );
  60. _compactZonesCheck();
  61. const U32 zoneRangeStart = mNumTotalAllocatedZones;
  62. mNumTotalAllocatedZones += numZones;
  63. mNumActiveZones += numZones;
  64. object->mNumZones = numZones;
  65. object->mZoneRangeStart = zoneRangeStart;
  66. // Allocate zone lists for all of the zones managed by the object.
  67. // Add an entry to each list that points back to the zone space.
  68. mZoneLists.increment( numZones );
  69. for( U32 i = zoneRangeStart; i < mNumTotalAllocatedZones; ++ i )
  70. {
  71. mZoneLists[i] = _allocZoneList(object);
  72. }
  73. // Add space to list.
  74. mZoneSpaces.push_back( object );
  75. object->mManager = this;
  76. // Set ZoneObjectType.
  77. object->mTypeMask |= ZoneObjectType;
  78. // Put the object on the dirty list.
  79. if( !object->isRootZone() )
  80. {
  81. // Make sure the object gets on the zone space list even
  82. // if it is already on the object dirty list.
  83. object->mZoneRefDirty = false;
  84. notifyObjectChanged( object );
  85. }
  86. #ifdef DEBUG_SPEW
  87. Platform::outputDebugString( "[SceneZoneSpaceManager] Range %i-%i allocated to: %s",
  88. zoneRangeStart, numZones, object->describeSelf().c_str() );
  89. #endif
  90. }
  91. //-----------------------------------------------------------------------------
  92. void SceneZoneSpaceManager::unregisterZones( SceneZoneSpace* object )
  93. {
  94. S32 zoneSpaceIndex = _getZoneSpaceIndex( object );
  95. AssertFatal( zoneSpaceIndex != -1, "SceneZoneSpaceManager::unregisterZones - Object not registered as zone space" );
  96. AssertFatal( mNumActiveZones >= object->mNumZones, "SceneZoneSpaceManager::unregisterZones - Too many zones removed");
  97. const U32 zoneRangeStart = object->getZoneRangeStart();
  98. const U32 numZones = object->getZoneRange();
  99. // Destroy the zone lists for the zones registered
  100. // by the object.
  101. for( U32 j = zoneRangeStart; j < zoneRangeStart + numZones; j ++ )
  102. {
  103. ZoneObjectList* list = mZoneLists[j];
  104. // Delete all object links.
  105. _clearZoneList( j );
  106. _freeZoneList(list);
  107. }
  108. // Destroy the connections the zone space has.
  109. object->_disconnectAllZoneSpaces();
  110. // Remove the zone manager entry.
  111. mNumActiveZones -= numZones;
  112. mZoneSpaces.erase( zoneSpaceIndex );
  113. // Clear ZoneObjectType.
  114. object->mTypeMask &= ~ZoneObjectType;
  115. // Clear zone assignments.
  116. object->mZoneRangeStart = InvalidZoneId;
  117. object->mNumZones = 0;
  118. object->mManager = NULL;
  119. // Mark the zone space's area as dirty.
  120. mDirtyArea.intersect( object->getWorldBox() );
  121. #ifdef DEBUG_SPEW
  122. Platform::outputDebugString( "[SceneZoneSpaceManager] Range %i-%i released from: %s",
  123. zoneRangeStart, numZones, object->describeSelf().c_str() );
  124. #endif
  125. }
  126. //-----------------------------------------------------------------------------
  127. void SceneZoneSpaceManager::_rezoneObjects( const Box3F& area )
  128. {
  129. static Vector< SceneObject* > sObjects( __FILE__, __LINE__ );
  130. // Find all objects in the area. We cannot use the callback
  131. // version here and directly trigger rezoning since the rezoning
  132. // itself does a container query.
  133. sObjects.clear();
  134. mContainer->findObjectList( area, 0xFFFFFFFF, &sObjects );
  135. // Rezone the objects.
  136. const U32 numObjects = sObjects.size();
  137. for( U32 i = 0; i < numObjects; ++ i )
  138. {
  139. SceneObject* object = sObjects[ i ];
  140. if( object != getRootZone() )
  141. _rezoneObject( object );
  142. }
  143. }
  144. //-----------------------------------------------------------------------------
  145. void SceneZoneSpaceManager::_compactZonesCheck()
  146. {
  147. if( mNumActiveZones > ( mNumTotalAllocatedZones / 2 ) )
  148. return;
  149. // Redistribute the zone IDs among the current zone spaces
  150. // so that the range of IDs is consecutive.
  151. const U32 numZoneSpaces = mZoneSpaces.size();
  152. U32 nextZoneId = 0;
  153. Vector< ZoneObjectList* > newZoneLists;
  154. newZoneLists.setSize( mNumActiveZones );
  155. for( U32 i = 0; i < numZoneSpaces; ++ i )
  156. {
  157. SceneZoneSpace* space = mZoneSpaces[ i ];
  158. const U32 oldZoneRangeStart = space->getZoneRangeStart();
  159. const U32 newZoneRangeStart = nextZoneId;
  160. const U32 numZones = space->getZoneRange();
  161. // Assign the new zone range start.
  162. space->mZoneRangeStart = newZoneRangeStart;
  163. nextZoneId += numZones;
  164. // Relocate the zone lists to match the new zone IDs and update
  165. // the contents of the zone lists to match the new IDs.
  166. for( U32 n = 0; n < numZones; ++ n )
  167. {
  168. const U32 newZoneId = newZoneRangeStart + n;
  169. const U32 oldZoneId = oldZoneRangeStart + n;
  170. // Relocate list.
  171. newZoneLists[ newZoneId ] = mZoneLists[ oldZoneId ];
  172. if (mZoneLists[ newZoneId ] == NULL)
  173. continue;
  174. // Update entries.
  175. for (SceneObject* obj : mZoneLists[ newZoneId ]->getObjects())
  176. {
  177. mObjectZoneLists.replaceListBin(obj->mZoneListHandle, oldZoneId, newZoneId);
  178. }
  179. }
  180. }
  181. mNumTotalAllocatedZones = nextZoneId;
  182. mZoneLists = newZoneLists;
  183. AssertFatal( mNumTotalAllocatedZones == mNumActiveZones, "SceneZoneSpaceManager::_compactZonesCheck - Error during compact; mismatch between active and allocated zones" );
  184. }
  185. //-----------------------------------------------------------------------------
  186. S32 SceneZoneSpaceManager::_getZoneSpaceIndex( SceneZoneSpace* object ) const
  187. {
  188. const U32 numZoneSpaces = getNumZoneSpaces();
  189. for( U32 i = 0; i < numZoneSpaces; ++ i )
  190. if( mZoneSpaces[ i ] == object )
  191. return i;
  192. return -1;
  193. }
  194. //-----------------------------------------------------------------------------
  195. void SceneZoneSpaceManager::findZone( const Point3F& p, SceneZoneSpace*& owner, U32& zone ) const
  196. {
  197. AssertFatal( mNumActiveZones >= 1, "SceneZoneSpaceManager::findZone - Must have at least one active zone in scene (outdoor zone)" );
  198. // If there are no zones in the level other than the outdoor
  199. // zone, just return that.
  200. if( mNumActiveZones == 1 )
  201. {
  202. owner = getRootZone();
  203. zone = RootZoneId;
  204. return;
  205. }
  206. PROFILE_SCOPE( SceneZoneSpaceManager_findZone );
  207. // Query the scene container for zones with a query
  208. // box that tightly fits around the point.
  209. Box3F queryBox( p.x - 0.1f, p.y - 0.1f, p.z - 0.1f,
  210. p.x + 0.1f, p.y + 0.1f, p.z + 0.1f );
  211. _queryZoneSpaces( queryBox );
  212. // Go through the zones and look for the first one that
  213. // contains the given point.
  214. const U32 numZones = mZoneSpacesQueryList.size();
  215. for( U32 i = 0; i < numZones; ++ i )
  216. {
  217. SceneZoneSpace* zoneSpace = dynamic_cast< SceneZoneSpace* >( mZoneSpacesQueryList[ i ] );
  218. if( !zoneSpace )
  219. continue;
  220. AssertFatal( zoneSpace != getRootZone(), "SceneZoneSpaceManager::findZone - SceneRootZone returned by zone manager query" );
  221. // If the point is in one of the zones of this manager,
  222. // then make this the result.
  223. U32 inZone = zoneSpace->getPointZone( p );
  224. if( inZone != InvalidZoneId )
  225. {
  226. owner = zoneSpace;
  227. zone = inZone;
  228. return;
  229. }
  230. }
  231. // No other zone matched so return the outdoor zone.
  232. owner = getRootZone();
  233. zone = RootZoneId;
  234. }
  235. //-----------------------------------------------------------------------------
  236. U32 SceneZoneSpaceManager::findZones( const Box3F& area, Vector< U32 >& outZones ) const
  237. {
  238. // Query all zone spaces in the area.
  239. _queryZoneSpaces( area );
  240. // Query each zone space for overlaps with the given
  241. // area and add the zones to outZones.
  242. bool outsideIncluded = false;
  243. U32 numTotalZones = 0;
  244. const U32 numZoneSpaces = mZoneSpacesQueryList.size();
  245. for( U32 i = 0; i < numZoneSpaces; ++ i )
  246. {
  247. SceneZoneSpace* zoneSpace = dynamic_cast< SceneZoneSpace* >( mZoneSpacesQueryList[ i ] );
  248. if( !zoneSpace )
  249. continue;
  250. AssertFatal( zoneSpace != getRootZone(), "SceneZoneSpaceManager::findZones - SceneRootZone returned by zone manager query" );
  251. // Query manager.
  252. U32 zones[ SceneObject::MaxObjectZones ];
  253. U32 numZones = 0;
  254. outsideIncluded |= zoneSpace->getOverlappingZones( area, zones, numZones );
  255. // Add overlapped zones.
  256. for( U32 n = 0; n < numZones; n ++ )
  257. {
  258. outZones.push_back( zones[ n ] );
  259. numTotalZones ++;
  260. }
  261. }
  262. // If the area box wasn't fully enclosed by the zones of the
  263. // manager(s) or the query only returned the outside zone,
  264. // add the outside zone to the list.
  265. if( outsideIncluded || numTotalZones == 0 )
  266. {
  267. outZones.push_back( RootZoneId );
  268. numTotalZones ++;
  269. }
  270. return numTotalZones;
  271. }
  272. //-----------------------------------------------------------------------------
  273. void SceneZoneSpaceManager::_rezoneObject( SceneObject* object )
  274. {
  275. PROFILE_SCOPE( SceneZoneSpaceManager_rezoneObject );
  276. AssertFatal( !dynamic_cast< SceneRootZone* >( object ), "SceneZoneSpaceManager::_rezoneObject - Cannot rezone the SceneRootZone!" );
  277. // If the object is not yet assigned to zones,
  278. // do so now and return.
  279. if( !object->mNumCurrZones )
  280. {
  281. _zoneInsert( object );
  282. return;
  283. }
  284. // If we have no zones in the scene other than the outdoor zone or if the
  285. // object has global bounds on (and thus is always in the outdoor zone) or
  286. // is an object that is restricted to the outdoor zone, leave the object's
  287. // zoning state untouched.
  288. if( mNumActiveZones == 1 || object->isGlobalBounds() || object->getTypeMask() & OUTDOOR_OBJECT_TYPEMASK )
  289. {
  290. object->mZoneRefDirty = false;
  291. return;
  292. }
  293. // First, find out whether there's even a chance of the zoning to have changed
  294. // for the object.
  295. _queryZoneSpaces( object->getWorldBox() );
  296. U32 numZones = 0;
  297. U32* zones = mObjectZoneLists.getValues(object->mZoneListHandle, numZones);
  298. const U32 numZoneSpaces = mZoneSpacesQueryList.size();
  299. if( !numZoneSpaces )
  300. {
  301. // There is no zone in the object's area. If it is already assigned to the
  302. // root zone, then we don't need an update. Otherwise, we do.
  303. if( object->mNumCurrZones == 1 &&
  304. zones[0] == RootZoneId )
  305. {
  306. object->mZoneRefDirty = false;
  307. return;
  308. }
  309. }
  310. // Update the object's zoning information by removing and recomputing
  311. // its zoning information.
  312. _zoneRemove( object, false );
  313. _zoneInsert( object, true ); // Query already in place.
  314. }
  315. //-----------------------------------------------------------------------------
  316. void SceneZoneSpaceManager::registerObject( SceneObject* object )
  317. {
  318. // Just put it on the dirty list.
  319. notifyObjectChanged( object );
  320. }
  321. //-----------------------------------------------------------------------------
  322. void SceneZoneSpaceManager::unregisterObject( SceneObject* object )
  323. {
  324. // Remove from dirty list.
  325. mDirtyObjects.remove( object );
  326. // Remove from zone lists.
  327. _zoneRemove( object );
  328. // If it's a zone space, unregister it.
  329. if( object->getTypeMask() & ZoneObjectType && dynamic_cast< SceneZoneSpace* >( object ) )
  330. {
  331. SceneZoneSpace* zoneSpace = static_cast< SceneZoneSpace* >( object );
  332. unregisterZones( zoneSpace );
  333. mDirtyZoneSpaces.remove( zoneSpace );
  334. }
  335. }
  336. //-----------------------------------------------------------------------------
  337. void SceneZoneSpaceManager::updateObject( SceneObject* object )
  338. {
  339. // If no zone spaces have changed and the object's zoning
  340. // state is clean, then there's nothing to do for this object.
  341. if( mDirtyZoneSpaces.empty() && !object->mZoneRefDirty )
  342. return;
  343. // Otherwise update all the dirty zoning state.
  344. updateZoningState();
  345. }
  346. //-----------------------------------------------------------------------------
  347. void SceneZoneSpaceManager::notifyObjectChanged( SceneObject* object )
  348. {
  349. AssertFatal( object != getRootZone(), "SceneZoneSpaceManager::notifyObjectChanged - Cannot dirty root zone!" );
  350. // Ignore if object is already on the dirty list.
  351. if( object->mZoneRefDirty )
  352. return;
  353. // Put the object on the respective dirty list.
  354. if( object->getTypeMask() & ZoneObjectType )
  355. {
  356. SceneZoneSpace* zoneSpace = dynamic_cast< SceneZoneSpace* >( object );
  357. AssertFatal( zoneSpace != NULL, "SceneZoneSpaceManager::notifyObjectChanged - ZoneObjectType is not a SceneZoneSpace!" );
  358. if( zoneSpace )
  359. mDirtyZoneSpaces.push_back( zoneSpace );
  360. }
  361. else
  362. {
  363. mDirtyObjects.push_back( object );
  364. }
  365. // Mark object as dirty.
  366. object->mZoneRefDirty = true;
  367. }
  368. //-----------------------------------------------------------------------------
  369. void SceneZoneSpaceManager::updateZoningState()
  370. {
  371. // If there are no dirty objects, there's nothing to do.
  372. if( mDirtyObjects.empty() &&
  373. mDirtyZoneSpaces.empty() &&
  374. mDirtyArea == Box3F::Invalid )
  375. return;
  376. // Otherwise, first update the zone spaces. Do this in two passes:
  377. // first take all the dirty zone spaces out of the zoning state and
  378. // then rezone the combined area of all dirty zone spaces.
  379. //
  380. // Note that this path here is pretty much only relevant during loading
  381. // or editing and thus can be less performant than the path for individual
  382. // objects below.
  383. while( !mDirtyZoneSpaces.empty() )
  384. {
  385. SceneZoneSpace* zoneSpace = mDirtyZoneSpaces.last();
  386. mDirtyZoneSpaces.decrement();
  387. // Remove the zoning state of the object.
  388. _zoneRemove( zoneSpace );
  389. // Destroy all connections that this zone space has to
  390. // other zone spaces.
  391. zoneSpace->_disconnectAllZoneSpaces();
  392. // Nuke its zone lists.
  393. const U32 numZones = zoneSpace->getZoneRange();
  394. for( U32 n = 0; n < numZones; ++ n )
  395. _clearZoneList( zoneSpace->getZoneRangeStart() + n );
  396. // Merge into dirty region.
  397. mDirtyArea.intersect( zoneSpace->getWorldBox() );
  398. }
  399. if( mDirtyArea != Box3F::Invalid )
  400. {
  401. // Rezone everything in the dirty region.
  402. _rezoneObjects( mDirtyArea );
  403. mDirtyArea = Box3F::Invalid;
  404. // Verify zoning state.
  405. #ifdef DEBUG_VERIFY
  406. verifyState();
  407. #endif
  408. // Fire the zoning changed signal to let interested parties
  409. // know that the zoning setup of the scene has changed.
  410. getZoningChangedSignal().trigger( this );
  411. }
  412. // And finally, update objects that have changed state.
  413. while( !mDirtyObjects.empty() )
  414. {
  415. SceneObject* object = mDirtyObjects.last();
  416. mDirtyObjects.decrement();
  417. if( object->mZoneRefDirty )
  418. _rezoneObject( object );
  419. AssertFatal( !object->mZoneRefDirty, "SceneZoneSpaceManager::updateZoningState - Object still dirty!" );
  420. }
  421. AssertFatal( mDirtyObjects.empty(), "SceneZoneSpaceManager::updateZoningState - Still have dirty objects!" );
  422. AssertFatal( mDirtyZoneSpaces.empty(), "SceneZoneSpaceManager::updateZoningState - Still have dirty zones!" );
  423. }
  424. //-----------------------------------------------------------------------------
  425. void SceneZoneSpaceManager::_zoneInsert( SceneObject* object, bool queryListInitialized )
  426. {
  427. PROFILE_SCOPE( SceneZoneSpaceManager_zoneInsert );
  428. AssertFatal( object->mNumCurrZones == 0, "SceneZoneSpaceManager::_zoneInsert - Object already in zone list" );
  429. AssertFatal( object->getContainer() != NULL, "SceneZoneSpaceManager::_zoneInsert - Object must be in scene" );
  430. AssertFatal( !dynamic_cast< SceneRootZone* >( object ), "SceneZoneSpaceManager::_zoneInsert - Must not be called on SceneRootZone" );
  431. // If all we have is a single zone in the scene, then it must
  432. // be the outdoor zone. Simply assign the object to it. Also do this
  433. // if the object has global bounds on since we always assign these to
  434. // just the outdoor zone. Finally, also do it for all object types that
  435. // we want to restrict to the outdoor zone.
  436. bool outsideOnly = mNumActiveZones == 1 || object->isGlobalBounds() || (object->getTypeMask() & OUTDOOR_OBJECT_TYPEMASK);
  437. U32 numGlobalZones = 0;
  438. U32 remainingZones = SceneObject::MaxObjectZones;
  439. U32 globalZones[SceneObject::MaxObjectZones];
  440. bool outsideIncluded = true;
  441. mTempObjectZones.clear();
  442. if (!outsideOnly)
  443. {
  444. // Otherwise find all zones spaces that intersect with the object's
  445. // world box.
  446. if (!queryListInitialized)
  447. _queryZoneSpaces(object->getWorldBox());
  448. // Go through the zone spaces and link all zones that the object
  449. // overlaps.
  450. const U32 numZoneSpaces = mZoneSpacesQueryList.size();
  451. mTempObjectZones.reserve(numZoneSpaces);
  452. for (U32 i = 0; i < numZoneSpaces; ++i)
  453. {
  454. SceneZoneSpace* zoneSpace = dynamic_cast<SceneZoneSpace*>(mZoneSpacesQueryList[i]);
  455. if (!zoneSpace)
  456. continue;
  457. AssertFatal(zoneSpace != getRootZone(), "SceneZoneSpaceManager::_zoneInsert - SceneRootZone returned by zone space query");
  458. // If we are inserting a zone space, then the query will turn up
  459. // the object itself at some point. Skip it.
  460. if (zoneSpace == object)
  461. continue;
  462. // Find the zones that the object overlaps within
  463. // the zone space.
  464. U32 numZones = 0;
  465. U32 zones[SceneObject::MaxObjectZones];
  466. bool overlapsOutside = zoneSpace->getOverlappingZones(object, zones, numZones);
  467. AssertFatal(numZones != 0 || overlapsOutside,
  468. "SceneZoneSpaceManager::_zoneInsert - Object must be fully contained in one or more zones or intersect the outside zone");
  469. outsideIncluded &= overlapsOutside; // Only include outside if *none* of the zones fully contains the object.
  470. // Clamp the zone count
  471. numZones = getMin(remainingZones, numZones);
  472. if (numZones > 0)
  473. {
  474. // Add to temp list
  475. TempZoneRecord zoneRecord;
  476. zoneRecord.numZones = numZones;
  477. zoneRecord.space = zoneSpace;
  478. zoneRecord.startZone = numGlobalZones;
  479. dCopyArray(globalZones + numGlobalZones, zones, numZones);
  480. mTempObjectZones.push_back(zoneRecord);
  481. numGlobalZones += zoneRecord.numZones;
  482. remainingZones -= zoneRecord.numZones;
  483. }
  484. }
  485. }
  486. // If the object crosses into the outside zone or hasn't been
  487. // added to any zone above, add it to the outside zone.
  488. if (outsideOnly || (outsideIncluded && remainingZones > 0))
  489. {
  490. TempZoneRecord zoneRecord;
  491. zoneRecord.numZones = 1;
  492. zoneRecord.space = static_cast<SceneZoneSpace*>(getRootZone());
  493. zoneRecord.startZone = numGlobalZones;
  494. globalZones[numGlobalZones++] = RootZoneId;
  495. mTempObjectZones.push_back(zoneRecord);
  496. }
  497. if (numGlobalZones > 0)
  498. {
  499. _setObjectZoneList(object, numGlobalZones, globalZones);
  500. }
  501. for (TempZoneRecord record : mTempObjectZones)
  502. {
  503. // Let the zone manager know we have added objects to its
  504. // zones.
  505. if (record.numZones > 0)
  506. {
  507. record.space->_onZoneAddObject(object, globalZones + record.startZone, record.numZones);
  508. }
  509. }
  510. // Mark the zoning state of the object as current.
  511. object->mZoneRefDirty = false;
  512. }
  513. //-----------------------------------------------------------------------------
  514. void SceneZoneSpaceManager::_zoneRemove( SceneObject* obj, bool freeList )
  515. {
  516. if (obj->mZoneListHandle == 0)
  517. return;
  518. PROFILE_SCOPE( SceneZoneSpaceManager_zoneRemove );
  519. // Remove the object from the zone lists.
  520. U32 numZones = 0;
  521. U32* zones = NULL;
  522. bool zonesDirty = obj->mZoneRefDirty;
  523. zones = mObjectZoneLists.getValues(obj->mZoneListHandle, numZones);
  524. for (U32 i=0; i<numZones; i++)
  525. {
  526. // Let the zone owner know we are removing an object
  527. // from its zones.
  528. SceneZoneSpace* space = getZoneOwner(zones[i]);
  529. if (space == NULL)
  530. {
  531. AssertFatal(zonesDirty, "Object still has reference to removed zone");
  532. continue;
  533. }
  534. space->_onZoneRemoveObject(obj);
  535. // Remove the object from the zones object list
  536. Vector<SceneObject*>* objectList = getZoneObjects(zones[i]);
  537. if (objectList)
  538. {
  539. Vector<SceneObject*>::iterator itr = T3D::find(objectList->begin(), objectList->end(), obj);
  540. if (itr != objectList->end())
  541. {
  542. objectList->erase(itr);
  543. }
  544. }
  545. }
  546. // Clear the object's zoning state.
  547. mObjectZoneLists.freeList(obj->mZoneListHandle);
  548. obj->mZoneListHandle = 0;
  549. obj->mZoneRefDirty = false;
  550. obj->mNumCurrZones = 0;
  551. }
  552. //-----------------------------------------------------------------------------
  553. /// Realloc zoning state to the given object.
  554. void SceneZoneSpaceManager::_zoneRealloc(SceneObject* object, bool queryListInitialized)
  555. {
  556. if (object->mZoneListHandle == 0)
  557. return _zoneInsert(object, queryListInitialized);
  558. }
  559. //-----------------------------------------------------------------------------
  560. void SceneZoneSpaceManager::_setObjectZoneList( SceneObject* object, U32 numZones, U32* zoneList )
  561. {
  562. #ifdef TORQUE_ENABLE_ASSERTS
  563. SceneZoneSpace* zoneSpace = dynamic_cast<SceneZoneSpace*>(object);
  564. if (zoneSpace)
  565. {
  566. for (U32 i = 0; i < numZones; i++)
  567. {
  568. bool inRange = zoneList[i] >= zoneSpace->mZoneRangeStart && zoneList[i] < (zoneSpace->mZoneRangeStart+zoneSpace->mNumZones);
  569. AssertFatal(!inRange, "SCene::_addToZoneList - Cannot add zone to itself");
  570. }
  571. }
  572. #endif
  573. // Alloc or re-use entry
  574. if (object->mZoneListHandle == 0)
  575. {
  576. object->mZoneListHandle = mObjectZoneLists.allocList(numZones, zoneList);
  577. }
  578. else if (numZones == 0)
  579. {
  580. mObjectZoneLists.freeList(object->mZoneListHandle);
  581. object->mZoneListHandle = 0;
  582. }
  583. else
  584. {
  585. mObjectZoneLists.reallocList(object->mZoneListHandle, numZones, zoneList);
  586. }
  587. // Make sure each zone has the object in its list
  588. for (U32 i = 0; i < numZones; i++)
  589. {
  590. mZoneLists[zoneList[i]]->mObjects.push_back(object);
  591. }
  592. object->mNumCurrZones = numZones;
  593. }
  594. //-----------------------------------------------------------------------------
  595. void SceneZoneSpaceManager::_clearZoneList( U32 zoneId )
  596. {
  597. AssertFatal( zoneId < getNumZones(), "SceneZoneSpaceManager::_clearZoneList - Zone ID out of range" );
  598. ZoneObjectList* list = mZoneLists[zoneId];
  599. SceneZoneSpace* zoneSpace = getZoneOwner( zoneId );
  600. // Go through the objects in the zone list and unlink and
  601. // delete their zone entries.
  602. for( SceneObject* object : list->getObjects() )
  603. {
  604. AssertFatal( object != NULL, "SceneZoneSpaceManager::_clearZoneList - Object field not set on link" );
  605. AssertFatal( object->mNumCurrZones > 0, "SceneZoneSpaceManager::_clearZoneList - Bad reference count" );
  606. object->mNumCurrZones --;
  607. // Keep things simple here and flag the objects zone list for re-calculation
  608. object->mZoneRefDirty = true;
  609. // Let the zone know we have removed the object.
  610. zoneSpace->_onZoneRemoveObject( object );
  611. }
  612. // Reset all objects for zone
  613. list->mObjects.clear();
  614. }
  615. //-----------------------------------------------------------------------------
  616. bool SceneZoneSpaceManager::_isInZoneList( U32 zoneId, SceneObject* object ) const
  617. {
  618. SceneZoneSpaceManager::ZoneObjectList* list = mZoneLists[zoneId];
  619. if (list == NULL)
  620. return false;
  621. for (SceneObject* obj : list->getObjects())
  622. {
  623. if (obj == object)
  624. return true;
  625. }
  626. return false;
  627. }
  628. //-----------------------------------------------------------------------------
  629. void SceneZoneSpaceManager::_queryZoneSpaces( const Box3F& area ) const
  630. {
  631. mZoneSpacesQueryList.clear();
  632. mContainer->findObjectList( area, ZoneObjectType, &mZoneSpacesQueryList );
  633. }
  634. //-----------------------------------------------------------------------------
  635. void SceneZoneSpaceManager::dumpZoneStates( bool update )
  636. {
  637. if( update )
  638. _rezoneObjects( getRootZone()->getWorldBox() );
  639. const U32 numZoneSpaces = mZoneSpaces.size();
  640. for( U32 i = 0; i < numZoneSpaces; ++ i )
  641. mZoneSpaces[ i ]->dumpZoneState( false );
  642. }
  643. //-----------------------------------------------------------------------------
  644. void SceneZoneSpaceManager::verifyState()
  645. {
  646. AssertFatal( mZoneSpaces.size() <= mNumActiveZones,
  647. "SceneZoneSpaceManager::verifyState - More zone spaces than active zones!" );
  648. AssertFatal( mNumTotalAllocatedZones >= mNumActiveZones,
  649. "SceneZoneSpaceManager::verifyState - Fewer allocated than active zones!" );
  650. AssertFatal( mRootZone->getZoneRangeStart() == 0,
  651. "SceneZoneSpaceManager::verifyState - Invalid ID on root zone!" );
  652. AssertFatal( mRootZone->getZoneRange() == 1,
  653. "SceneZoneSpaceManager::verifyState - Invalid zone range on root zone!" );
  654. // First validate the zone spaces themselves.
  655. const U32 numZoneSpaces = mZoneSpaces.size();
  656. for( U32 i = 0; i < numZoneSpaces; ++ i )
  657. {
  658. SceneZoneSpace* space = mZoneSpaces[ i ];
  659. #ifndef TORQUE_DISABLE_MEMORY_MANAGER
  660. Memory::checkPtr( space );
  661. #endif
  662. AssertFatal( space->getTypeMask() & ZoneObjectType, "SceneZoneSpaceManager::verifyState - Zone space is not a ZoneObjectType!" );
  663. const U32 zoneRangeStart = space->getZoneRangeStart();
  664. const U32 numZones = space->getZoneRange();
  665. // Verify each of the allocated zones in this space.
  666. for( U32 n = 0; n < numZones; ++ n )
  667. {
  668. const U32 zoneId = zoneRangeStart + n;
  669. // Simple validation of zone ID.
  670. AssertFatal( isValidZoneId( zoneId ), "SceneZoneSpaceManager::verifyState - Zone space is assigned in invalid zone ID!" );
  671. AssertFatal( mZoneLists[ zoneId ] != NULL, "SceneZoneSpaceManager::verifyState - Zone list missing for zone!" );
  672. AssertFatal( mZoneLists[ zoneId ]->mManager == space, "SceneZoneSpaceManager::verifyState - Zone list entry #0 is not referring back to zone!" );
  673. for( SceneObject* object : mZoneLists[ zoneId ]->getObjects() )
  674. {
  675. AssertFatal( mObjectZoneLists.containsBinItem(object->mZoneListHandle, zoneId), "SceneZoneSpaceManager::verifyState - Object doesn't have zone in list!");
  676. #ifndef TORQUE_DISABLE_MEMORY_MANAGER
  677. Memory::checkPtr( object );
  678. #endif
  679. }
  680. }
  681. // Make sure no other zone space owns any of the same IDs.
  682. for( U32 n = 0; n < numZoneSpaces; ++ n )
  683. {
  684. if( n == i )
  685. continue;
  686. SceneZoneSpace* otherSpace = mZoneSpaces[ n ];
  687. AssertFatal( otherSpace->getZoneRangeStart() >= zoneRangeStart + numZones ||
  688. otherSpace->getZoneRangeStart() + otherSpace->getZoneRange() <= zoneRangeStart,
  689. "SceneZoneSpaceManager::verifyState - Overlap between zone ID ranges of zone spaces!" );
  690. }
  691. // Make sure that all zone connections appear to be valid.
  692. for( SceneZoneSpace::ZoneSpaceRef* ref = space->mConnectedZoneSpaces; ref != NULL; ref = ref->mNext )
  693. {
  694. #ifndef TORQUE_DISABLE_MEMORY_MANAGER
  695. Memory::checkPtr( ref->mZoneSpace );
  696. #endif
  697. AssertFatal( _getZoneSpaceIndex( ref->mZoneSpace ) != -1, "SceneZoneSpaceManager::verifyState - Zone connected to invalid zone!" );
  698. AssertFatal( ref->mZoneSpace->getTypeMask() & ZoneObjectType, "SceneZoneSpaceManager::verifyState - Zone space is not a ZoneObjectType!" );
  699. }
  700. }
  701. //TODO: can do a lot more validation here
  702. }
  703. //-----------------------------------------------------------------------------
  704. SceneZoneSpaceManager::ZoneObjectList* SceneZoneSpaceManager::_allocZoneList(SceneZoneSpace* space)
  705. {
  706. SceneZoneSpaceManager::ZoneObjectList* ret = NULL;
  707. if (!mZoneListPool.empty())
  708. {
  709. ret = mZoneListPool.last();
  710. ret->mManager = space;
  711. mZoneListPool.pop_back();
  712. }
  713. else
  714. {
  715. ret = new SceneZoneSpaceManager::ZoneObjectList(space);
  716. }
  717. return ret;
  718. }
  719. //-----------------------------------------------------------------------------
  720. void SceneZoneSpaceManager::_freeZoneList(SceneZoneSpaceManager::ZoneObjectList* list)
  721. {
  722. list->mManager = NULL;
  723. list->getObjects().clear();
  724. mZoneListPool.push_back(list);
  725. }