2012-09-19 15:15:01 +00:00
//-----------------------------------------------------------------------------
// 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 )
2017-04-11 04:45:02 +00:00
: mRootZone ( new SceneRootZone ( ) ) ,
mContainer ( container ) ,
2012-09-19 15:15:01 +00:00
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
}