sceneZoneSpaceManager.cpp 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923
  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/sceneContainer.h"
  27. #include "scene/zones/sceneRootZone.h"
  28. #include "scene/zones/sceneZoneSpace.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. ClassChunker< SceneObject::ZoneRef > SceneZoneSpaceManager::smZoneRefChunker;
  35. //-----------------------------------------------------------------------------
  36. SceneZoneSpaceManager::SceneZoneSpaceManager( SceneContainer* container )
  37. : mRootZone( new SceneRootZone() ),
  38. mContainer( container ),
  39. mNumTotalAllocatedZones( 0 ),
  40. mNumActiveZones( 0 ),
  41. mDirtyArea( Box3F::Invalid )
  42. {
  43. VECTOR_SET_ASSOCIATION( mZoneSpaces );
  44. VECTOR_SET_ASSOCIATION( mZoneLists );
  45. VECTOR_SET_ASSOCIATION( mZoneSpacesQueryList );
  46. VECTOR_SET_ASSOCIATION( mDirtyObjects );
  47. VECTOR_SET_ASSOCIATION( mDirtyZoneSpaces );
  48. }
  49. //-----------------------------------------------------------------------------
  50. SceneZoneSpaceManager::~SceneZoneSpaceManager()
  51. {
  52. // Delete root zone.
  53. SAFE_DELETE( mRootZone );
  54. mNumTotalAllocatedZones = 0;
  55. mNumActiveZones = 0;
  56. }
  57. //-----------------------------------------------------------------------------
  58. void SceneZoneSpaceManager::registerZones( SceneZoneSpace* object, U32 numZones )
  59. {
  60. AssertFatal( _getZoneSpaceIndex( object ) == -1, "SceneZoneSpaceManager::registerZones - Object already registered" );
  61. _compactZonesCheck();
  62. const U32 zoneRangeStart = mNumTotalAllocatedZones;
  63. mNumTotalAllocatedZones += numZones;
  64. mNumActiveZones += numZones;
  65. object->mNumZones = numZones;
  66. object->mZoneRangeStart = zoneRangeStart;
  67. // Allocate zone lists for all of the zones managed by the object.
  68. // Add an entry to each list that points back to the zone space.
  69. mZoneLists.increment( numZones );
  70. for( U32 i = zoneRangeStart; i < mNumTotalAllocatedZones; ++ i )
  71. {
  72. SceneObject::ZoneRef* zoneRef = smZoneRefChunker.alloc();
  73. zoneRef->object = object;
  74. zoneRef->nextInBin = NULL;
  75. zoneRef->prevInBin = NULL;
  76. zoneRef->nextInObj = NULL;
  77. zoneRef->zone = i;
  78. mZoneLists[ i ] = zoneRef;
  79. }
  80. // Add space to list.
  81. mZoneSpaces.push_back( object );
  82. object->mManager = this;
  83. // Set ZoneObjectType.
  84. object->mTypeMask |= ZoneObjectType;
  85. // Put the object on the dirty list.
  86. if( !object->isRootZone() )
  87. {
  88. // Make sure the object gets on the zone space list even
  89. // if it is already on the object dirty list.
  90. object->mZoneRefDirty = false;
  91. notifyObjectChanged( object );
  92. }
  93. #ifdef DEBUG_SPEW
  94. Platform::outputDebugString( "[SceneZoneSpaceManager] Range %i-%i allocated to: %s",
  95. zoneRangeStart, numZones, object->describeSelf().c_str() );
  96. #endif
  97. }
  98. //-----------------------------------------------------------------------------
  99. void SceneZoneSpaceManager::unregisterZones( SceneZoneSpace* object )
  100. {
  101. S32 zoneSpaceIndex = _getZoneSpaceIndex( object );
  102. AssertFatal( zoneSpaceIndex != -1, "SceneZoneSpaceManager::unregisterZones - Object not registered as zone space" );
  103. AssertFatal( mNumActiveZones >= object->mNumZones, "SceneZoneSpaceManager::unregisterZones - Too many zones removed");
  104. const U32 zoneRangeStart = object->getZoneRangeStart();
  105. const U32 numZones = object->getZoneRange();
  106. // Destroy the zone lists for the zones registered
  107. // by the object.
  108. for( U32 j = zoneRangeStart; j < zoneRangeStart + numZones; j ++ )
  109. {
  110. // Delete all object links.
  111. _clearZoneList( j );
  112. // Delete the first link which refers to the zone itself.
  113. smZoneRefChunker.free( mZoneLists[ j ] );
  114. mZoneLists[ j ] = NULL;
  115. }
  116. // Destroy the connections the zone space has.
  117. object->_disconnectAllZoneSpaces();
  118. // Remove the zone manager entry.
  119. mNumActiveZones -= numZones;
  120. mZoneSpaces.erase( zoneSpaceIndex );
  121. // Clear ZoneObjectType.
  122. object->mTypeMask &= ~ZoneObjectType;
  123. // Clear zone assignments.
  124. object->mZoneRangeStart = InvalidZoneId;
  125. object->mNumZones = 0;
  126. object->mManager = NULL;
  127. // Mark the zone space's area as dirty.
  128. mDirtyArea.intersect( object->getWorldBox() );
  129. #ifdef DEBUG_SPEW
  130. Platform::outputDebugString( "[SceneZoneSpaceManager] Range %i-%i released from: %s",
  131. zoneRangeStart, numZones, object->describeSelf().c_str() );
  132. #endif
  133. }
  134. //-----------------------------------------------------------------------------
  135. void SceneZoneSpaceManager::_rezoneObjects( const Box3F& area )
  136. {
  137. static Vector< SceneObject* > sObjects( __FILE__, __LINE__ );
  138. // Find all objects in the area. We cannot use the callback
  139. // version here and directly trigger rezoning since the rezoning
  140. // itself does a container query.
  141. sObjects.clear();
  142. mContainer->findObjectList( area, 0xFFFFFFFF, &sObjects );
  143. // Rezone the objects.
  144. const U32 numObjects = sObjects.size();
  145. for( U32 i = 0; i < numObjects; ++ i )
  146. {
  147. SceneObject* object = sObjects[ i ];
  148. if( object != getRootZone() )
  149. _rezoneObject( object );
  150. }
  151. }
  152. //-----------------------------------------------------------------------------
  153. void SceneZoneSpaceManager::_compactZonesCheck()
  154. {
  155. if( mNumActiveZones > ( mNumTotalAllocatedZones / 2 ) )
  156. return;
  157. // Redistribute the zone IDs among the current zone spaces
  158. // so that the range of IDs is consecutive.
  159. const U32 numZoneSpaces = mZoneSpaces.size();
  160. U32 nextZoneId = 0;
  161. Vector< SceneObject::ZoneRef* > newZoneLists;
  162. newZoneLists.setSize( mNumActiveZones );
  163. for( U32 i = 0; i < numZoneSpaces; ++ i )
  164. {
  165. SceneZoneSpace* space = mZoneSpaces[ i ];
  166. const U32 oldZoneRangeStart = space->getZoneRangeStart();
  167. const U32 newZoneRangeStart = nextZoneId;
  168. const U32 numZones = space->getZoneRange();
  169. // Assign the new zone range start.
  170. space->mZoneRangeStart = newZoneRangeStart;
  171. nextZoneId += numZones;
  172. // Relocate the zone lists to match the new zone IDs and update
  173. // the contents of the zone lists to match the new IDs.
  174. for( U32 n = 0; n < numZones; ++ n )
  175. {
  176. const U32 newZoneId = newZoneRangeStart + n;
  177. const U32 oldZoneId = oldZoneRangeStart + n;
  178. // Relocate list.
  179. newZoneLists[ newZoneId ] = mZoneLists[ oldZoneId ];
  180. // Update entries.
  181. for( SceneObject::ZoneRef* ref = newZoneLists[ newZoneId ]; ref != NULL; ref = ref->nextInBin )
  182. ref->zone = newZoneId;
  183. }
  184. }
  185. mNumTotalAllocatedZones = nextZoneId;
  186. mZoneLists = newZoneLists;
  187. AssertFatal( mNumTotalAllocatedZones == mNumActiveZones, "SceneZoneSpaceManager::_compactZonesCheck - Error during compact; mismatch between active and allocated zones" );
  188. }
  189. //-----------------------------------------------------------------------------
  190. S32 SceneZoneSpaceManager::_getZoneSpaceIndex( SceneZoneSpace* object ) const
  191. {
  192. const U32 numZoneSpaces = getNumZoneSpaces();
  193. for( U32 i = 0; i < numZoneSpaces; ++ i )
  194. if( mZoneSpaces[ i ] == object )
  195. return i;
  196. return -1;
  197. }
  198. //-----------------------------------------------------------------------------
  199. void SceneZoneSpaceManager::findZone( const Point3F& p, SceneZoneSpace*& owner, U32& zone ) const
  200. {
  201. AssertFatal( mNumActiveZones >= 1, "SceneZoneSpaceManager::findZone - Must have at least one active zone in scene (outdoor zone)" );
  202. // If there are no zones in the level other than the outdoor
  203. // zone, just return that.
  204. if( mNumActiveZones == 1 )
  205. {
  206. owner = getRootZone();
  207. zone = RootZoneId;
  208. return;
  209. }
  210. PROFILE_SCOPE( SceneZoneSpaceManager_findZone );
  211. // Query the scene container for zones with a query
  212. // box that tightly fits around the point.
  213. Box3F queryBox( p.x - 0.1f, p.y - 0.1f, p.z - 0.1f,
  214. p.x + 0.1f, p.y + 0.1f, p.z + 0.1f );
  215. _queryZoneSpaces( queryBox );
  216. // Go through the zones and look for the first one that
  217. // contains the given point.
  218. const U32 numZones = mZoneSpacesQueryList.size();
  219. for( U32 i = 0; i < numZones; ++ i )
  220. {
  221. SceneZoneSpace* zoneSpace = dynamic_cast< SceneZoneSpace* >( mZoneSpacesQueryList[ i ] );
  222. if( !zoneSpace )
  223. continue;
  224. AssertFatal( zoneSpace != getRootZone(), "SceneZoneSpaceManager::findZone - SceneRootZone returned by zone manager query" );
  225. // If the point is in one of the zones of this manager,
  226. // then make this the result.
  227. U32 inZone = zoneSpace->getPointZone( p );
  228. if( inZone != InvalidZoneId )
  229. {
  230. owner = zoneSpace;
  231. zone = inZone;
  232. return;
  233. }
  234. }
  235. // No other zone matched so return the outdoor zone.
  236. owner = getRootZone();
  237. zone = RootZoneId;
  238. }
  239. //-----------------------------------------------------------------------------
  240. U32 SceneZoneSpaceManager::findZones( const Box3F& area, Vector< U32 >& outZones ) const
  241. {
  242. // Query all zone spaces in the area.
  243. _queryZoneSpaces( area );
  244. // Query each zone space for overlaps with the given
  245. // area and add the zones to outZones.
  246. bool outsideIncluded = false;
  247. U32 numTotalZones = 0;
  248. const U32 numZoneSpaces = mZoneSpacesQueryList.size();
  249. for( U32 i = 0; i < numZoneSpaces; ++ i )
  250. {
  251. SceneZoneSpace* zoneSpace = dynamic_cast< SceneZoneSpace* >( mZoneSpacesQueryList[ i ] );
  252. if( !zoneSpace )
  253. continue;
  254. AssertFatal( zoneSpace != getRootZone(), "SceneZoneSpaceManager::findZones - SceneRootZone returned by zone manager query" );
  255. // Query manager.
  256. U32 zones[ SceneObject::MaxObjectZones ];
  257. U32 numZones = 0;
  258. outsideIncluded |= zoneSpace->getOverlappingZones( area, zones, numZones );
  259. // Add overlapped zones.
  260. for( U32 n = 0; n < numZones; n ++ )
  261. {
  262. outZones.push_back( zones[ n ] );
  263. numTotalZones ++;
  264. }
  265. }
  266. // If the area box wasn't fully enclosed by the zones of the
  267. // manager(s) or the query only returned the outside zone,
  268. // add the outside zone to the list.
  269. if( outsideIncluded || numTotalZones == 0 )
  270. {
  271. outZones.push_back( RootZoneId );
  272. numTotalZones ++;
  273. }
  274. return numTotalZones;
  275. }
  276. //-----------------------------------------------------------------------------
  277. void SceneZoneSpaceManager::_rezoneObject( SceneObject* object )
  278. {
  279. PROFILE_SCOPE( SceneZoneSpaceManager_rezoneObject );
  280. AssertFatal( !dynamic_cast< SceneRootZone* >( object ), "SceneZoneSpaceManager::_rezoneObject - Cannot rezone the SceneRootZone!" );
  281. // If the object is not yet assigned to zones,
  282. // do so now and return.
  283. if( !object->mNumCurrZones )
  284. {
  285. _zoneInsert( object );
  286. return;
  287. }
  288. // If we have no zones in the scene other than the outdoor zone or if the
  289. // object has global bounds on (and thus is always in the outdoor zone) or
  290. // is an object that is restricted to the outdoor zone, leave the object's
  291. // zoning state untouched.
  292. if( mNumActiveZones == 1 || object->isGlobalBounds() || object->getTypeMask() & OUTDOOR_OBJECT_TYPEMASK )
  293. {
  294. object->mZoneRefDirty = false;
  295. return;
  296. }
  297. // First, find out whether there's even a chance of the zoning to have changed
  298. // for the object.
  299. _queryZoneSpaces( object->getWorldBox() );
  300. const U32 numZoneSpaces = mZoneSpacesQueryList.size();
  301. if( !numZoneSpaces )
  302. {
  303. // There is no zone in the object's area. If it is already assigned to the
  304. // root zone, then we don't need an update. Otherwise, we do.
  305. if( object->mNumCurrZones == 1 &&
  306. object->mZoneRefHead &&
  307. object->mZoneRefHead->zone == RootZoneId )
  308. {
  309. object->mZoneRefDirty = false;
  310. return;
  311. }
  312. }
  313. // Update the object's zoning information by removing and recomputing
  314. // its zoning information.
  315. _zoneRemove( object );
  316. _zoneInsert( object, true ); // Query already in place.
  317. }
  318. //-----------------------------------------------------------------------------
  319. void SceneZoneSpaceManager::registerObject( SceneObject* object )
  320. {
  321. // Just put it on the dirty list.
  322. notifyObjectChanged( object );
  323. }
  324. //-----------------------------------------------------------------------------
  325. void SceneZoneSpaceManager::unregisterObject( SceneObject* object )
  326. {
  327. // Remove from dirty list.
  328. mDirtyObjects.remove( object );
  329. // Remove from zone lists.
  330. _zoneRemove( object );
  331. // If it's a zone space, unregister it.
  332. if( object->getTypeMask() & ZoneObjectType && dynamic_cast< SceneZoneSpace* >( object ) )
  333. {
  334. SceneZoneSpace* zoneSpace = static_cast< SceneZoneSpace* >( object );
  335. unregisterZones( zoneSpace );
  336. mDirtyZoneSpaces.remove( zoneSpace );
  337. }
  338. }
  339. //-----------------------------------------------------------------------------
  340. void SceneZoneSpaceManager::updateObject( SceneObject* object )
  341. {
  342. // If no zone spaces have changed and the object's zoning
  343. // state is clean, then there's nothing to do for this object.
  344. if( mDirtyZoneSpaces.empty() && !object->mZoneRefDirty )
  345. return;
  346. // Otherwise update all the dirty zoning state.
  347. updateZoningState();
  348. }
  349. //-----------------------------------------------------------------------------
  350. void SceneZoneSpaceManager::notifyObjectChanged( SceneObject* object )
  351. {
  352. AssertFatal( object != getRootZone(), "SceneZoneSpaceManager::notifyObjectChanged - Cannot dirty root zone!" );
  353. // Ignore if object is already on the dirty list.
  354. if( object->mZoneRefDirty )
  355. return;
  356. // Put the object on the respective dirty list.
  357. if( object->getTypeMask() & ZoneObjectType )
  358. {
  359. SceneZoneSpace* zoneSpace = dynamic_cast< SceneZoneSpace* >( object );
  360. AssertFatal( zoneSpace != NULL, "SceneZoneSpaceManager::notifyObjectChanged - ZoneObjectType is not a SceneZoneSpace!" );
  361. if( zoneSpace )
  362. mDirtyZoneSpaces.push_back( zoneSpace );
  363. }
  364. else
  365. {
  366. mDirtyObjects.push_back( object );
  367. }
  368. // Mark object as dirty.
  369. object->mZoneRefDirty = true;
  370. }
  371. //-----------------------------------------------------------------------------
  372. void SceneZoneSpaceManager::updateZoningState()
  373. {
  374. // If there are no dirty objects, there's nothing to do.
  375. if( mDirtyObjects.empty() &&
  376. mDirtyZoneSpaces.empty() &&
  377. mDirtyArea == Box3F::Invalid )
  378. return;
  379. // Otherwise, first update the zone spaces. Do this in two passes:
  380. // first take all the dirty zone spaces out of the zoning state and
  381. // then rezone the combined area of all dirty zone spaces.
  382. //
  383. // Note that this path here is pretty much only relevant during loading
  384. // or editing and thus can be less performant than the path for individual
  385. // objects below.
  386. while( !mDirtyZoneSpaces.empty() )
  387. {
  388. SceneZoneSpace* zoneSpace = mDirtyZoneSpaces.last();
  389. mDirtyZoneSpaces.decrement();
  390. // Remove the zoning state of the object.
  391. _zoneRemove( zoneSpace );
  392. // Destroy all connections that this zone space has to
  393. // other zone spaces.
  394. zoneSpace->_disconnectAllZoneSpaces();
  395. // Nuke its zone lists.
  396. const U32 numZones = zoneSpace->getZoneRange();
  397. for( U32 n = 0; n < numZones; ++ n )
  398. _clearZoneList( zoneSpace->getZoneRangeStart() + n );
  399. // Merge into dirty region.
  400. mDirtyArea.intersect( zoneSpace->getWorldBox() );
  401. }
  402. if( mDirtyArea != Box3F::Invalid )
  403. {
  404. // Rezone everything in the dirty region.
  405. _rezoneObjects( mDirtyArea );
  406. mDirtyArea = Box3F::Invalid;
  407. // Verify zoning state.
  408. #ifdef DEBUG_VERIFY
  409. verifyState();
  410. #endif
  411. // Fire the zoning changed signal to let interested parties
  412. // know that the zoning setup of the scene has changed.
  413. getZoningChangedSignal().trigger( this );
  414. }
  415. // And finally, update objects that have changed state.
  416. while( !mDirtyObjects.empty() )
  417. {
  418. SceneObject* object = mDirtyObjects.last();
  419. mDirtyObjects.decrement();
  420. if( object->mZoneRefDirty )
  421. _rezoneObject( object );
  422. AssertFatal( !object->mZoneRefDirty, "SceneZoneSpaceManager::updateZoningState - Object still dirty!" );
  423. }
  424. AssertFatal( mDirtyObjects.empty(), "SceneZoneSpaceManager::updateZoningState - Still have dirty objects!" );
  425. AssertFatal( mDirtyZoneSpaces.empty(), "SceneZoneSpaceManager::updateZoningState - Still have dirty zones!" );
  426. }
  427. //-----------------------------------------------------------------------------
  428. void SceneZoneSpaceManager::_zoneInsert( SceneObject* object, bool queryListInitialized )
  429. {
  430. PROFILE_SCOPE( SceneZoneSpaceManager_zoneInsert );
  431. AssertFatal( object->mNumCurrZones == 0, "SceneZoneSpaceManager::_zoneInsert - Object already in zone list" );
  432. AssertFatal( object->getContainer() != NULL, "SceneZoneSpaceManager::_zoneInsert - Object must be in scene" );
  433. AssertFatal( !dynamic_cast< SceneRootZone* >( object ), "SceneZoneSpaceManager::_zoneInsert - Must not be called on SceneRootZone" );
  434. // If all we have is a single zone in the scene, then it must
  435. // be the outdoor zone. Simply assign the object to it. Also do this
  436. // if the object has global bounds on since we always assign these to
  437. // just the outdoor zone. Finally, also do it for all object types that
  438. // we want to restrict to the outdoor zone.
  439. if( mNumActiveZones == 1 || object->isGlobalBounds() || object->getTypeMask() & OUTDOOR_OBJECT_TYPEMASK )
  440. _addToOutdoorZone( object );
  441. else
  442. {
  443. // Otherwise find all zones spaces that intersect with the object's
  444. // world box.
  445. if( !queryListInitialized )
  446. _queryZoneSpaces( object->getWorldBox() );
  447. // Go through the zone spaces and link all zones that the object
  448. // overlaps.
  449. bool outsideIncluded = true;
  450. const U32 numZoneSpaces = mZoneSpacesQueryList.size();
  451. for( U32 i = 0; i < numZoneSpaces; ++ i )
  452. {
  453. SceneZoneSpace* zoneSpace = dynamic_cast< SceneZoneSpace* >( mZoneSpacesQueryList[ i ] );
  454. if( !zoneSpace )
  455. continue;
  456. AssertFatal( zoneSpace != getRootZone(), "SceneZoneSpaceManager::_zoneInsert - SceneRootZone returned by zone space query" );
  457. // If we are inserting a zone space, then the query will turn up
  458. // the object itself at some point. Skip it.
  459. if( zoneSpace == object )
  460. continue;
  461. // Find the zones that the object overlaps within
  462. // the zone space.
  463. U32 numZones = 0;
  464. U32 zones[ SceneObject::MaxObjectZones ];
  465. bool overlapsOutside = zoneSpace->getOverlappingZones( object, zones, numZones );
  466. AssertFatal( numZones != 0 || overlapsOutside,
  467. "SceneZoneSpaceManager::_zoneInsert - Object must be fully contained in one or more zones or intersect the outside zone" );
  468. outsideIncluded &= overlapsOutside; // Only include outside if *none* of the zones fully contains the object.
  469. // Link the object to the zones.
  470. for( U32 n = 0; n < numZones; ++ n )
  471. _addToZoneList( zones[ n ], object );
  472. // Let the zone manager know we have added objects to its
  473. // zones.
  474. if( numZones > 0 )
  475. zoneSpace->_onZoneAddObject( object, zones, numZones );
  476. }
  477. // If the object crosses into the outside zone or hasn't been
  478. // added to any zone above, add it to the outside zone.
  479. if( outsideIncluded )
  480. _addToOutdoorZone( object );
  481. }
  482. // Mark the zoning state of the object as current.
  483. object->mZoneRefDirty = false;
  484. }
  485. //-----------------------------------------------------------------------------
  486. void SceneZoneSpaceManager::_zoneRemove( SceneObject* obj )
  487. {
  488. PROFILE_SCOPE( SceneZoneSpaceManager_zoneRemove );
  489. // Remove the object from the zone lists.
  490. for( SceneObject::ZoneRef* walk = obj->mZoneRefHead; walk != NULL; )
  491. {
  492. // Let the zone owner know we are removing an object
  493. // from its zones.
  494. getZoneOwner( walk->zone )->_onZoneRemoveObject( walk->object );
  495. // Now remove the ZoneRef link this object has in the
  496. // zone list of the current zone.
  497. SceneObject::ZoneRef* remove = walk;
  498. walk = walk->nextInObj;
  499. remove->prevInBin->nextInBin = remove->nextInBin;
  500. if( remove->nextInBin )
  501. remove->nextInBin->prevInBin = remove->prevInBin;
  502. smZoneRefChunker.free( remove );
  503. }
  504. // Clear the object's zoning state.
  505. obj->mZoneRefHead = NULL;
  506. obj->mZoneRefDirty = false;
  507. obj->mNumCurrZones = 0;
  508. }
  509. //-----------------------------------------------------------------------------
  510. void SceneZoneSpaceManager::_addToZoneList( U32 zoneId, SceneObject* object )
  511. {
  512. SceneObject::ZoneRef* zoneList = mZoneLists[ zoneId ];
  513. AssertFatal( zoneList != NULL, "SceneZoneSpaceManager::_addToZoneList - Zone list not initialized" );
  514. AssertFatal( object != zoneList->object, "SCene::_addToZoneList - Cannot add zone to itself" );
  515. SceneObject::ZoneRef* newRef = smZoneRefChunker.alloc();
  516. // Add the object to the zone list.
  517. newRef->zone = zoneId;
  518. newRef->object = object;
  519. newRef->nextInBin = zoneList->nextInBin;
  520. newRef->prevInBin = zoneList;
  521. if( zoneList->nextInBin )
  522. zoneList->nextInBin->prevInBin = newRef;
  523. zoneList->nextInBin = newRef;
  524. // Add the zone to the object list.
  525. newRef->nextInObj = object->mZoneRefHead;
  526. object->mZoneRefHead = newRef;
  527. object->mNumCurrZones ++;
  528. }
  529. //-----------------------------------------------------------------------------
  530. void SceneZoneSpaceManager::_clearZoneList( U32 zoneId )
  531. {
  532. AssertFatal( zoneId < getNumZones(), "SceneZoneSpaceManager::_clearZoneList - Zone ID out of range" );
  533. SceneObject::ZoneRef* list = mZoneLists[ zoneId ];
  534. SceneZoneSpace* zoneSpace = getZoneOwner( zoneId );
  535. // Go through the objects in the zone list and unlink and
  536. // delete their zone entries.
  537. for( SceneObject::ZoneRef* walk = list->nextInBin; walk != NULL; walk = walk->nextInBin )
  538. {
  539. SceneObject* object = walk->object;
  540. AssertFatal( object != NULL, "SceneZoneSpaceManager::_clearZoneList - Object field not set on link" );
  541. // The zone entry links on the objects are singly-linked lists
  542. // linked through nextInObject so we need to find where in the
  543. // objects zone entry list the node for the current zone is.
  544. SceneObject::ZoneRef** ptrNext = &object->mZoneRefHead;
  545. while( *ptrNext && *ptrNext != walk )
  546. ptrNext = &( *ptrNext )->nextInObj;
  547. AssertFatal( *ptrNext == walk, "SceneZoneSpaceManager::_clearZoneList - Zone entry not found on object in zone list!");
  548. // Unlink and delete the entry.
  549. *ptrNext = ( *ptrNext )->nextInObj;
  550. smZoneRefChunker.free( walk );
  551. object->mNumCurrZones --;
  552. // If this is the only zone the object was in, mark
  553. // its zoning state as dirty so it will get assigned
  554. // to the outdoor zone on the next update.
  555. if( !object->mZoneRefHead )
  556. object->mZoneRefDirty = true;
  557. // Let the zone know we have removed the object.
  558. zoneSpace->_onZoneRemoveObject( object );
  559. }
  560. list->nextInBin = NULL;
  561. }
  562. //-----------------------------------------------------------------------------
  563. SceneObject::ZoneRef* SceneZoneSpaceManager::_findInZoneList( U32 zoneId, SceneObject* object ) const
  564. {
  565. for( SceneObject::ZoneRef* ref = object->mZoneRefHead; ref != NULL; ref = ref->nextInObj )
  566. if( ref->zone == zoneId )
  567. return ref;
  568. return NULL;
  569. }
  570. //-----------------------------------------------------------------------------
  571. void SceneZoneSpaceManager::_addToOutdoorZone( SceneObject* object )
  572. {
  573. AssertFatal( !object->mZoneRefHead || !_findInZoneList( RootZoneId, object ),
  574. "SceneZoneSpaceManager::_addToOutdoorZone - Object already added to outdoor zone" );
  575. // Add the object to the outside's zone list. This method is always called
  576. // *last* after the object has already been assigned to any other zone it
  577. // intersects. Since we always prepend to the zoning lists, this means that
  578. // the outdoor zone will always be *first* in the list of zones that an object
  579. // is assigned to which generally is a good order.
  580. _addToZoneList( RootZoneId, object );
  581. // Let the zone know we added an object to it.
  582. const U32 zoneId = RootZoneId;
  583. static_cast< SceneZoneSpace* >( getRootZone() )->_onZoneAddObject( object, &zoneId, 1 );
  584. }
  585. //-----------------------------------------------------------------------------
  586. void SceneZoneSpaceManager::_queryZoneSpaces( const Box3F& area ) const
  587. {
  588. mZoneSpacesQueryList.clear();
  589. mContainer->findObjectList( area, ZoneObjectType, &mZoneSpacesQueryList );
  590. }
  591. //-----------------------------------------------------------------------------
  592. void SceneZoneSpaceManager::dumpZoneStates( bool update )
  593. {
  594. if( update )
  595. _rezoneObjects( getRootZone()->getWorldBox() );
  596. const U32 numZoneSpaces = mZoneSpaces.size();
  597. for( U32 i = 0; i < numZoneSpaces; ++ i )
  598. mZoneSpaces[ i ]->dumpZoneState( false );
  599. }
  600. //-----------------------------------------------------------------------------
  601. void SceneZoneSpaceManager::verifyState()
  602. {
  603. AssertFatal( mZoneSpaces.size() <= mNumActiveZones,
  604. "SceneZoneSpaceManager::verifyState - More zone spaces than active zones!" );
  605. AssertFatal( mNumTotalAllocatedZones >= mNumActiveZones,
  606. "SceneZoneSpaceManager::verifyState - Fewer allocated than active zones!" );
  607. AssertFatal( mRootZone->getZoneRangeStart() == 0,
  608. "SceneZoneSpaceManager::verifyState - Invalid ID on root zone!" );
  609. AssertFatal( mRootZone->getZoneRange() == 1,
  610. "SceneZoneSpaceManager::verifyState - Invalid zone range on root zone!" );
  611. // First validate the zone spaces themselves.
  612. const U32 numZoneSpaces = mZoneSpaces.size();
  613. for( U32 i = 0; i < numZoneSpaces; ++ i )
  614. {
  615. SceneZoneSpace* space = mZoneSpaces[ i ];
  616. #ifndef TORQUE_DISABLE_MEMORY_MANAGER
  617. Memory::checkPtr( space );
  618. #endif
  619. AssertFatal( space->getTypeMask() & ZoneObjectType, "SceneZoneSpaceManager::verifyState - Zone space is not a ZoneObjectType!" );
  620. const U32 zoneRangeStart = space->getZoneRangeStart();
  621. const U32 numZones = space->getZoneRange();
  622. // Verify each of the allocated zones in this space.
  623. for( U32 n = 0; n < numZones; ++ n )
  624. {
  625. const U32 zoneId = zoneRangeStart + n;
  626. // Simple validation of zone ID.
  627. AssertFatal( isValidZoneId( zoneId ), "SceneZoneSpaceManager::verifyState - Zone space is assigned in invalid zone ID!" );
  628. AssertFatal( mZoneLists[ zoneId ] != NULL, "SceneZoneSpaceManager::verifyState - Zone list missing for zone!" );
  629. AssertFatal( mZoneLists[ zoneId ]->object == space, "SceneZoneSpaceManager::verifyState - Zone list entry #0 is not referring back to zone!" );
  630. for( SceneObject::ZoneRef* ref = mZoneLists[ zoneId ]; ref != NULL; ref = ref->nextInBin )
  631. {
  632. AssertFatal( ref->zone == zoneId, "SceneZoneSpaceManager::verifyState - Incorrect ID in zone list!" );
  633. AssertFatal( ref->object != NULL, "SceneZoneSpaceManager::verifyState - Null object pointer in zone list!" );
  634. #ifndef TORQUE_DISABLE_MEMORY_MANAGER
  635. Memory::checkPtr( ref->object );
  636. #endif
  637. }
  638. }
  639. // Make sure no other zone space owns any of the same IDs.
  640. for( U32 n = 0; n < numZoneSpaces; ++ n )
  641. {
  642. if( n == i )
  643. continue;
  644. SceneZoneSpace* otherSpace = mZoneSpaces[ n ];
  645. AssertFatal( otherSpace->getZoneRangeStart() >= zoneRangeStart + numZones ||
  646. otherSpace->getZoneRangeStart() + otherSpace->getZoneRange() <= zoneRangeStart,
  647. "SceneZoneSpaceManager::verifyState - Overlap between zone ID ranges of zone spaces!" );
  648. }
  649. // Make sure that all zone connections appear to be valid.
  650. for( SceneZoneSpace::ZoneSpaceRef* ref = space->mConnectedZoneSpaces; ref != NULL; ref = ref->mNext )
  651. {
  652. #ifndef TORQUE_DISABLE_MEMORY_MANAGER
  653. Memory::checkPtr( ref->mZoneSpace );
  654. #endif
  655. AssertFatal( _getZoneSpaceIndex( ref->mZoneSpace ) != -1, "SceneZoneSpaceManager::verifyState - Zone connected to invalid zone!" );
  656. AssertFatal( ref->mZoneSpace->getTypeMask() & ZoneObjectType, "SceneZoneSpaceManager::verifyState - Zone space is not a ZoneObjectType!" );
  657. }
  658. }
  659. //TODO: can do a lot more validation here
  660. }