From d9ff5d3f0e0ddabd7c314a9a537b20f5c29c9551 Mon Sep 17 00:00:00 2001 From: James Urquhart Date: Mon, 6 Feb 2023 02:07:05 +0000 Subject: [PATCH] Basic zone code refactor using lists instead of linked lists --- Engine/source/T3D/zone.cpp | 23 +- .../scene/culling/sceneCullingState.cpp | 37 ++- Engine/source/scene/sceneContainer.h | 131 +++++++- Engine/source/scene/sceneObject.cpp | 21 +- Engine/source/scene/sceneObject.h | 69 +--- Engine/source/scene/zones/sceneZoneSpace.cpp | 3 +- .../scene/zones/sceneZoneSpaceManager.cpp | 297 ++++++++++-------- .../scene/zones/sceneZoneSpaceManager.h | 127 ++++---- 8 files changed, 419 insertions(+), 289 deletions(-) diff --git a/Engine/source/T3D/zone.cpp b/Engine/source/T3D/zone.cpp index 97078d5ec..59c8cc77d 100644 --- a/Engine/source/T3D/zone.cpp +++ b/Engine/source/T3D/zone.cpp @@ -108,20 +108,31 @@ void Zone::selectWithin() SceneZoneSpaceManager* zoneManager = zoneClient->getSceneManager()->getZoneManager(); if (zoneManager) { + SceneZoneSpaceManager::ObjectZoneValueIterator itr, itrEnd; + 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; - for (SceneObject::ObjectZonesIterator zoneIter(obj); zoneIter.isValid(); ++zoneIter) - { - if (*zoneIter != zoneId) - fullyEnclosed = false; + zoneManager->getObjectZoneValueIterators(obj, itr, itrEnd); + + for (itr; itr != itrEnd; itr++) + { + if (*itr != zoneId) + { + fullyEnclosed = false; + break; + } } + if (fullyEnclosed) sql.insertObject(obj); } + } } } diff --git a/Engine/source/scene/culling/sceneCullingState.cpp b/Engine/source/scene/culling/sceneCullingState.cpp index 13fb46912..16a2628b4 100644 --- a/Engine/source/scene/culling/sceneCullingState.cpp +++ b/Engine/source/scene/culling/sceneCullingState.cpp @@ -96,10 +96,20 @@ SceneCullingState::SceneCullingState( SceneManager* sceneManager, const SceneCam bool SceneCullingState::isWithinVisibleZone( SceneObject* object ) const { - for( SceneObject::ZoneRef* ref = object->_getZoneRefHead(); - ref != NULL; ref = ref->nextInObj ) - if( mZoneVisibilityFlags.test( ref->zone ) ) + SceneManager* mgr = object->getSceneManager(); + SceneZoneSpaceManager* zm = mgr->getZoneManager(); + + 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 false; } @@ -148,8 +158,19 @@ void SceneCullingState::addOccluder( SceneObject* object ) // Add the frustum to all zones that the object is assigned to. - for( SceneObject::ZoneRef* ref = object->_getZoneRefHead(); ref != NULL; ref = ref->nextInObj ) - addCullingVolumeToZone( ref->zone, volume ); + U32 numZones = 0; + 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( object->getWorldBox(), - SceneObject::ObjectZonesIterator( object ) + mSceneManager->getZoneManager()->makeObjectZoneValueIterator(object) ); 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& farPlane = getCullingFrustum().getPlanes()[ Frustum::PlaneFar ]; + SceneZoneSpaceManager* zoneMgr = mSceneManager->getZoneManager(); + for( U32 i = 0; i < numObjects; ++ i ) { SceneObject* object = objects[ i ]; @@ -777,7 +800,7 @@ U32 SceneCullingState::cullObjects( SceneObject** objects, U32 numObjects, U32 c { CullingTestResult result = _test( object->getWorldBox(), - SceneObject::ObjectZonesIterator( object ), + zoneMgr->makeObjectZoneValueIterator( object ), nearPlane, farPlane ); diff --git a/Engine/source/scene/sceneContainer.h b/Engine/source/scene/sceneContainer.h index 9258a889c..aa3dca65b 100644 --- a/Engine/source/scene/sceneContainer.h +++ b/Engine/source/scene/sceneContainer.h @@ -96,7 +96,7 @@ public: return mItems == NULL; } - inline T* getPtr() + inline T* getPtr() const { return mItems; } @@ -150,6 +150,67 @@ public: 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: #pragma pack(2) @@ -228,6 +289,35 @@ public: 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: /// Gets a free entry from the free entry list. @@ -250,7 +340,7 @@ public: { BinList list; ListHandle retHandle = 0; - + list.numValues = numValues; list.startValue = mLastValueIdx; @@ -296,7 +386,7 @@ public: list.numValues = numValues; list.startValue = mLastValueIdx; - + mLastValueIdx += numValues; 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. /// This will automatically be called by freeList usually /// once CompactionThreshold list values have been freed. diff --git a/Engine/source/scene/sceneObject.cpp b/Engine/source/scene/sceneObject.cpp index 3f711c227..62cfb2a75 100644 --- a/Engine/source/scene/sceneObject.cpp +++ b/Engine/source/scene/sceneObject.cpp @@ -128,8 +128,8 @@ SceneObject::SceneObject() mSceneManager = NULL; + mZoneListHandle = 0; mNumCurrZones = 0; - mZoneRefHead = NULL; mZoneRefDirty = false; mLightPlugin = NULL; @@ -168,7 +168,7 @@ SceneObject::~SceneObject() { AssertFatal(mContainer == NULL, "SceneObject::~SceneObject - Object still in container!"); - AssertFatal( mZoneRefHead == NULL, + AssertFatal( mZoneListHandle == NULL, "SceneObject::~SceneObject - Object still linked in reference lists!"); AssertFatal( !mSceneObjectLinks, "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 ) { @@ -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(); // Not the most efficient way to do this, walking the list, // but it's an uncommon call... - ZoneRef* walk = mZoneRefHead; - for( U32 i = 0; i < index; ++ i ) - { - walk = walk->nextInObj; - AssertFatal( walk != NULL, "SceneObject::_getCurrZone - Too few object refs!" ); - } - AssertFatal( walk != NULL, "SceneObject::_getCurrZone - Too few object refs!" ); + U32 numZones = 0; + U32* zones = NULL; + zones = manager->getZoneIDS(this, numZones); - return walk->zone; + return index < numZones ? zones[index] : 0; } //----------------------------------------------------------------------------- diff --git a/Engine/source/scene/sceneObject.h b/Engine/source/scene/sceneObject.h index 45e23fa29..19c61e57f 100644 --- a/Engine/source/scene/sceneObject.h +++ b/Engine/source/scene/sceneObject.h @@ -224,77 +224,20 @@ class SceneObject : public NetObject, public ProcessObject /// @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 /// to when the state is actually needed and this flag indicates a refresh /// is necessary. - mutable bool mZoneRefDirty; + bool mZoneRefDirty; /// Number of zones this object is assigned to. /// @note If #mZoneRefDirty is set, this might be outdated. - mutable U32 mNumCurrZones; + U32 mNumCurrZones; - /// List of zones that this object is part of. - /// @note If #mZoneRefDirty is set, this might be outdated. - mutable ZoneRef* mZoneRefHead; + /// Handle for the zone list of this object + U32 mZoneListHandle; /// Refresh the zoning state of this object, if it isn't up-to-date anymore. - void _updateZoningState() const; - - /// 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; } + void _updateZoningState(); /// @} @@ -763,7 +706,7 @@ class SceneObject : public NetObject, public ProcessObject U32 getNumCurrZones() const { return mNumCurrZones; } /// Returns the nth zone containing this object. - U32 getCurrZone(const U32 index) const; + U32 getCurrZone(const U32 index); /// @} diff --git a/Engine/source/scene/zones/sceneZoneSpace.cpp b/Engine/source/scene/zones/sceneZoneSpace.cpp index e417900a9..3e5173537 100644 --- a/Engine/source/scene/zones/sceneZoneSpace.cpp +++ b/Engine/source/scene/zones/sceneZoneSpace.cpp @@ -348,7 +348,8 @@ void SceneZoneSpace::dumpZoneState( bool update ) 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() ); } } diff --git a/Engine/source/scene/zones/sceneZoneSpaceManager.cpp b/Engine/source/scene/zones/sceneZoneSpaceManager.cpp index 21d161731..082ce1744 100644 --- a/Engine/source/scene/zones/sceneZoneSpaceManager.cpp +++ b/Engine/source/scene/zones/sceneZoneSpaceManager.cpp @@ -37,10 +37,6 @@ //#define DEBUG_SPEW - -ClassChunker< SceneObject::ZoneRef > SceneZoneSpaceManager::smZoneRefChunker; - - //----------------------------------------------------------------------------- SceneZoneSpaceManager::SceneZoneSpaceManager( SceneContainer* container ) @@ -89,15 +85,7 @@ void SceneZoneSpaceManager::registerZones( SceneZoneSpace* object, U32 numZones 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; + mZoneLists[i] = _allocZoneList(object); } // Add space to list. @@ -143,14 +131,12 @@ void SceneZoneSpaceManager::unregisterZones( SceneZoneSpace* object ) for( U32 j = zoneRangeStart; j < zoneRangeStart + numZones; j ++ ) { - // Delete all object links. + ZoneObjectList* list = mZoneLists[j]; + // Delete all object links. _clearZoneList( j ); - // Delete the first link which refers to the zone itself. - - smZoneRefChunker.free( mZoneLists[ j ] ); - mZoneLists[ j ] = NULL; + _freeZoneList(list); } // Destroy the connections the zone space has. @@ -219,7 +205,7 @@ void SceneZoneSpaceManager::_compactZonesCheck() const U32 numZoneSpaces = mZoneSpaces.size(); U32 nextZoneId = 0; - Vector< SceneObject::ZoneRef* > newZoneLists; + Vector< ZoneObjectList* > newZoneLists; newZoneLists.setSize( mNumActiveZones ); for( U32 i = 0; i < numZoneSpaces; ++ i ) @@ -247,10 +233,15 @@ void SceneZoneSpaceManager::_compactZonesCheck() newZoneLists[ newZoneId ] = mZoneLists[ oldZoneId ]; + if (mZoneLists[ newZoneId ] == NULL) + continue; + // Update entries. - for( SceneObject::ZoneRef* ref = newZoneLists[ newZoneId ]; ref != NULL; ref = ref->nextInBin ) - ref->zone = newZoneId; + for (SceneObject* obj : mZoneLists[ newZoneId ]->getObjects()) + { + mObjectZoneLists.replaceListBin(obj->mZoneListHandle, oldZoneId, newZoneId); + } } } @@ -415,6 +406,9 @@ void SceneZoneSpaceManager::_rezoneObject( SceneObject* object ) _queryZoneSpaces( object->getWorldBox() ); + U32 numZones = 0; + U32* zones = mObjectZoneLists.getValues(object->mZoneListHandle, numZones); + const U32 numZoneSpaces = mZoneSpacesQueryList.size(); if( !numZoneSpaces ) { @@ -422,8 +416,7 @@ void SceneZoneSpaceManager::_rezoneObject( SceneObject* object ) // root zone, then we don't need an update. Otherwise, we do. if( object->mNumCurrZones == 1 && - object->mZoneRefHead && - object->mZoneRefHead->zone == RootZoneId ) + zones[0] == RootZoneId ) { object->mZoneRefDirty = false; 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 // we want to restrict to the outdoor zone. - if( mNumActiveZones == 1 || object->isGlobalBounds() || object->getTypeMask() & OUTDOOR_OBJECT_TYPEMASK ) - _addToOutdoorZone( object ); - else + bool outsideOnly = mNumActiveZones == 1 || object->isGlobalBounds() || (object->getTypeMask() & OUTDOOR_OBJECT_TYPEMASK); + U32 numGlobalZones = 0; + U32 remainingZones = SceneObject::MaxObjectZones; + U32 globalZones[SceneObject::MaxObjectZones]; + + if (!outsideOnly) { // Otherwise find all zones spaces that intersect with the object's // world box. - if( !queryListInitialized ) - _queryZoneSpaces( object->getWorldBox() ); + 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 ) + + mTempObjectZones.clear(); + mTempObjectZones.reserve(numZoneSpaces); + + for (U32 i = 0; i < numZoneSpaces; ++i) { - SceneZoneSpace* zoneSpace = dynamic_cast< SceneZoneSpace* >( mZoneSpacesQueryList[ i ] ); - if( !zoneSpace ) + SceneZoneSpace* zoneSpace = dynamic_cast(mZoneSpacesQueryList[i]); + if (!zoneSpace) 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 // the object itself at some point. Skip it. - if( zoneSpace == object ) + 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" ); + 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. + // Clamp the zone count + numZones = getMin(remainingZones, numZones); - for( U32 n = 0; n < numZones; ++ n ) - _addToZoneList( zones[ n ], object ); + if (numZones > 0) + { + // 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 - // zones. + mTempObjectZones.push_back(zoneRecord); + zoneSpace->_onZoneAddObject(object, zones + zoneRecord.startZone, numZones); - if( numZones > 0 ) - zoneSpace->_onZoneAddObject( object, zones, numZones ); + numGlobalZones += zoneRecord.numZones; + remainingZones -= zoneRecord.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 ); + if (outsideOnly || (outsideIncluded && remainingZones > 0)) + { + TempZoneRecord zoneRecord; + zoneRecord.numZones = 1; + zoneRecord.space = static_cast(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. @@ -679,65 +707,76 @@ void SceneZoneSpaceManager::_zoneInsert( SceneObject* object, bool queryListInit void SceneZoneSpaceManager::_zoneRemove( SceneObject* obj ) { + if (obj->mZoneListHandle == 0) + return; + PROFILE_SCOPE( SceneZoneSpaceManager_zoneRemove ); // 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; izone )->_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 ); + getZoneOwner( zones[i] )->_onZoneRemoveObject(obj); } // Clear the object's zoning state. - obj->mZoneRefHead = NULL; + mObjectZoneLists.freeList(obj->mZoneListHandle); + + obj->mZoneListHandle = 0; obj->mZoneRefDirty = false; 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; - newRef->object = object; - newRef->nextInBin = zoneList->nextInBin; - newRef->prevInBin = zoneList; +void SceneZoneSpaceManager::_setObjectZoneList( SceneObject* object, U32 numZones, U32* zoneList ) +{ +#ifdef TORQUE_ENABLE_ASSERTS + SceneZoneSpace* zoneSpace = dynamic_cast(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 ) - zoneList->nextInBin->prevInBin = newRef; + // Alloc or re-use entry - zoneList->nextInBin = newRef; - - // Add the zone to the object list. - - newRef->nextInObj = object->mZoneRefHead; - object->mZoneRefHead = newRef; - object->mNumCurrZones ++; + if (object->mZoneListHandle == 0) + { + object->mZoneListHandle = mObjectZoneLists.allocList(numZones, zoneList); + } + else if (numZones == 0) + { + 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" ); - SceneObject::ZoneRef* list = mZoneLists[ zoneId ]; + ZoneObjectList* 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 ) + for( SceneObject* object : list->getObjects() ) { - 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 ); + AssertFatal( object->mNumCurrZones > 0, "SceneZoneSpaceManager::_clearZoneList - Bad reference count" ); object->mNumCurrZones --; @@ -778,47 +802,30 @@ void SceneZoneSpaceManager::_clearZoneList( U32 zoneId ) // its zoning state as dirty so it will get assigned // to the outdoor zone on the next update. - if( !object->mZoneRefHead ) + if( object->mNumCurrZones == 0 ) 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 +bool SceneZoneSpaceManager::_isInZoneList( U32 zoneId, SceneObject* object ) const { - for( SceneObject::ZoneRef* ref = object->mZoneRefHead; ref != NULL; ref = ref->nextInObj ) - if( ref->zone == zoneId ) - return ref; + SceneZoneSpaceManager::ZoneObjectList* list = mZoneLists[zoneId]; + if (list == NULL) + return false; - return NULL; -} + for (SceneObject* obj : list->getObjects()) + { + if (obj == object) + return true; + } -//----------------------------------------------------------------------------- - -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 ); + return false; } //----------------------------------------------------------------------------- @@ -880,12 +887,11 @@ void SceneZoneSpaceManager::verifyState() 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!" ); + 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( ref->object != NULL, "SceneZoneSpaceManager::verifyState - Null object pointer in zone list!" ); + AssertFatal( mObjectZoneLists.containsBinItem(object->mZoneListHandle, zoneId), "SceneZoneSpaceManager::verifyState - Object doesn't have zone in list!"); #ifndef TORQUE_DISABLE_MEMORY_MANAGER Memory::checkPtr( ref->object ); @@ -921,3 +927,32 @@ void SceneZoneSpaceManager::verifyState() //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); +} diff --git a/Engine/source/scene/zones/sceneZoneSpaceManager.h b/Engine/source/scene/zones/sceneZoneSpaceManager.h index 8912fe5c0..d2a909204 100644 --- a/Engine/source/scene/zones/sceneZoneSpaceManager.h +++ b/Engine/source/scene/zones/sceneZoneSpaceManager.h @@ -39,22 +39,24 @@ #include "core/dataChunker.h" #endif +#ifndef _SCENECONTAINER_H_ +#include "scene/sceneContainer.h" +#endif + class SceneContainer; class SceneRootZone; class SceneZoneSpace; - +class Zone; /// Object that manages zone spaces in a scene. class SceneZoneSpaceManager { public: - class ZoneContentIterator; - 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. /// @@ -65,6 +67,10 @@ class SceneZoneSpaceManager /// of time. typedef Signal< void( SceneZoneSpaceManager* ) > ZoningChangedSignal; + + typedef SceneContainerBinRefList ZoneValueList; + typedef SceneContainerBinRefList::ValueIterator ObjectZoneValueIterator; + enum : U32 { /// Zone ID of the exterior zone. @@ -74,64 +80,21 @@ class SceneZoneSpaceManager InvalidZoneId = 0xFFFFFFFF, }; - /// Iterator for the contents of a given zone. - class ZoneContentIterator + class ZoneObjectList { - public: + protected: - ZoneContentIterator( SceneZoneSpaceManager* manager, S32 zoneId, bool upToDate = true ) - { - AssertFatal( zoneId < manager->getNumZones(), "SceneZoneSpaceManager::ZoneContentIterator - Zone ID out of range" ); + friend class SceneZoneSpaceManager; - if( upToDate ) - { - // 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() ); - } + SceneObject* mManager; + Vector mObjects; - mCurrent = manager->mZoneLists[ zoneId ]->nextInBin; // Skip zone object entry. - } + public: - bool isValid() const - { - 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; - } + ZoneObjectList(SceneObject* manager) : mManager(manager) { ; } - private: - - SceneObject::ZoneRef* mCurrent; + inline SceneObject* getManager() const { return mManager; } + inline Vector getObjects() const { return mObjects; } }; protected: @@ -153,13 +116,25 @@ class SceneZoneSpaceManager /// Object list for each zone in the scene. /// 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. mutable Vector< SceneObject* > mZoneSpacesQueryList; - /// Allocator for ZoneRefs. - static ClassChunker< SceneObject::ZoneRef > smZoneRefChunker; + struct TempZoneRecord + { + SceneZoneSpace* space; + U32 startZone; + U32 numZones; + }; + + Vector mTempObjectZones; /// @name Dirty Lists /// 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. void _zoneRemove( SceneObject* object ); - /// Add to given object to the zone list of the given zone. - void _addToZoneList( U32 zoneId, SceneObject* object ); + /// Realloc zoning state to the given 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. /// @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 ); /// Find the given object in the zone list of the given zone. - SceneObject::ZoneRef* _findInZoneList( U32 zoneId, SceneObject* object ) const; - - /// Assign the given object to the outdoor zone. - void _addToOutdoorZone( SceneObject* object ); + bool _isInZoneList( U32 zoneId, SceneObject* object ) const; /// Rezone all objects in the given area. void _rezoneObjects( const Box3F& area ); @@ -217,6 +192,10 @@ class SceneZoneSpaceManager /// Fill #mZoneSpacesQueryList with all ZoneObjectType objects in the given area. void _queryZoneSpaces( const Box3F& area ) const; + ZoneObjectList* _allocZoneList(SceneZoneSpace* space); + + void _freeZoneList(ZoneObjectList* list); + public: SceneZoneSpaceManager( SceneContainer* container ); @@ -277,7 +256,8 @@ class SceneZoneSpaceManager SceneZoneSpace* getZoneOwner( const U32 zoneId ) const { 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. @@ -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); + } + /// @} };