//----------------------------------------------------------------------------- // Copyright (c) 2012 GarageGames, LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to // deal in the Software without restriction, including without limitation the // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or // sell copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. //----------------------------------------------------------------------------- #ifndef _SCENEZONESPACEMANAGER_H_ #define _SCENEZONESPACEMANAGER_H_ #ifndef _SCENEOBJECT_H_ #include "scene/sceneObject.h" #endif #ifndef _TVECTOR_H_ #include "core/util/tVector.h" #endif #ifndef _TSIGNAL_H_ #include "core/util/tSignal.h" #endif #ifndef _DATACHUNKER_H_ #include "core/dataChunker.h" #endif class SceneContainer; class SceneRootZone; class SceneZoneSpace; /// Object that manages zone spaces in a scene. class SceneZoneSpaceManager { public: class ZoneContentIterator; friend class SceneZoneSpace; // mZoneLists friend class ZoneContentIterator; // mZoneLists /// A signal used to notify that the zone setup of the scene has changed. /// /// @note If you use this signal to maintain state that depends on the zoning /// setup, it's best to not immediately update the sate in response to this /// signal. The reason is that during loading and editing, the signal may /// be fired a lot and continuously updating dependent data may waste a lot /// of time. typedef Signal< void( SceneZoneSpaceManager* ) > ZoningChangedSignal; enum : U32 { /// Zone ID of the exterior zone. RootZoneId = 0, /// Constant to indicate an invalid zone ID. InvalidZoneId = 0xFFFFFFFF, }; /// Iterator for the contents of a given zone. class ZoneContentIterator { 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: /// The root and outdoor zone of the scene. SceneRootZone* mRootZone; /// Scene container that holds the zone spaces we are managing. SceneContainer* mContainer; /// Collection of objects that manage zones. Vector< SceneZoneSpace* > mZoneSpaces; /// Total number of zones that have been allocated in the scene. U32 mNumTotalAllocatedZones; /// Number of zone IDs that are in active use. U32 mNumActiveZones; /// Object list for each zone in the scene. /// First entry in the list points back to the zone manager. Vector< SceneObject::ZoneRef* > mZoneLists; /// Vector used repeatedly for zone space queries on the container. mutable Vector< SceneObject* > mZoneSpacesQueryList; /// Allocator for ZoneRefs. static ClassChunker< SceneObject::ZoneRef > smZoneRefChunker; /// @name Dirty Lists /// Updating the zoning state of a scene is done en block rather than /// individually for each object as it changes transforms or size. /// @{ /// Area of the scene that needs to be rezoned. Box3F mDirtyArea; /// List of zone spaces that have changed state and need updating. Vector< SceneZoneSpace* > mDirtyZoneSpaces; /// List of objects (non-zone spaces) that have changed state and need /// updating. Vector< SceneObject* > mDirtyObjects; /// @} /// Check to see if we have accumulated a lot of unallocate zone IDs and if so, /// compact the zoning lists by reassigning IDs. /// /// @warn This method may alter all zone IDs in the scene! void _compactZonesCheck(); /// Return the index into #mZoneSpaces for the given object or -1 if /// @object is not a zone manager. S32 _getZoneSpaceIndex( SceneZoneSpace* object ) const; /// Attach zoning state to the given object. void _zoneInsert( SceneObject* object, bool queryListInitialized = false ); /// 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 ); /// Clear all objects assigned to the given zone. /// @note This does not remove the first link in the zone list which is the link /// back to the zone manager. 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 ); /// Rezone all objects in the given area. void _rezoneObjects( const Box3F& area ); /// Update the zoning state of the given object. void _rezoneObject( SceneObject* object ); /// Fill #mZoneSpacesQueryList with all ZoneObjectType objects in the given area. void _queryZoneSpaces( const Box3F& area ) const; public: SceneZoneSpaceManager( SceneContainer* container ); ~SceneZoneSpaceManager(); /// Bring the zoning state of the scene up to date. This will cause objects /// that have moved or have been resized to be rezoned and will updated regions /// of the scene that had their zoning setup changed. /// /// @note This method depends on proper use of notifyObjectChanged(). void updateZoningState(); /// @name Objects /// @{ /// Add zoning state to the given object. void registerObject( SceneObject* object ); /// Remove the given object from the zoning state. void unregisterObject( SceneObject* object ); /// Let the manager know that state relevant to zoning of the given /// object has changed. void notifyObjectChanged( SceneObject* object ); /// Update the zoning state of the given object. void updateObject( SceneObject* object ); /// @} /// @name Zones /// @{ /// Return the root zone of the scene. SceneRootZone* getRootZone() const { return mRootZone; } /// Register a zone manager. /// /// @param object SceneZoneSpace object that contains zones. /// @param numZones Number of zones that @a object contains. void registerZones( SceneZoneSpace* object, U32 numZones ); /// Unregister a zone manager. /// /// @param object Object that contains zones. void unregisterZones( SceneZoneSpace* object ); /// Return true if the given ID belongs to a currently registered zone. bool isValidZoneId( const U32 zoneId ) const { return ( zoneId < mNumTotalAllocatedZones && mZoneLists[ zoneId ] ); } /// Get the scene object that contains the zone with the given ID. /// /// @param zoneId ID of the zone. Must be valid. /// @return The zone space that has registered the given zone. SceneZoneSpace* getZoneOwner( const U32 zoneId ) const { AssertFatal( isValidZoneId( zoneId ), "SceneManager::getZoneOwner - Invalid zone ID!"); return ( SceneZoneSpace* ) mZoneLists[ zoneId ]->object; } /// Return the total number of zones in the scene. U32 getNumZones() const { return mNumTotalAllocatedZones; } /// Return the effective amount of used zone IDs in the scene. U32 getNumActiveZones() const { return mNumActiveZones; } /// Return the total number of objects in the scene that manage zones. U32 getNumZoneSpaces() const { return mZoneSpaces.size(); } /// Find the zone that contains the given point. /// /// Note that the result can be any zone containing the given /// point. void findZone( const Point3F& point, SceneZoneSpace*& outZoneSpace, U32& outZoneID ) const; /// Collect the IDs of all zones that overlap the given area. /// /// @param area AABB of scene space to query. /// @param outZones IDs of all overlapped zones are added to this vector. /// /// @return Number of zones that have been added to @a outZones. Always at least /// 1 as at least the outdoor zone always overlaps the given area (though if another zone /// manager fully contains @a area, the outdoor zone will not be added to the list). U32 findZones( const Box3F& area, Vector< U32 >& outZones ) const; static ZoningChangedSignal& getZoningChangedSignal() { static ZoningChangedSignal sSignal; return sSignal; } /// @name Debugging /// @{ /// Verify the current zoning state. This makes sure all the connectivity /// information and all the zone assignments appear to be correct. void verifyState(); /// Dump the current state of all zone spaces in the scene to the console. /// @param update If true, zoning state states are updated first; if false, zoning is /// dumped as is. void dumpZoneStates( bool update = true ); /// @} /// @} }; #endif // !_SCENEZONESPACEMANAGER_H_