2
0
Эх сурвалжийг харах

Basic zone code refactor using lists instead of linked lists

James Urquhart 2 жил өмнө
parent
commit
d9ff5d3f0e

+ 17 - 6
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);
             }
+         }
       }
    }
 

+ 30 - 7
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
          );

+ 128 - 3
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.

+ 9 - 12
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;
 }
 
 //-----------------------------------------------------------------------------

+ 6 - 63
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);
 
       /// @}   
 

+ 2 - 1
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() );
    }
 }

+ 168 - 133
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<SceneZoneSpace*>(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.
-
-         for( U32 n = 0; n < numZones; ++ n )
-            _addToZoneList( zones[ n ], object );
-
-         // Let the zone manager know we have added objects to its
-         // zones.
+         // Clamp the zone count
+         numZones = getMin(remainingZones, numZones);
 
-         if( numZones > 0 )
-            zoneSpace->_onZoneAddObject( object, zones, numZones );
+         if (numZones > 0)
+         {
+            // Add to temp list
+            TempZoneRecord zoneRecord;
+            zoneRecord.numZones = numZones;
+            zoneRecord.space = zoneSpace;
+            zoneRecord.startZone = numGlobalZones;
+            dCopyArray(globalZones + numGlobalZones, zones, numZones);
+
+            mTempObjectZones.push_back(zoneRecord);
+            zoneSpace->_onZoneAddObject(object, zones + zoneRecord.startZone, 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<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.
@@ -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; i<numZones; i++)
    {
       // 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 );
+      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 ];
-
-   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();
+   if (object->mZoneListHandle == 0)
+      return _zoneInsert(object, queryListInitialized);
 
-   // 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;
+void SceneZoneSpaceManager::_setObjectZoneList( SceneObject* object, U32 numZones, U32* zoneList )
+{
+#ifdef TORQUE_ENABLE_ASSERTS
+   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
 
-   // Add the zone to the object list.
+   // Alloc or re-use entry
 
-   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;
-}
-
-//-----------------------------------------------------------------------------
-
-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.
+   for (SceneObject* obj : list->getObjects())
+   {
+      if (obj == object)
+         return true;
+   }
 
-   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);
+}

+ 66 - 71
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<U32> ZoneValueList;
+      typedef SceneContainerBinRefList<U32>::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:
-
-            ZoneContentIterator( SceneZoneSpaceManager* manager, S32 zoneId, bool upToDate = true )
-            {
-               AssertFatal( zoneId < manager->getNumZones(), "SceneZoneSpaceManager::ZoneContentIterator - Zone ID out of range" );
-
-               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() );
-               }
-
-               mCurrent = manager->mZoneLists[ zoneId ]->nextInBin; // Skip zone object entry.
-            }
-
-            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;
-            }
-
-         private:
-
-            SceneObject::ZoneRef* mCurrent;
+      protected:
+
+         friend class SceneZoneSpaceManager;
+
+         SceneObject* mManager;
+         Vector<SceneObject*> mObjects;
+
+      public:
+
+         ZoneObjectList(SceneObject* manager) : mManager(manager) { ; }
+
+         inline SceneObject* getManager() const { return mManager; }
+         inline Vector<SceneObject*> 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<TempZoneRecord> 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);
+      }
+
       /// @}
 };