Torque3D Documentation / _generateds / sceneZoneSpaceManager.cpp

sceneZoneSpaceManager.cpp

Engine/source/scene/zones/sceneZoneSpaceManager.cpp

More...

Detailed Description

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