123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923 |
- //-----------------------------------------------------------------------------
- // Copyright (c) 2012 GarageGames, LLC
- //
- // Permission is hereby granted, free of charge, to any person obtaining a copy
- // of this software and associated documentation files (the "Software"), to
- // deal in the Software without restriction, including without limitation the
- // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
- // sell copies of the Software, and to permit persons to whom the Software is
- // furnished to do so, subject to the following conditions:
- //
- // The above copyright notice and this permission notice shall be included in
- // all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- // IN THE SOFTWARE.
- //-----------------------------------------------------------------------------
- #include "platform/platform.h"
- #include "scene/zones/sceneZoneSpaceManager.h"
- #include "platform/profiler.h"
- #include "platform/platformMemory.h"
- #include "scene/sceneContainer.h"
- #include "scene/zones/sceneRootZone.h"
- #include "scene/zones/sceneZoneSpace.h"
- // Uncomment to enable verification code for debugging. This slows the
- // manager down significantly but will allow to find zoning state corruption
- // much quicker.
- //#define DEBUG_VERIFY
- //#define DEBUG_SPEW
- ClassChunker< SceneObject::ZoneRef > SceneZoneSpaceManager::smZoneRefChunker;
- //-----------------------------------------------------------------------------
- SceneZoneSpaceManager::SceneZoneSpaceManager( SceneContainer* container )
- : mRootZone( new SceneRootZone() ),
- mContainer( container ),
- mNumTotalAllocatedZones( 0 ),
- mNumActiveZones( 0 ),
- mDirtyArea( Box3F::Invalid )
- {
- VECTOR_SET_ASSOCIATION( mZoneSpaces );
- VECTOR_SET_ASSOCIATION( mZoneLists );
- VECTOR_SET_ASSOCIATION( mZoneSpacesQueryList );
- VECTOR_SET_ASSOCIATION( mDirtyObjects );
- VECTOR_SET_ASSOCIATION( mDirtyZoneSpaces );
- }
- //-----------------------------------------------------------------------------
- SceneZoneSpaceManager::~SceneZoneSpaceManager()
- {
- // Delete root zone.
- SAFE_DELETE( mRootZone );
- mNumTotalAllocatedZones = 0;
- mNumActiveZones = 0;
- }
- //-----------------------------------------------------------------------------
- void SceneZoneSpaceManager::registerZones( SceneZoneSpace* object, U32 numZones )
- {
- AssertFatal( _getZoneSpaceIndex( object ) == -1, "SceneZoneSpaceManager::registerZones - Object already registered" );
- _compactZonesCheck();
- const U32 zoneRangeStart = mNumTotalAllocatedZones;
- mNumTotalAllocatedZones += numZones;
- mNumActiveZones += numZones;
- object->mNumZones = numZones;
- object->mZoneRangeStart = zoneRangeStart;
- // Allocate zone lists for all of the zones managed by the object.
- // Add an entry to each list that points back to the zone space.
- mZoneLists.increment( numZones );
- for( U32 i = zoneRangeStart; i < mNumTotalAllocatedZones; ++ i )
- {
- SceneObject::ZoneRef* zoneRef = smZoneRefChunker.alloc();
- zoneRef->object = object;
- zoneRef->nextInBin = NULL;
- zoneRef->prevInBin = NULL;
- zoneRef->nextInObj = NULL;
- zoneRef->zone = i;
- mZoneLists[ i ] = zoneRef;
- }
- // Add space to list.
- mZoneSpaces.push_back( object );
- object->mManager = this;
- // Set ZoneObjectType.
- object->mTypeMask |= ZoneObjectType;
- // Put the object on the dirty list.
- if( !object->isRootZone() )
- {
- // Make sure the object gets on the zone space list even
- // if it is already on the object dirty list.
- object->mZoneRefDirty = false;
- notifyObjectChanged( object );
- }
- #ifdef DEBUG_SPEW
- Platform::outputDebugString( "[SceneZoneSpaceManager] Range %i-%i allocated to: %s",
- zoneRangeStart, numZones, object->describeSelf().c_str() );
- #endif
- }
- //-----------------------------------------------------------------------------
- void SceneZoneSpaceManager::unregisterZones( SceneZoneSpace* object )
- {
- S32 zoneSpaceIndex = _getZoneSpaceIndex( object );
-
- AssertFatal( zoneSpaceIndex != -1, "SceneZoneSpaceManager::unregisterZones - Object not registered as zone space" );
- AssertFatal( mNumActiveZones >= object->mNumZones, "SceneZoneSpaceManager::unregisterZones - Too many zones removed");
- const U32 zoneRangeStart = object->getZoneRangeStart();
- const U32 numZones = object->getZoneRange();
- // Destroy the zone lists for the zones registered
- // by the object.
- for( U32 j = zoneRangeStart; j < zoneRangeStart + numZones; j ++ )
- {
- // Delete all object links.
- _clearZoneList( j );
- // Delete the first link which refers to the zone itself.
- smZoneRefChunker.free( mZoneLists[ j ] );
- mZoneLists[ j ] = NULL;
- }
- // Destroy the connections the zone space has.
- object->_disconnectAllZoneSpaces();
- // Remove the zone manager entry.
- mNumActiveZones -= numZones;
- mZoneSpaces.erase( zoneSpaceIndex );
- // Clear ZoneObjectType.
- object->mTypeMask &= ~ZoneObjectType;
- // Clear zone assignments.
- object->mZoneRangeStart = InvalidZoneId;
- object->mNumZones = 0;
- object->mManager = NULL;
- // Mark the zone space's area as dirty.
- mDirtyArea.intersect( object->getWorldBox() );
- #ifdef DEBUG_SPEW
- Platform::outputDebugString( "[SceneZoneSpaceManager] Range %i-%i released from: %s",
- zoneRangeStart, numZones, object->describeSelf().c_str() );
- #endif
- }
- //-----------------------------------------------------------------------------
- void SceneZoneSpaceManager::_rezoneObjects( const Box3F& area )
- {
- static Vector< SceneObject* > sObjects( __FILE__, __LINE__ );
- // Find all objects in the area. We cannot use the callback
- // version here and directly trigger rezoning since the rezoning
- // itself does a container query.
- sObjects.clear();
- mContainer->findObjectList( area, 0xFFFFFFFF, &sObjects );
- // Rezone the objects.
- const U32 numObjects = sObjects.size();
- for( U32 i = 0; i < numObjects; ++ i )
- {
- SceneObject* object = sObjects[ i ];
- if( object != getRootZone() )
- _rezoneObject( object );
- }
- }
- //-----------------------------------------------------------------------------
- void SceneZoneSpaceManager::_compactZonesCheck()
- {
- if( mNumActiveZones > ( mNumTotalAllocatedZones / 2 ) )
- return;
- // Redistribute the zone IDs among the current zone spaces
- // so that the range of IDs is consecutive.
- const U32 numZoneSpaces = mZoneSpaces.size();
- U32 nextZoneId = 0;
-
- Vector< SceneObject::ZoneRef* > newZoneLists;
- newZoneLists.setSize( mNumActiveZones );
- for( U32 i = 0; i < numZoneSpaces; ++ i )
- {
- SceneZoneSpace* space = mZoneSpaces[ i ];
- const U32 oldZoneRangeStart = space->getZoneRangeStart();
- const U32 newZoneRangeStart = nextZoneId;
- const U32 numZones = space->getZoneRange();
- // Assign the new zone range start.
- space->mZoneRangeStart = newZoneRangeStart;
- nextZoneId += numZones;
- // Relocate the zone lists to match the new zone IDs and update
- // the contents of the zone lists to match the new IDs.
- for( U32 n = 0; n < numZones; ++ n )
- {
- const U32 newZoneId = newZoneRangeStart + n;
- const U32 oldZoneId = oldZoneRangeStart + n;
- // Relocate list.
- newZoneLists[ newZoneId ] = mZoneLists[ oldZoneId ];
- // Update entries.
- for( SceneObject::ZoneRef* ref = newZoneLists[ newZoneId ]; ref != NULL; ref = ref->nextInBin )
- ref->zone = newZoneId;
- }
- }
- mNumTotalAllocatedZones = nextZoneId;
- mZoneLists = newZoneLists;
- AssertFatal( mNumTotalAllocatedZones == mNumActiveZones, "SceneZoneSpaceManager::_compactZonesCheck - Error during compact; mismatch between active and allocated zones" );
- }
- //-----------------------------------------------------------------------------
- S32 SceneZoneSpaceManager::_getZoneSpaceIndex( SceneZoneSpace* object ) const
- {
- const U32 numZoneSpaces = getNumZoneSpaces();
- for( U32 i = 0; i < numZoneSpaces; ++ i )
- if( mZoneSpaces[ i ] == object )
- return i;
- return -1;
- }
- //-----------------------------------------------------------------------------
- void SceneZoneSpaceManager::findZone( const Point3F& p, SceneZoneSpace*& owner, U32& zone ) const
- {
- AssertFatal( mNumActiveZones >= 1, "SceneZoneSpaceManager::findZone - Must have at least one active zone in scene (outdoor zone)" );
- // If there are no zones in the level other than the outdoor
- // zone, just return that.
- if( mNumActiveZones == 1 )
- {
- owner = getRootZone();
- zone = RootZoneId;
- return;
- }
-
- PROFILE_SCOPE( SceneZoneSpaceManager_findZone );
- // Query the scene container for zones with a query
- // box that tightly fits around the point.
- Box3F queryBox( p.x - 0.1f, p.y - 0.1f, p.z - 0.1f,
- p.x + 0.1f, p.y + 0.1f, p.z + 0.1f );
- _queryZoneSpaces( queryBox );
- // Go through the zones and look for the first one that
- // contains the given point.
- const U32 numZones = mZoneSpacesQueryList.size();
- for( U32 i = 0; i < numZones; ++ i )
- {
- SceneZoneSpace* zoneSpace = dynamic_cast< SceneZoneSpace* >( mZoneSpacesQueryList[ i ] );
- if( !zoneSpace )
- continue;
- AssertFatal( zoneSpace != getRootZone(), "SceneZoneSpaceManager::findZone - SceneRootZone returned by zone manager query" );
- // If the point is in one of the zones of this manager,
- // then make this the result.
- U32 inZone = zoneSpace->getPointZone( p );
- if( inZone != InvalidZoneId )
- {
- owner = zoneSpace;
- zone = inZone;
- return;
- }
- }
- // No other zone matched so return the outdoor zone.
- owner = getRootZone();
- zone = RootZoneId;
- }
- //-----------------------------------------------------------------------------
- U32 SceneZoneSpaceManager::findZones( const Box3F& area, Vector< U32 >& outZones ) const
- {
- // Query all zone spaces in the area.
- _queryZoneSpaces( area );
- // Query each zone space for overlaps with the given
- // area and add the zones to outZones.
- bool outsideIncluded = false;
- U32 numTotalZones = 0;
- const U32 numZoneSpaces = mZoneSpacesQueryList.size();
- for( U32 i = 0; i < numZoneSpaces; ++ i )
- {
- SceneZoneSpace* zoneSpace = dynamic_cast< SceneZoneSpace* >( mZoneSpacesQueryList[ i ] );
- if( !zoneSpace )
- continue;
- AssertFatal( zoneSpace != getRootZone(), "SceneZoneSpaceManager::findZones - SceneRootZone returned by zone manager query" );
- // Query manager.
- U32 zones[ SceneObject::MaxObjectZones ];
- U32 numZones = 0;
- outsideIncluded |= zoneSpace->getOverlappingZones( area, zones, numZones );
- // Add overlapped zones.
-
- for( U32 n = 0; n < numZones; n ++ )
- {
- outZones.push_back( zones[ n ] );
- numTotalZones ++;
- }
- }
- // If the area box wasn't fully enclosed by the zones of the
- // manager(s) or the query only returned the outside zone,
- // add the outside zone to the list.
- if( outsideIncluded || numTotalZones == 0 )
- {
- outZones.push_back( RootZoneId );
- numTotalZones ++;
- }
- return numTotalZones;
- }
- //-----------------------------------------------------------------------------
- void SceneZoneSpaceManager::_rezoneObject( SceneObject* object )
- {
- PROFILE_SCOPE( SceneZoneSpaceManager_rezoneObject );
- AssertFatal( !dynamic_cast< SceneRootZone* >( object ), "SceneZoneSpaceManager::_rezoneObject - Cannot rezone the SceneRootZone!" );
- // If the object is not yet assigned to zones,
- // do so now and return.
- if( !object->mNumCurrZones )
- {
- _zoneInsert( object );
- return;
- }
- // If we have no zones in the scene other than the outdoor zone or if the
- // object has global bounds on (and thus is always in the outdoor zone) or
- // is an object that is restricted to the outdoor zone, leave the object's
- // zoning state untouched.
- if( mNumActiveZones == 1 || object->isGlobalBounds() || object->getTypeMask() & OUTDOOR_OBJECT_TYPEMASK )
- {
- object->mZoneRefDirty = false;
- return;
- }
- // First, find out whether there's even a chance of the zoning to have changed
- // for the object.
- _queryZoneSpaces( object->getWorldBox() );
- const U32 numZoneSpaces = mZoneSpacesQueryList.size();
- if( !numZoneSpaces )
- {
- // There is no zone in the object's area. If it is already assigned to the
- // root zone, then we don't need an update. Otherwise, we do.
- if( object->mNumCurrZones == 1 &&
- object->mZoneRefHead &&
- object->mZoneRefHead->zone == RootZoneId )
- {
- object->mZoneRefDirty = false;
- return;
- }
- }
- // Update the object's zoning information by removing and recomputing
- // its zoning information.
- _zoneRemove( object );
- _zoneInsert( object, true ); // Query already in place.
- }
- //-----------------------------------------------------------------------------
- void SceneZoneSpaceManager::registerObject( SceneObject* object )
- {
- // Just put it on the dirty list.
- notifyObjectChanged( object );
- }
- //-----------------------------------------------------------------------------
- void SceneZoneSpaceManager::unregisterObject( SceneObject* object )
- {
- // Remove from dirty list.
- mDirtyObjects.remove( object );
- // Remove from zone lists.
- _zoneRemove( object );
- // If it's a zone space, unregister it.
- if( object->getTypeMask() & ZoneObjectType && dynamic_cast< SceneZoneSpace* >( object ) )
- {
- SceneZoneSpace* zoneSpace = static_cast< SceneZoneSpace* >( object );
- unregisterZones( zoneSpace );
- mDirtyZoneSpaces.remove( zoneSpace );
- }
- }
- //-----------------------------------------------------------------------------
- void SceneZoneSpaceManager::updateObject( SceneObject* object )
- {
- // If no zone spaces have changed and the object's zoning
- // state is clean, then there's nothing to do for this object.
- if( mDirtyZoneSpaces.empty() && !object->mZoneRefDirty )
- return;
- // Otherwise update all the dirty zoning state.
- updateZoningState();
- }
- //-----------------------------------------------------------------------------
- void SceneZoneSpaceManager::notifyObjectChanged( SceneObject* object )
- {
- AssertFatal( object != getRootZone(), "SceneZoneSpaceManager::notifyObjectChanged - Cannot dirty root zone!" );
- // Ignore if object is already on the dirty list.
- if( object->mZoneRefDirty )
- return;
- // Put the object on the respective dirty list.
- if( object->getTypeMask() & ZoneObjectType )
- {
- SceneZoneSpace* zoneSpace = dynamic_cast< SceneZoneSpace* >( object );
- AssertFatal( zoneSpace != NULL, "SceneZoneSpaceManager::notifyObjectChanged - ZoneObjectType is not a SceneZoneSpace!" );
- if( zoneSpace )
- mDirtyZoneSpaces.push_back( zoneSpace );
- }
- else
- {
- mDirtyObjects.push_back( object );
- }
- // Mark object as dirty.
- object->mZoneRefDirty = true;
- }
- //-----------------------------------------------------------------------------
- void SceneZoneSpaceManager::updateZoningState()
- {
- // If there are no dirty objects, there's nothing to do.
- if( mDirtyObjects.empty() &&
- mDirtyZoneSpaces.empty() &&
- mDirtyArea == Box3F::Invalid )
- return;
- // Otherwise, first update the zone spaces. Do this in two passes:
- // first take all the dirty zone spaces out of the zoning state and
- // then rezone the combined area of all dirty zone spaces.
- //
- // Note that this path here is pretty much only relevant during loading
- // or editing and thus can be less performant than the path for individual
- // objects below.
- while( !mDirtyZoneSpaces.empty() )
- {
- SceneZoneSpace* zoneSpace = mDirtyZoneSpaces.last();
- mDirtyZoneSpaces.decrement();
-
- // Remove the zoning state of the object.
- _zoneRemove( zoneSpace );
- // Destroy all connections that this zone space has to
- // other zone spaces.
- zoneSpace->_disconnectAllZoneSpaces();
- // Nuke its zone lists.
- const U32 numZones = zoneSpace->getZoneRange();
- for( U32 n = 0; n < numZones; ++ n )
- _clearZoneList( zoneSpace->getZoneRangeStart() + n );
- // Merge into dirty region.
- mDirtyArea.intersect( zoneSpace->getWorldBox() );
- }
- if( mDirtyArea != Box3F::Invalid )
- {
- // Rezone everything in the dirty region.
- _rezoneObjects( mDirtyArea );
- mDirtyArea = Box3F::Invalid;
- // Verify zoning state.
- #ifdef DEBUG_VERIFY
- verifyState();
- #endif
- // Fire the zoning changed signal to let interested parties
- // know that the zoning setup of the scene has changed.
- getZoningChangedSignal().trigger( this );
- }
- // And finally, update objects that have changed state.
- while( !mDirtyObjects.empty() )
- {
- SceneObject* object = mDirtyObjects.last();
- mDirtyObjects.decrement();
- if( object->mZoneRefDirty )
- _rezoneObject( object );
- AssertFatal( !object->mZoneRefDirty, "SceneZoneSpaceManager::updateZoningState - Object still dirty!" );
- }
- AssertFatal( mDirtyObjects.empty(), "SceneZoneSpaceManager::updateZoningState - Still have dirty objects!" );
- AssertFatal( mDirtyZoneSpaces.empty(), "SceneZoneSpaceManager::updateZoningState - Still have dirty zones!" );
- }
- //-----------------------------------------------------------------------------
- void SceneZoneSpaceManager::_zoneInsert( SceneObject* object, bool queryListInitialized )
- {
- PROFILE_SCOPE( SceneZoneSpaceManager_zoneInsert );
- AssertFatal( object->mNumCurrZones == 0, "SceneZoneSpaceManager::_zoneInsert - Object already in zone list" );
- AssertFatal( object->getContainer() != NULL, "SceneZoneSpaceManager::_zoneInsert - Object must be in scene" );
- AssertFatal( !dynamic_cast< SceneRootZone* >( object ), "SceneZoneSpaceManager::_zoneInsert - Must not be called on SceneRootZone" );
- // If all we have is a single zone in the scene, then it must
- // be the outdoor zone. Simply assign the object to it. Also do this
- // if the object has global bounds on since we always assign these to
- // just the outdoor zone. Finally, also do it for all object types that
- // we want to restrict to the outdoor zone.
- if( mNumActiveZones == 1 || object->isGlobalBounds() || object->getTypeMask() & OUTDOOR_OBJECT_TYPEMASK )
- _addToOutdoorZone( object );
- else
- {
- // Otherwise find all zones spaces that intersect with the object's
- // world box.
- if( !queryListInitialized )
- _queryZoneSpaces( object->getWorldBox() );
- // Go through the zone spaces and link all zones that the object
- // overlaps.
- bool outsideIncluded = true;
- const U32 numZoneSpaces = mZoneSpacesQueryList.size();
- for( U32 i = 0; i < numZoneSpaces; ++ i )
- {
- SceneZoneSpace* zoneSpace = dynamic_cast< SceneZoneSpace* >( mZoneSpacesQueryList[ i ] );
- if( !zoneSpace )
- continue;
- AssertFatal( zoneSpace != getRootZone(), "SceneZoneSpaceManager::_zoneInsert - SceneRootZone returned by zone space query" );
- // If we are inserting a zone space, then the query will turn up
- // the object itself at some point. Skip it.
- if( zoneSpace == object )
- continue;
- // Find the zones that the object overlaps within
- // the zone space.
- U32 numZones = 0;
- U32 zones[ SceneObject::MaxObjectZones ];
- bool overlapsOutside = zoneSpace->getOverlappingZones( object, zones, numZones );
- AssertFatal( numZones != 0 || overlapsOutside,
- "SceneZoneSpaceManager::_zoneInsert - Object must be fully contained in one or more zones or intersect the outside zone" );
- outsideIncluded &= overlapsOutside; // Only include outside if *none* of the zones fully contains the object.
- // Link the object to the zones.
- for( U32 n = 0; n < numZones; ++ n )
- _addToZoneList( zones[ n ], object );
- // Let the zone manager know we have added objects to its
- // zones.
- if( numZones > 0 )
- zoneSpace->_onZoneAddObject( object, zones, numZones );
- }
- // If the object crosses into the outside zone or hasn't been
- // added to any zone above, add it to the outside zone.
- if( outsideIncluded )
- _addToOutdoorZone( object );
- }
- // Mark the zoning state of the object as current.
- object->mZoneRefDirty = false;
- }
- //-----------------------------------------------------------------------------
- void SceneZoneSpaceManager::_zoneRemove( SceneObject* obj )
- {
- PROFILE_SCOPE( SceneZoneSpaceManager_zoneRemove );
- // Remove the object from the zone lists.
- for( SceneObject::ZoneRef* walk = obj->mZoneRefHead; walk != NULL; )
- {
- // Let the zone owner know we are removing an object
- // from its zones.
- getZoneOwner( walk->zone )->_onZoneRemoveObject( walk->object );
- // Now remove the ZoneRef link this object has in the
- // zone list of the current zone.
- SceneObject::ZoneRef* remove = walk;
- walk = walk->nextInObj;
- remove->prevInBin->nextInBin = remove->nextInBin;
- if( remove->nextInBin )
- remove->nextInBin->prevInBin = remove->prevInBin;
- smZoneRefChunker.free( remove );
- }
- // Clear the object's zoning state.
- obj->mZoneRefHead = NULL;
- obj->mZoneRefDirty = false;
- obj->mNumCurrZones = 0;
- }
- //-----------------------------------------------------------------------------
- void SceneZoneSpaceManager::_addToZoneList( U32 zoneId, SceneObject* object )
- {
- SceneObject::ZoneRef* zoneList = mZoneLists[ zoneId ];
- AssertFatal( zoneList != NULL, "SceneZoneSpaceManager::_addToZoneList - Zone list not initialized" );
- AssertFatal( object != zoneList->object, "SCene::_addToZoneList - Cannot add zone to itself" );
- SceneObject::ZoneRef* newRef = smZoneRefChunker.alloc();
- // Add the object to the zone list.
- newRef->zone = zoneId;
- newRef->object = object;
- newRef->nextInBin = zoneList->nextInBin;
- newRef->prevInBin = zoneList;
- if( zoneList->nextInBin )
- zoneList->nextInBin->prevInBin = newRef;
- zoneList->nextInBin = newRef;
- // Add the zone to the object list.
- newRef->nextInObj = object->mZoneRefHead;
- object->mZoneRefHead = newRef;
- object->mNumCurrZones ++;
- }
- //-----------------------------------------------------------------------------
- void SceneZoneSpaceManager::_clearZoneList( U32 zoneId )
- {
- AssertFatal( zoneId < getNumZones(), "SceneZoneSpaceManager::_clearZoneList - Zone ID out of range" );
- SceneObject::ZoneRef* list = mZoneLists[ zoneId ];
- SceneZoneSpace* zoneSpace = getZoneOwner( zoneId );
- // Go through the objects in the zone list and unlink and
- // delete their zone entries.
- for( SceneObject::ZoneRef* walk = list->nextInBin; walk != NULL; walk = walk->nextInBin )
- {
- SceneObject* object = walk->object;
- AssertFatal( object != NULL, "SceneZoneSpaceManager::_clearZoneList - Object field not set on link" );
- // The zone entry links on the objects are singly-linked lists
- // linked through nextInObject so we need to find where in the
- // objects zone entry list the node for the current zone is.
- SceneObject::ZoneRef** ptrNext = &object->mZoneRefHead;
- while( *ptrNext && *ptrNext != walk )
- ptrNext = &( *ptrNext )->nextInObj;
- AssertFatal( *ptrNext == walk, "SceneZoneSpaceManager::_clearZoneList - Zone entry not found on object in zone list!");
- // Unlink and delete the entry.
- *ptrNext = ( *ptrNext )->nextInObj;
- smZoneRefChunker.free( walk );
- object->mNumCurrZones --;
- // If this is the only zone the object was in, mark
- // its zoning state as dirty so it will get assigned
- // to the outdoor zone on the next update.
- if( !object->mZoneRefHead )
- object->mZoneRefDirty = true;
- // Let the zone know we have removed the object.
- zoneSpace->_onZoneRemoveObject( object );
- }
- list->nextInBin = NULL;
- }
- //-----------------------------------------------------------------------------
- SceneObject::ZoneRef* SceneZoneSpaceManager::_findInZoneList( U32 zoneId, SceneObject* object ) const
- {
- for( SceneObject::ZoneRef* ref = object->mZoneRefHead; ref != NULL; ref = ref->nextInObj )
- if( ref->zone == zoneId )
- return ref;
- return NULL;
- }
- //-----------------------------------------------------------------------------
- void SceneZoneSpaceManager::_addToOutdoorZone( SceneObject* object )
- {
- AssertFatal( !object->mZoneRefHead || !_findInZoneList( RootZoneId, object ),
- "SceneZoneSpaceManager::_addToOutdoorZone - Object already added to outdoor zone" );
- // Add the object to the outside's zone list. This method is always called
- // *last* after the object has already been assigned to any other zone it
- // intersects. Since we always prepend to the zoning lists, this means that
- // the outdoor zone will always be *first* in the list of zones that an object
- // is assigned to which generally is a good order.
- _addToZoneList( RootZoneId, object );
- // Let the zone know we added an object to it.
- const U32 zoneId = RootZoneId;
- static_cast< SceneZoneSpace* >( getRootZone() )->_onZoneAddObject( object, &zoneId, 1 );
- }
- //-----------------------------------------------------------------------------
- void SceneZoneSpaceManager::_queryZoneSpaces( const Box3F& area ) const
- {
- mZoneSpacesQueryList.clear();
- mContainer->findObjectList( area, ZoneObjectType, &mZoneSpacesQueryList );
- }
- //-----------------------------------------------------------------------------
- void SceneZoneSpaceManager::dumpZoneStates( bool update )
- {
- if( update )
- _rezoneObjects( getRootZone()->getWorldBox() );
- const U32 numZoneSpaces = mZoneSpaces.size();
- for( U32 i = 0; i < numZoneSpaces; ++ i )
- mZoneSpaces[ i ]->dumpZoneState( false );
- }
- //-----------------------------------------------------------------------------
- void SceneZoneSpaceManager::verifyState()
- {
- AssertFatal( mZoneSpaces.size() <= mNumActiveZones,
- "SceneZoneSpaceManager::verifyState - More zone spaces than active zones!" );
- AssertFatal( mNumTotalAllocatedZones >= mNumActiveZones,
- "SceneZoneSpaceManager::verifyState - Fewer allocated than active zones!" );
- AssertFatal( mRootZone->getZoneRangeStart() == 0,
- "SceneZoneSpaceManager::verifyState - Invalid ID on root zone!" );
- AssertFatal( mRootZone->getZoneRange() == 1,
- "SceneZoneSpaceManager::verifyState - Invalid zone range on root zone!" );
- // First validate the zone spaces themselves.
- const U32 numZoneSpaces = mZoneSpaces.size();
- for( U32 i = 0; i < numZoneSpaces; ++ i )
- {
- SceneZoneSpace* space = mZoneSpaces[ i ];
- #ifndef TORQUE_DISABLE_MEMORY_MANAGER
- Memory::checkPtr( space );
- #endif
- AssertFatal( space->getTypeMask() & ZoneObjectType, "SceneZoneSpaceManager::verifyState - Zone space is not a ZoneObjectType!" );
- const U32 zoneRangeStart = space->getZoneRangeStart();
- const U32 numZones = space->getZoneRange();
- // Verify each of the allocated zones in this space.
- for( U32 n = 0; n < numZones; ++ n )
- {
- const U32 zoneId = zoneRangeStart + n;
- // Simple validation of zone ID.
- AssertFatal( isValidZoneId( zoneId ), "SceneZoneSpaceManager::verifyState - Zone space is assigned in invalid zone ID!" );
- AssertFatal( mZoneLists[ zoneId ] != NULL, "SceneZoneSpaceManager::verifyState - Zone list missing for zone!" );
- AssertFatal( mZoneLists[ zoneId ]->object == space, "SceneZoneSpaceManager::verifyState - Zone list entry #0 is not referring back to zone!" );
- for( SceneObject::ZoneRef* ref = mZoneLists[ zoneId ]; ref != NULL; ref = ref->nextInBin )
- {
- AssertFatal( ref->zone == zoneId, "SceneZoneSpaceManager::verifyState - Incorrect ID in zone list!" );
- AssertFatal( ref->object != NULL, "SceneZoneSpaceManager::verifyState - Null object pointer in zone list!" );
- #ifndef TORQUE_DISABLE_MEMORY_MANAGER
- Memory::checkPtr( ref->object );
- #endif
- }
- }
- // Make sure no other zone space owns any of the same IDs.
- for( U32 n = 0; n < numZoneSpaces; ++ n )
- {
- if( n == i )
- continue;
- SceneZoneSpace* otherSpace = mZoneSpaces[ n ];
- AssertFatal( otherSpace->getZoneRangeStart() >= zoneRangeStart + numZones ||
- otherSpace->getZoneRangeStart() + otherSpace->getZoneRange() <= zoneRangeStart,
- "SceneZoneSpaceManager::verifyState - Overlap between zone ID ranges of zone spaces!" );
- }
- // Make sure that all zone connections appear to be valid.
- for( SceneZoneSpace::ZoneSpaceRef* ref = space->mConnectedZoneSpaces; ref != NULL; ref = ref->mNext )
- {
- #ifndef TORQUE_DISABLE_MEMORY_MANAGER
- Memory::checkPtr( ref->mZoneSpace );
- #endif
- AssertFatal( _getZoneSpaceIndex( ref->mZoneSpace ) != -1, "SceneZoneSpaceManager::verifyState - Zone connected to invalid zone!" );
- AssertFatal( ref->mZoneSpace->getTypeMask() & ZoneObjectType, "SceneZoneSpaceManager::verifyState - Zone space is not a ZoneObjectType!" );
- }
- }
- //TODO: can do a lot more validation here
- }
|