Basic zone code refactor using lists instead of linked lists

This commit is contained in:
James Urquhart 2023-02-06 02:07:05 +00:00 committed by Brian Roberts
parent 7e5eacc43d
commit d9ff5d3f0e
8 changed files with 419 additions and 289 deletions

View file

@ -108,20 +108,31 @@ void Zone::selectWithin()
SceneZoneSpaceManager* zoneManager = zoneClient->getSceneManager()->getZoneManager(); SceneZoneSpaceManager* zoneManager = zoneClient->getSceneManager()->getZoneManager();
if (zoneManager) if (zoneManager)
{ {
SceneZoneSpaceManager::ObjectZoneValueIterator itr, itrEnd;
for (U32 zoneId = zoneClient->mZoneRangeStart; zoneId < zoneClient->mZoneRangeStart + zoneClient->mNumZones; ++zoneId) for (U32 zoneId = zoneClient->mZoneRangeStart; zoneId < zoneClient->mZoneRangeStart + zoneClient->mNumZones; ++zoneId)
for (SceneZoneSpaceManager::ZoneContentIterator iter(zoneManager, zoneId, false); iter.isValid(); ++iter) {
SceneZoneSpaceManager::ZoneObjectList* list = zoneManager->mZoneLists[zoneId];
for (SceneObject* zoneObject : list->getObjects())
{ {
SceneObject* obj = (SceneObject*)iter->getServerObject(); SceneObject* obj = (SceneObject*)zoneObject->getServerObject();
bool fullyEnclosed = true; bool fullyEnclosed = true;
for (SceneObject::ObjectZonesIterator zoneIter(obj); zoneIter.isValid(); ++zoneIter) zoneManager->getObjectZoneValueIterators(obj, itr, itrEnd);
{
if (*zoneIter != zoneId) for (itr; itr != itrEnd; itr++)
fullyEnclosed = false; {
if (*itr != zoneId)
{
fullyEnclosed = false;
break;
}
} }
if (fullyEnclosed) if (fullyEnclosed)
sql.insertObject(obj); sql.insertObject(obj);
} }
}
} }
} }

View file

@ -96,10 +96,20 @@ SceneCullingState::SceneCullingState( SceneManager* sceneManager, const SceneCam
bool SceneCullingState::isWithinVisibleZone( SceneObject* object ) const bool SceneCullingState::isWithinVisibleZone( SceneObject* object ) const
{ {
for( SceneObject::ZoneRef* ref = object->_getZoneRefHead(); SceneManager* mgr = object->getSceneManager();
ref != NULL; ref = ref->nextInObj ) SceneZoneSpaceManager* zm = mgr->getZoneManager();
if( mZoneVisibilityFlags.test( ref->zone ) )
U32 numZones = 0;
U32* zones = NULL;
SceneZoneSpaceManager::ObjectZoneValueIterator itr, itrEnd;
zm->getObjectZoneValueIterators(object, itr, itrEnd);
for (itr; itr != itrEnd; itr++)
{
if (mZoneVisibilityFlags.test(*itr))
return true; return true;
}
return false; return false;
} }
@ -148,8 +158,19 @@ void SceneCullingState::addOccluder( SceneObject* object )
// Add the frustum to all zones that the object is assigned to. // Add the frustum to all zones that the object is assigned to.
for( SceneObject::ZoneRef* ref = object->_getZoneRefHead(); ref != NULL; ref = ref->nextInObj ) U32 numZones = 0;
addCullingVolumeToZone( ref->zone, volume ); U32* zones = NULL;
//object->getSceneManager()->getZoneManager()
SceneManager* sm = object->getSceneManager();
SceneZoneSpaceManager* zm = sm->getZoneManager();
SceneZoneSpaceManager::ObjectZoneValueIterator itr, itrEnd;
zm->getObjectZoneValueIterators(object, itr, itrEnd);
for (itr; itr != itrEnd; itr++)
{
addCullingVolumeToZone(*itr, volume);
}
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -679,7 +700,7 @@ bool SceneCullingState::isOccluded( SceneObject* object ) const
CullingTestResult result = _testOccludersOnly( CullingTestResult result = _testOccludersOnly(
object->getWorldBox(), object->getWorldBox(),
SceneObject::ObjectZonesIterator( object ) mSceneManager->getZoneManager()->makeObjectZoneValueIterator(object)
); );
return ( result == SceneZoneCullingState::CullingTestPositiveByOcclusion ); return ( result == SceneZoneCullingState::CullingTestPositiveByOcclusion );
@ -719,6 +740,8 @@ U32 SceneCullingState::cullObjects( SceneObject** objects, U32 numObjects, U32 c
const PlaneF& nearPlane = getCullingFrustum().getPlanes()[ Frustum::PlaneNear ]; const PlaneF& nearPlane = getCullingFrustum().getPlanes()[ Frustum::PlaneNear ];
const PlaneF& farPlane = getCullingFrustum().getPlanes()[ Frustum::PlaneFar ]; const PlaneF& farPlane = getCullingFrustum().getPlanes()[ Frustum::PlaneFar ];
SceneZoneSpaceManager* zoneMgr = mSceneManager->getZoneManager();
for( U32 i = 0; i < numObjects; ++ i ) for( U32 i = 0; i < numObjects; ++ i )
{ {
SceneObject* object = objects[ i ]; SceneObject* object = objects[ i ];
@ -777,7 +800,7 @@ U32 SceneCullingState::cullObjects( SceneObject** objects, U32 numObjects, U32 c
{ {
CullingTestResult result = _test( CullingTestResult result = _test(
object->getWorldBox(), object->getWorldBox(),
SceneObject::ObjectZonesIterator( object ), zoneMgr->makeObjectZoneValueIterator( object ),
nearPlane, nearPlane,
farPlane farPlane
); );

View file

@ -96,7 +96,7 @@ public:
return mItems == NULL; return mItems == NULL;
} }
inline T* getPtr() inline T* getPtr() const
{ {
return mItems; return mItems;
} }
@ -150,6 +150,67 @@ public:
CompactionThreshold = 4096 CompactionThreshold = 4096
}; };
struct ValueIterator
{
T* binList;
BinCount numElements;
BinCount currentElement;
ValueIterator() : binList(NULL), numElements(0), currentElement(0) { ; }
ValueIterator(T* list, U32 count, U32 idx=0) : binList(list), numElements(count), currentElement(idx)
{
}
inline T operator*()
{
return binList[currentElement];
}
inline bool isValid() const
{
return currentElement < numElements;
}
inline ValueIterator& operator++()
{
if (currentElement < numElements)
currentElement++;
return *this;
}
inline ValueIterator& operator++(int other)
{
currentElement += other;
currentElement = mMin(currentElement, numElements);
return *this;
}
inline ValueIterator& operator+(const U32 other)
{
currentElement += other;
currentElement = mMin(currentElement, numElements);
return *this;
}
inline ValueIterator& operator=(const ValueIterator& other)
{
binList = other.binList;
numElements = other.numElements;
currentElement = other.currentElement;
return *this;
}
inline bool operator==(const ValueIterator& other) const
{
return binList == other.binList && currentElement == other.currentElement;
}
inline bool operator!=(const ValueIterator& other) const
{
return !(binList == other.binList && currentElement == other.currentElement);
}
};
protected: protected:
#pragma pack(2) #pragma pack(2)
@ -228,6 +289,35 @@ public:
return mBinValues.getPtr() + list.startValue; return mBinValues.getPtr() + list.startValue;
} }
void getValueIterators(ListHandle handle, ValueIterator& start, ValueIterator& end)
{
if (handle == 0)
{
start = ValueIterator(NULL, 0);
end = ValueIterator(NULL, 0);
return;
}
U32 realIDX = handle - 1;
BinList& list = mBinLists[realIDX];
start = ValueIterator(mBinValues.getPtr() + list.startValue, list.numValues, 0);
end = ValueIterator(mBinValues.getPtr() + list.startValue, list.numValues, list.numValues);
}
ValueIterator getValueIterator(ListHandle handle)
{
if (handle == 0)
{
return ValueIterator(NULL, 0);
}
U32 realIDX = handle - 1;
BinList& list = mBinLists[realIDX];
return ValueIterator(mBinValues.getPtr() + list.startValue, list.numValues);
}
protected: protected:
/// Gets a free entry from the free entry list. /// Gets a free entry from the free entry list.
@ -250,7 +340,7 @@ public:
{ {
BinList list; BinList list;
ListHandle retHandle = 0; ListHandle retHandle = 0;
list.numValues = numValues; list.numValues = numValues;
list.startValue = mLastValueIdx; list.startValue = mLastValueIdx;
@ -296,7 +386,7 @@ public:
list.numValues = numValues; list.numValues = numValues;
list.startValue = mLastValueIdx; list.startValue = mLastValueIdx;
mLastValueIdx += numValues; mLastValueIdx += numValues;
mBinValues.realloc(mLastValueIdx, false); mBinValues.realloc(mLastValueIdx, false);
} }
@ -330,6 +420,41 @@ public:
} }
} }
void replaceListBin(ListHandle handle, BinValue oldValue, BinValue newValue)
{
if (handle == 0)
return;
U32 realIDX = handle - 1;
BinList& list = mBinLists[realIDX];
BinValue* values = mBinValues.getPtr() + list.startValue;
for (U32 i = 0; i < list.numValues; i++)
{
if (values[i] == oldValue)
values[i] = newValue;
break;
}
}
bool containsBinItem(ListHandle handle, BinValue value) const
{
if (handle == 0)
return false;
U32 realIDX = handle - 1;
const BinList& list = mBinLists[realIDX];
const BinValue* values = mBinValues.getPtr() + list.startValue;
for (U32 i = 0; i < list.numValues; i++)
{
if (values[i] == value)
return true;
}
return false;
}
/// Compacts the BinValue lists. /// Compacts the BinValue lists.
/// This will automatically be called by freeList usually /// This will automatically be called by freeList usually
/// once CompactionThreshold list values have been freed. /// once CompactionThreshold list values have been freed.

View file

@ -128,8 +128,8 @@ SceneObject::SceneObject()
mSceneManager = NULL; mSceneManager = NULL;
mZoneListHandle = 0;
mNumCurrZones = 0; mNumCurrZones = 0;
mZoneRefHead = NULL;
mZoneRefDirty = false; mZoneRefDirty = false;
mLightPlugin = NULL; mLightPlugin = NULL;
@ -168,7 +168,7 @@ SceneObject::~SceneObject()
{ {
AssertFatal(mContainer == NULL, AssertFatal(mContainer == NULL,
"SceneObject::~SceneObject - Object still in container!"); "SceneObject::~SceneObject - Object still in container!");
AssertFatal( mZoneRefHead == NULL, AssertFatal( mZoneListHandle == NULL,
"SceneObject::~SceneObject - Object still linked in reference lists!"); "SceneObject::~SceneObject - Object still linked in reference lists!");
AssertFatal( !mSceneObjectLinks, AssertFatal( !mSceneObjectLinks,
"SceneObject::~SceneObject() - object is still linked to SceneTrackers" ); "SceneObject::~SceneObject() - object is still linked to SceneTrackers" );
@ -1024,7 +1024,7 @@ void SceneObject::unpackUpdate( NetConnection* conn, BitStream* stream )
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
void SceneObject::_updateZoningState() const void SceneObject::_updateZoningState()
{ {
if( mZoneRefDirty ) if( mZoneRefDirty )
{ {
@ -1038,21 +1038,18 @@ void SceneObject::_updateZoningState() const
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
U32 SceneObject::getCurrZone( const U32 index ) const U32 SceneObject::getCurrZone( const U32 index )
{ {
SceneZoneSpaceManager* manager = getSceneManager()->getZoneManager();
_updateZoningState(); _updateZoningState();
// Not the most efficient way to do this, walking the list, // Not the most efficient way to do this, walking the list,
// but it's an uncommon call... // but it's an uncommon call...
ZoneRef* walk = mZoneRefHead; U32 numZones = 0;
for( U32 i = 0; i < index; ++ i ) U32* zones = NULL;
{ zones = manager->getZoneIDS(this, numZones);
walk = walk->nextInObj;
AssertFatal( walk != NULL, "SceneObject::_getCurrZone - Too few object refs!" );
}
AssertFatal( walk != NULL, "SceneObject::_getCurrZone - Too few object refs!" );
return walk->zone; return index < numZones ? zones[index] : 0;
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------

View file

@ -224,77 +224,20 @@ class SceneObject : public NetObject, public ProcessObject
/// @name Zoning /// @name Zoning
/// @{ /// @{
/// Bidirectional link between a zone manager and its objects.
struct ZoneRef
{
/// ID of zone.
U32 zone;
/// Object that is referenced in the link.
SceneObject* object;
/// Next link in chain of container.
ZoneRef* nextInBin;
/// Previous link in chain of container.
ZoneRef* prevInBin;
/// Next link in chain that is associated with #object.
ZoneRef* nextInObj;
};
/// Iterator over the zones that the object is assigned to.
/// @note This iterator expects a clean zoning state. It will not update the
/// zoning state in case it is dirty.
struct ObjectZonesIterator
{
ObjectZonesIterator( SceneObject* object )
: mCurrent( object->_getZoneRefHead() ) {}
bool isValid() const
{
return ( mCurrent != NULL );
}
ObjectZonesIterator& operator ++()
{
AssertFatal( isValid(), "SceneObject::ObjectZonesIterator::operator++ - Invalid iterator!" );
mCurrent = mCurrent->nextInObj;
return *this;
}
U32 operator *() const
{
AssertFatal( isValid(), "SceneObject::ObjectZonesIterator::operator* - Invalid iterator!" );
return mCurrent->zone;
}
private:
ZoneRef* mCurrent;
};
friend struct ObjectZonesIterator;
/// If an object moves, its zoning state needs to be updated. This is deferred /// If an object moves, its zoning state needs to be updated. This is deferred
/// to when the state is actually needed and this flag indicates a refresh /// to when the state is actually needed and this flag indicates a refresh
/// is necessary. /// is necessary.
mutable bool mZoneRefDirty; bool mZoneRefDirty;
/// Number of zones this object is assigned to. /// Number of zones this object is assigned to.
/// @note If #mZoneRefDirty is set, this might be outdated. /// @note If #mZoneRefDirty is set, this might be outdated.
mutable U32 mNumCurrZones; U32 mNumCurrZones;
/// List of zones that this object is part of. /// Handle for the zone list of this object
/// @note If #mZoneRefDirty is set, this might be outdated. U32 mZoneListHandle;
mutable ZoneRef* mZoneRefHead;
/// Refresh the zoning state of this object, if it isn't up-to-date anymore. /// Refresh the zoning state of this object, if it isn't up-to-date anymore.
void _updateZoningState() const; void _updateZoningState();
/// Return the first link in the zone list of this object. Each link represents
/// a single zone that the object is assigned to.
///
/// @note This method will return the zoning list as is. In case the zoning state
/// of the object is dirty, the list contents may be outdated.
ZoneRef* _getZoneRefHead() const { return mZoneRefHead; }
/// @} /// @}
@ -763,7 +706,7 @@ class SceneObject : public NetObject, public ProcessObject
U32 getNumCurrZones() const { return mNumCurrZones; } U32 getNumCurrZones() const { return mNumCurrZones; }
/// Returns the nth zone containing this object. /// Returns the nth zone containing this object.
U32 getCurrZone(const U32 index) const; U32 getCurrZone(const U32 index);
/// @} /// @}

View file

@ -348,7 +348,8 @@ void SceneZoneSpace::dumpZoneState( bool update )
Con::printf( "--- Zone %i", zoneId ); Con::printf( "--- Zone %i", zoneId );
for( SceneZoneSpaceManager::ZoneContentIterator iter( mManager, zoneId, false ); iter.isValid(); ++ iter ) SceneZoneSpaceManager::ZoneObjectList* list = mManager->mZoneLists[zoneId];
for( SceneObject* iter : list->getObjects() )
Con::printf( iter->describeSelf() ); Con::printf( iter->describeSelf() );
} }
} }

View file

@ -37,10 +37,6 @@
//#define DEBUG_SPEW //#define DEBUG_SPEW
ClassChunker< SceneObject::ZoneRef > SceneZoneSpaceManager::smZoneRefChunker;
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
SceneZoneSpaceManager::SceneZoneSpaceManager( SceneContainer* container ) SceneZoneSpaceManager::SceneZoneSpaceManager( SceneContainer* container )
@ -89,15 +85,7 @@ void SceneZoneSpaceManager::registerZones( SceneZoneSpace* object, U32 numZones
mZoneLists.increment( numZones ); mZoneLists.increment( numZones );
for( U32 i = zoneRangeStart; i < mNumTotalAllocatedZones; ++ i ) for( U32 i = zoneRangeStart; i < mNumTotalAllocatedZones; ++ i )
{ {
SceneObject::ZoneRef* zoneRef = smZoneRefChunker.alloc(); mZoneLists[i] = _allocZoneList(object);
zoneRef->object = object;
zoneRef->nextInBin = NULL;
zoneRef->prevInBin = NULL;
zoneRef->nextInObj = NULL;
zoneRef->zone = i;
mZoneLists[ i ] = zoneRef;
} }
// Add space to list. // Add space to list.
@ -143,14 +131,12 @@ void SceneZoneSpaceManager::unregisterZones( SceneZoneSpace* object )
for( U32 j = zoneRangeStart; j < zoneRangeStart + numZones; j ++ ) for( U32 j = zoneRangeStart; j < zoneRangeStart + numZones; j ++ )
{ {
// Delete all object links. ZoneObjectList* list = mZoneLists[j];
// Delete all object links.
_clearZoneList( j ); _clearZoneList( j );
// Delete the first link which refers to the zone itself. _freeZoneList(list);
smZoneRefChunker.free( mZoneLists[ j ] );
mZoneLists[ j ] = NULL;
} }
// Destroy the connections the zone space has. // Destroy the connections the zone space has.
@ -219,7 +205,7 @@ void SceneZoneSpaceManager::_compactZonesCheck()
const U32 numZoneSpaces = mZoneSpaces.size(); const U32 numZoneSpaces = mZoneSpaces.size();
U32 nextZoneId = 0; U32 nextZoneId = 0;
Vector< SceneObject::ZoneRef* > newZoneLists; Vector< ZoneObjectList* > newZoneLists;
newZoneLists.setSize( mNumActiveZones ); newZoneLists.setSize( mNumActiveZones );
for( U32 i = 0; i < numZoneSpaces; ++ i ) for( U32 i = 0; i < numZoneSpaces; ++ i )
@ -247,10 +233,15 @@ void SceneZoneSpaceManager::_compactZonesCheck()
newZoneLists[ newZoneId ] = mZoneLists[ oldZoneId ]; newZoneLists[ newZoneId ] = mZoneLists[ oldZoneId ];
if (mZoneLists[ newZoneId ] == NULL)
continue;
// Update entries. // Update entries.
for( SceneObject::ZoneRef* ref = newZoneLists[ newZoneId ]; ref != NULL; ref = ref->nextInBin ) for (SceneObject* obj : mZoneLists[ newZoneId ]->getObjects())
ref->zone = newZoneId; {
mObjectZoneLists.replaceListBin(obj->mZoneListHandle, oldZoneId, newZoneId);
}
} }
} }
@ -415,6 +406,9 @@ void SceneZoneSpaceManager::_rezoneObject( SceneObject* object )
_queryZoneSpaces( object->getWorldBox() ); _queryZoneSpaces( object->getWorldBox() );
U32 numZones = 0;
U32* zones = mObjectZoneLists.getValues(object->mZoneListHandle, numZones);
const U32 numZoneSpaces = mZoneSpacesQueryList.size(); const U32 numZoneSpaces = mZoneSpacesQueryList.size();
if( !numZoneSpaces ) if( !numZoneSpaces )
{ {
@ -422,8 +416,7 @@ void SceneZoneSpaceManager::_rezoneObject( SceneObject* object )
// root zone, then we don't need an update. Otherwise, we do. // root zone, then we don't need an update. Otherwise, we do.
if( object->mNumCurrZones == 1 && if( object->mNumCurrZones == 1 &&
object->mZoneRefHead && zones[0] == RootZoneId )
object->mZoneRefHead->zone == RootZoneId )
{ {
object->mZoneRefDirty = false; object->mZoneRefDirty = false;
return; return;
@ -610,64 +603,99 @@ void SceneZoneSpaceManager::_zoneInsert( SceneObject* object, bool queryListInit
// just the outdoor zone. Finally, also do it for all object types that // just the outdoor zone. Finally, also do it for all object types that
// we want to restrict to the outdoor zone. // we want to restrict to the outdoor zone.
if( mNumActiveZones == 1 || object->isGlobalBounds() || object->getTypeMask() & OUTDOOR_OBJECT_TYPEMASK ) bool outsideOnly = mNumActiveZones == 1 || object->isGlobalBounds() || (object->getTypeMask() & OUTDOOR_OBJECT_TYPEMASK);
_addToOutdoorZone( object ); U32 numGlobalZones = 0;
else U32 remainingZones = SceneObject::MaxObjectZones;
U32 globalZones[SceneObject::MaxObjectZones];
if (!outsideOnly)
{ {
// Otherwise find all zones spaces that intersect with the object's // Otherwise find all zones spaces that intersect with the object's
// world box. // world box.
if( !queryListInitialized ) if (!queryListInitialized)
_queryZoneSpaces( object->getWorldBox() ); _queryZoneSpaces(object->getWorldBox());
// Go through the zone spaces and link all zones that the object // Go through the zone spaces and link all zones that the object
// overlaps. // overlaps.
bool outsideIncluded = true; bool outsideIncluded = true;
const U32 numZoneSpaces = mZoneSpacesQueryList.size(); const U32 numZoneSpaces = mZoneSpacesQueryList.size();
for( U32 i = 0; i < numZoneSpaces; ++ i )
mTempObjectZones.clear();
mTempObjectZones.reserve(numZoneSpaces);
for (U32 i = 0; i < numZoneSpaces; ++i)
{ {
SceneZoneSpace* zoneSpace = dynamic_cast< SceneZoneSpace* >( mZoneSpacesQueryList[ i ] ); SceneZoneSpace* zoneSpace = dynamic_cast<SceneZoneSpace*>(mZoneSpacesQueryList[i]);
if( !zoneSpace ) if (!zoneSpace)
continue; continue;
AssertFatal( zoneSpace != getRootZone(), "SceneZoneSpaceManager::_zoneInsert - SceneRootZone returned by zone space query" ); AssertFatal(zoneSpace != getRootZone(), "SceneZoneSpaceManager::_zoneInsert - SceneRootZone returned by zone space query");
// If we are inserting a zone space, then the query will turn up // If we are inserting a zone space, then the query will turn up
// the object itself at some point. Skip it. // the object itself at some point. Skip it.
if( zoneSpace == object ) if (zoneSpace == object)
continue; continue;
// Find the zones that the object overlaps within // Find the zones that the object overlaps within
// the zone space. // the zone space.
U32 numZones = 0; U32 numZones = 0;
U32 zones[ SceneObject::MaxObjectZones ]; U32 zones[SceneObject::MaxObjectZones];
bool overlapsOutside = zoneSpace->getOverlappingZones(object, zones, numZones);
bool overlapsOutside = zoneSpace->getOverlappingZones( object, zones, numZones ); AssertFatal(numZones != 0 || overlapsOutside,
AssertFatal( numZones != 0 || overlapsOutside, "SceneZoneSpaceManager::_zoneInsert - Object must be fully contained in one or more zones or intersect the outside zone");
"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. outsideIncluded &= overlapsOutside; // Only include outside if *none* of the zones fully contains the object.
// Link the object to the zones. // Clamp the zone count
numZones = getMin(remainingZones, numZones);
for( U32 n = 0; n < numZones; ++ n ) if (numZones > 0)
_addToZoneList( zones[ n ], object ); {
// Add to temp list
TempZoneRecord zoneRecord;
zoneRecord.numZones = numZones;
zoneRecord.space = zoneSpace;
zoneRecord.startZone = numGlobalZones;
dCopyArray(globalZones + numGlobalZones, zones, numZones);
// Let the zone manager know we have added objects to its mTempObjectZones.push_back(zoneRecord);
// zones. zoneSpace->_onZoneAddObject(object, zones + zoneRecord.startZone, numZones);
if( numZones > 0 ) numGlobalZones += zoneRecord.numZones;
zoneSpace->_onZoneAddObject( object, zones, numZones ); remainingZones -= zoneRecord.numZones;
}
} }
// If the object crosses into the outside zone or hasn't been // If the object crosses into the outside zone or hasn't been
// added to any zone above, add it to the outside zone. // added to any zone above, add it to the outside zone.
if( outsideIncluded ) if (outsideOnly || (outsideIncluded && remainingZones > 0))
_addToOutdoorZone( object ); {
TempZoneRecord zoneRecord;
zoneRecord.numZones = 1;
zoneRecord.space = static_cast<SceneZoneSpace*>(getRootZone());
zoneRecord.startZone = numGlobalZones;
globalZones[numGlobalZones++] = RootZoneId;
mTempObjectZones.push_back(zoneRecord);
}
}
for (TempZoneRecord record : mTempObjectZones)
{
// Let the zone manager know we have added objects to its
// zones.
_setObjectZoneList(object, numGlobalZones, globalZones);
if (record.numZones > 0)
{
record.space->_onZoneAddObject(object, globalZones + record.startZone, record.numZones);
}
} }
// Mark the zoning state of the object as current. // Mark the zoning state of the object as current.
@ -679,65 +707,76 @@ void SceneZoneSpaceManager::_zoneInsert( SceneObject* object, bool queryListInit
void SceneZoneSpaceManager::_zoneRemove( SceneObject* obj ) void SceneZoneSpaceManager::_zoneRemove( SceneObject* obj )
{ {
if (obj->mZoneListHandle == 0)
return;
PROFILE_SCOPE( SceneZoneSpaceManager_zoneRemove ); PROFILE_SCOPE( SceneZoneSpaceManager_zoneRemove );
// Remove the object from the zone lists. // Remove the object from the zone lists.
for( SceneObject::ZoneRef* walk = obj->mZoneRefHead; walk != NULL; ) U32 numZones = 0;
U32* zones = NULL;
zones = mObjectZoneLists.getValues(obj->mZoneListHandle, numZones);
for (U32 i=0; i<numZones; i++)
{ {
// Let the zone owner know we are removing an object // Let the zone owner know we are removing an object
// from its zones. // from its zones.
getZoneOwner( walk->zone )->_onZoneRemoveObject( walk->object ); getZoneOwner( zones[i] )->_onZoneRemoveObject(obj);
// 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. // Clear the object's zoning state.
obj->mZoneRefHead = NULL; mObjectZoneLists.freeList(obj->mZoneListHandle);
obj->mZoneListHandle = 0;
obj->mZoneRefDirty = false; obj->mZoneRefDirty = false;
obj->mNumCurrZones = 0; obj->mNumCurrZones = 0;
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
void SceneZoneSpaceManager::_addToZoneList( U32 zoneId, SceneObject* object ) /// Realloc zoning state to the given object.
void SceneZoneSpaceManager::_zoneRealloc(SceneObject* object, bool queryListInitialized)
{ {
SceneObject::ZoneRef* zoneList = mZoneLists[ zoneId ]; if (object->mZoneListHandle == 0)
return _zoneInsert(object, queryListInitialized);
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; void SceneZoneSpaceManager::_setObjectZoneList( SceneObject* object, U32 numZones, U32* zoneList )
newRef->object = object; {
newRef->nextInBin = zoneList->nextInBin; #ifdef TORQUE_ENABLE_ASSERTS
newRef->prevInBin = zoneList; SceneZoneSpace* zoneSpace = dynamic_cast<SceneZoneSpace*>(object);
if (zoneSpace)
{
for (U32 i = 0; i < numZones; i++)
{
bool inRange = zoneList[i] >= zoneSpace->mZoneRangeStart && zoneList[i] < (zoneSpace->mZoneRangeStart+zoneSpace->mNumZones);
AssertFatal(!inRange, "SCene::_addToZoneList - Cannot add zone to itself");
}
}
#endif
if( zoneList->nextInBin ) // Alloc or re-use entry
zoneList->nextInBin->prevInBin = newRef;
zoneList->nextInBin = newRef; if (object->mZoneListHandle == 0)
{
// Add the zone to the object list. object->mZoneListHandle = mObjectZoneLists.allocList(numZones, zoneList);
}
newRef->nextInObj = object->mZoneRefHead; else if (numZones == 0)
object->mZoneRefHead = newRef; {
object->mNumCurrZones ++; mObjectZoneLists.freeList(object->mZoneListHandle);
object->mZoneListHandle = 0;
}
else
{
mObjectZoneLists.reallocList(object->mZoneListHandle, numZones, zoneList);
}
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -746,31 +785,16 @@ void SceneZoneSpaceManager::_clearZoneList( U32 zoneId )
{ {
AssertFatal( zoneId < getNumZones(), "SceneZoneSpaceManager::_clearZoneList - Zone ID out of range" ); AssertFatal( zoneId < getNumZones(), "SceneZoneSpaceManager::_clearZoneList - Zone ID out of range" );
SceneObject::ZoneRef* list = mZoneLists[ zoneId ]; ZoneObjectList* list = mZoneLists[zoneId];
SceneZoneSpace* zoneSpace = getZoneOwner( zoneId ); SceneZoneSpace* zoneSpace = getZoneOwner( zoneId );
// Go through the objects in the zone list and unlink and // Go through the objects in the zone list and unlink and
// delete their zone entries. // delete their zone entries.
for( SceneObject::ZoneRef* walk = list->nextInBin; walk != NULL; walk = walk->nextInBin ) for( SceneObject* object : list->getObjects() )
{ {
SceneObject* object = walk->object;
AssertFatal( object != NULL, "SceneZoneSpaceManager::_clearZoneList - Object field not set on link" ); AssertFatal( object != NULL, "SceneZoneSpaceManager::_clearZoneList - Object field not set on link" );
AssertFatal( object->mNumCurrZones > 0, "SceneZoneSpaceManager::_clearZoneList - Bad reference count" );
// 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 --; object->mNumCurrZones --;
@ -778,47 +802,30 @@ void SceneZoneSpaceManager::_clearZoneList( U32 zoneId )
// its zoning state as dirty so it will get assigned // its zoning state as dirty so it will get assigned
// to the outdoor zone on the next update. // to the outdoor zone on the next update.
if( !object->mZoneRefHead ) if( object->mNumCurrZones == 0 )
object->mZoneRefDirty = true; object->mZoneRefDirty = true;
// Let the zone know we have removed the object. // Let the zone know we have removed the object.
zoneSpace->_onZoneRemoveObject( object ); zoneSpace->_onZoneRemoveObject( object );
} }
list->nextInBin = NULL;
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
SceneObject::ZoneRef* SceneZoneSpaceManager::_findInZoneList( U32 zoneId, SceneObject* object ) const bool SceneZoneSpaceManager::_isInZoneList( U32 zoneId, SceneObject* object ) const
{ {
for( SceneObject::ZoneRef* ref = object->mZoneRefHead; ref != NULL; ref = ref->nextInObj ) SceneZoneSpaceManager::ZoneObjectList* list = mZoneLists[zoneId];
if( ref->zone == zoneId ) if (list == NULL)
return ref; return false;
return NULL; for (SceneObject* obj : list->getObjects())
} {
if (obj == object)
return true;
}
//----------------------------------------------------------------------------- return false;
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 );
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -880,12 +887,11 @@ void SceneZoneSpaceManager::verifyState()
AssertFatal( isValidZoneId( zoneId ), "SceneZoneSpaceManager::verifyState - Zone space is assigned in invalid 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 ] != 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!" ); AssertFatal( mZoneLists[ zoneId ]->mManager == space, "SceneZoneSpaceManager::verifyState - Zone list entry #0 is not referring back to zone!" );
for( SceneObject::ZoneRef* ref = mZoneLists[ zoneId ]; ref != NULL; ref = ref->nextInBin ) for( SceneObject* object : mZoneLists[ zoneId ]->getObjects() )
{ {
AssertFatal( ref->zone == zoneId, "SceneZoneSpaceManager::verifyState - Incorrect ID in zone list!" ); AssertFatal( mObjectZoneLists.containsBinItem(object->mZoneListHandle, zoneId), "SceneZoneSpaceManager::verifyState - Object doesn't have zone in list!");
AssertFatal( ref->object != NULL, "SceneZoneSpaceManager::verifyState - Null object pointer in zone list!" );
#ifndef TORQUE_DISABLE_MEMORY_MANAGER #ifndef TORQUE_DISABLE_MEMORY_MANAGER
Memory::checkPtr( ref->object ); Memory::checkPtr( ref->object );
@ -921,3 +927,32 @@ void SceneZoneSpaceManager::verifyState()
//TODO: can do a lot more validation here //TODO: can do a lot more validation here
} }
//-----------------------------------------------------------------------------
SceneZoneSpaceManager::ZoneObjectList* SceneZoneSpaceManager::_allocZoneList(SceneZoneSpace* space)
{
SceneZoneSpaceManager::ZoneObjectList* ret = NULL;
if (!mZoneListPool.empty())
{
ret = mZoneListPool.last();
ret->mManager = space;
mZoneListPool.pop_back();
}
else
{
ret = new SceneZoneSpaceManager::ZoneObjectList(space);
}
return ret;
}
//-----------------------------------------------------------------------------
void SceneZoneSpaceManager::_freeZoneList(SceneZoneSpaceManager::ZoneObjectList* list)
{
list->mManager = NULL;
list->getObjects().clear();
mZoneListPool.push_back(list);
}

View file

@ -39,22 +39,24 @@
#include "core/dataChunker.h" #include "core/dataChunker.h"
#endif #endif
#ifndef _SCENECONTAINER_H_
#include "scene/sceneContainer.h"
#endif
class SceneContainer; class SceneContainer;
class SceneRootZone; class SceneRootZone;
class SceneZoneSpace; class SceneZoneSpace;
class Zone;
/// Object that manages zone spaces in a scene. /// Object that manages zone spaces in a scene.
class SceneZoneSpaceManager class SceneZoneSpaceManager
{ {
public: public:
class ZoneContentIterator;
friend class SceneZoneSpace; // mZoneLists friend class SceneZoneSpace; // mZoneLists
friend class ZoneContentIterator; // mZoneLists friend class Zone;
/// A signal used to notify that the zone setup of the scene has changed. /// A signal used to notify that the zone setup of the scene has changed.
/// ///
@ -65,6 +67,10 @@ class SceneZoneSpaceManager
/// of time. /// of time.
typedef Signal< void( SceneZoneSpaceManager* ) > ZoningChangedSignal; typedef Signal< void( SceneZoneSpaceManager* ) > ZoningChangedSignal;
typedef SceneContainerBinRefList<U32> ZoneValueList;
typedef SceneContainerBinRefList<U32>::ValueIterator ObjectZoneValueIterator;
enum : U32 enum : U32
{ {
/// Zone ID of the exterior zone. /// Zone ID of the exterior zone.
@ -74,64 +80,21 @@ class SceneZoneSpaceManager
InvalidZoneId = 0xFFFFFFFF, InvalidZoneId = 0xFFFFFFFF,
}; };
/// Iterator for the contents of a given zone. class ZoneObjectList
class ZoneContentIterator
{ {
public: protected:
ZoneContentIterator( SceneZoneSpaceManager* manager, S32 zoneId, bool upToDate = true ) friend class SceneZoneSpaceManager;
{
AssertFatal( zoneId < manager->getNumZones(), "SceneZoneSpaceManager::ZoneContentIterator - Zone ID out of range" );
if( upToDate ) SceneObject* mManager;
{ Vector<SceneObject*> mObjects;
// Since zoning is updated lazily, the zone contents may actually
// be out of date. Force an update by triggering rezoning on the
// zone object. This is brute-force but this iterator is not meant
// to be used for high-frequency code anyway.
//
// Use the area-based rezoning so that we can also properly iterate
// over the contents of SceneRootZone.
manager->_rezoneObjects( ( ( SceneObject* ) manager->getZoneOwner( zoneId ) )->getWorldBox() );
}
mCurrent = manager->mZoneLists[ zoneId ]->nextInBin; // Skip zone object entry. public:
}
bool isValid() const ZoneObjectList(SceneObject* manager) : mManager(manager) { ; }
{
return ( mCurrent != NULL );
}
bool operator !() const
{
return ( mCurrent == NULL );
}
ZoneContentIterator& operator ++()
{
if( mCurrent )
mCurrent = mCurrent->nextInBin;
return *this;
}
ZoneContentIterator& operator --()
{
if( mCurrent )
mCurrent = mCurrent->prevInBin;
return *this;
}
SceneObject* operator *() const
{
AssertFatal( mCurrent != NULL, "SceneManager::ZoneContentIterator::operator* - Invalid iterator" );
return mCurrent->object;
}
SceneObject* operator ->() const
{
AssertFatal( mCurrent != NULL, "SceneManager::ZoneContentIterator::operator-> - Invalid iterator" );
return mCurrent->object;
}
private: inline SceneObject* getManager() const { return mManager; }
inline Vector<SceneObject*> getObjects() const { return mObjects; }
SceneObject::ZoneRef* mCurrent;
}; };
protected: protected:
@ -153,13 +116,25 @@ class SceneZoneSpaceManager
/// Object list for each zone in the scene. /// Object list for each zone in the scene.
/// First entry in the list points back to the zone manager. /// First entry in the list points back to the zone manager.
Vector< SceneObject::ZoneRef* > mZoneLists; Vector< ZoneObjectList* > mZoneLists;
/// Free zone pool
Vector< ZoneObjectList* > mZoneListPool;
/// Pool for allocating object zone lists
ZoneValueList mObjectZoneLists;
/// Vector used repeatedly for zone space queries on the container. /// Vector used repeatedly for zone space queries on the container.
mutable Vector< SceneObject* > mZoneSpacesQueryList; mutable Vector< SceneObject* > mZoneSpacesQueryList;
/// Allocator for ZoneRefs. struct TempZoneRecord
static ClassChunker< SceneObject::ZoneRef > smZoneRefChunker; {
SceneZoneSpace* space;
U32 startZone;
U32 numZones;
};
Vector<TempZoneRecord> mTempObjectZones;
/// @name Dirty Lists /// @name Dirty Lists
/// Updating the zoning state of a scene is done en block rather than /// Updating the zoning state of a scene is done en block rather than
@ -194,8 +169,11 @@ class SceneZoneSpaceManager
/// Detach zoning state from the given object. /// Detach zoning state from the given object.
void _zoneRemove( SceneObject* object ); void _zoneRemove( SceneObject* object );
/// Add to given object to the zone list of the given zone. /// Realloc zoning state to the given object.
void _addToZoneList( U32 zoneId, SceneObject* object ); void _zoneRealloc(SceneObject* object, bool queryListInitialized = false);
/// Sets the entire zone list for an object
void _setObjectZoneList( SceneObject* object, U32 numZones, U32* zoneList );
/// Clear all objects assigned to the given zone. /// Clear all objects assigned to the given zone.
/// @note This does not remove the first link in the zone list which is the link /// @note This does not remove the first link in the zone list which is the link
@ -203,10 +181,7 @@ class SceneZoneSpaceManager
void _clearZoneList( U32 zoneId ); void _clearZoneList( U32 zoneId );
/// Find the given object in the zone list of the given zone. /// Find the given object in the zone list of the given zone.
SceneObject::ZoneRef* _findInZoneList( U32 zoneId, SceneObject* object ) const; bool _isInZoneList( U32 zoneId, SceneObject* object ) const;
/// Assign the given object to the outdoor zone.
void _addToOutdoorZone( SceneObject* object );
/// Rezone all objects in the given area. /// Rezone all objects in the given area.
void _rezoneObjects( const Box3F& area ); void _rezoneObjects( const Box3F& area );
@ -217,6 +192,10 @@ class SceneZoneSpaceManager
/// Fill #mZoneSpacesQueryList with all ZoneObjectType objects in the given area. /// Fill #mZoneSpacesQueryList with all ZoneObjectType objects in the given area.
void _queryZoneSpaces( const Box3F& area ) const; void _queryZoneSpaces( const Box3F& area ) const;
ZoneObjectList* _allocZoneList(SceneZoneSpace* space);
void _freeZoneList(ZoneObjectList* list);
public: public:
SceneZoneSpaceManager( SceneContainer* container ); SceneZoneSpaceManager( SceneContainer* container );
@ -277,7 +256,8 @@ class SceneZoneSpaceManager
SceneZoneSpace* getZoneOwner( const U32 zoneId ) const SceneZoneSpace* getZoneOwner( const U32 zoneId ) const
{ {
AssertFatal( isValidZoneId( zoneId ), "SceneManager::getZoneOwner - Invalid zone ID!"); AssertFatal( isValidZoneId( zoneId ), "SceneManager::getZoneOwner - Invalid zone ID!");
return ( SceneZoneSpace* ) mZoneLists[ zoneId ]->object; ZoneObjectList* list = mZoneLists[zoneId];
return ( SceneZoneSpace* )(list ? list->getManager() : NULL);
} }
/// Return the total number of zones in the scene. /// Return the total number of zones in the scene.
@ -325,6 +305,21 @@ class SceneZoneSpaceManager
/// @} /// @}
inline U32* getZoneIDS(SceneObject* object, U32& numZones)
{
return mObjectZoneLists.getValues(object->mZoneListHandle, numZones);
}
ObjectZoneValueIterator makeObjectZoneValueIterator(SceneObject* obj)
{
return mObjectZoneLists.getValueIterator(obj->mZoneListHandle);
}
void getObjectZoneValueIterators(SceneObject* obj, ObjectZoneValueIterator& start, ObjectZoneValueIterator& end)
{
mObjectZoneLists.getValueIterators(obj->mZoneListHandle, start, end);
}
/// @} /// @}
}; };