123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363 |
- //-----------------------------------------------------------------------------
- // Copyright (c) 2012 GarageGames, LLC
- //
- // Permission is hereby granted, free of charge, to any person obtaining a copy
- // of this software and associated documentation files (the "Software"), to
- // deal in the Software without restriction, including without limitation the
- // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
- // sell copies of the Software, and to permit persons to whom the Software is
- // furnished to do so, subject to the following conditions:
- //
- // The above copyright notice and this permission notice shall be included in
- // all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- // IN THE SOFTWARE.
- //-----------------------------------------------------------------------------
- #include "platform/platform.h"
- #include "scene/zones/sceneZoneSpace.h"
- #include "scene/zones/sceneTraversalState.h"
- #include "scene/zones/sceneZoneSpaceManager.h"
- #include "scene/sceneRenderState.h"
- #include "sim/netConnection.h"
- #include "core/stream/bitStream.h"
- #include "console/engineAPI.h"
- //#define DEBUG_SPEW
- ClassChunker< SceneZoneSpace::ZoneSpaceRef > SceneZoneSpace::smZoneSpaceRefChunker;
- //-----------------------------------------------------------------------------
- SceneZoneSpace::SceneZoneSpace()
- : mManager( NULL ),
- mZoneRangeStart( SceneZoneSpaceManager::InvalidZoneId ),
- mZoneGroup( InvalidZoneGroup ),
- mNumZones( 0 ),
- mZoneFlags( ZoneFlag_IsClosedOffSpace ),
- mConnectedZoneSpaces( NULL )
- {
- VECTOR_SET_ASSOCIATION( mOccluders );
- }
- //-----------------------------------------------------------------------------
- SceneZoneSpace::~SceneZoneSpace()
- {
- AssertFatal( mConnectedZoneSpaces == NULL, "SceneZoneSpace::~SceneZoneSpace - Still have connected zone spaces!" );
- }
- //-----------------------------------------------------------------------------
- void SceneZoneSpace::onSceneRemove()
- {
- _disconnectAllZoneSpaces();
- Parent::onSceneRemove();
- }
- //-----------------------------------------------------------------------------
- void SceneZoneSpace::initPersistFields()
- {
- docsURL;
- addGroup( "Zoning" );
- addProtectedField( "zoneGroup", TypeS32, Offset( mZoneGroup, SceneZoneSpace ),
- &_setZoneGroup, &defaultProtectedGetFn,
- "ID of group the zone is part of." );
- endGroup( "Zoning" );
- Parent::initPersistFields();
- }
- //-----------------------------------------------------------------------------
- bool SceneZoneSpace::writeField( StringTableEntry fieldName, const char* value )
- {
- // Don't write zoneGroup field if at default.
- static StringTableEntry sZoneGroup = StringTable->insert( "zoneGroup" );
- if( fieldName == sZoneGroup && getZoneGroup() == InvalidZoneGroup )
- return false;
- return Parent::writeField( fieldName, value );
- }
- //-----------------------------------------------------------------------------
- void SceneZoneSpace::setZoneGroup( U32 group )
- {
- if( mZoneGroup == group )
- return;
- mZoneGroup = group;
- setMaskBits( ZoneGroupMask );
- // Rezone to establish new connectivity.
- if( mManager )
- mManager->notifyObjectChanged( this );
- }
- //-----------------------------------------------------------------------------
- U32 SceneZoneSpace::packUpdate( NetConnection* connection, U32 mask, BitStream* stream )
- {
- U32 retMask = Parent::packUpdate( connection, mask, stream );
- if( stream->writeFlag( mask & ZoneGroupMask ) )
- stream->write( mZoneGroup );
- return retMask;
- }
- //-----------------------------------------------------------------------------
- void SceneZoneSpace::unpackUpdate( NetConnection* connection, BitStream* stream )
- {
- Parent::unpackUpdate( connection, stream );
- if( stream->readFlag() ) // ZoneGroupMask
- {
- U32 zoneGroup;
- stream->read( &zoneGroup );
- setZoneGroup( zoneGroup );
- }
- }
- //-----------------------------------------------------------------------------
- bool SceneZoneSpace::getOverlappingZones( SceneObject* obj, U32* outZones, U32& outNumZones )
- {
- return getOverlappingZones( obj->getWorldBox(), outZones, outNumZones );
- }
- //-----------------------------------------------------------------------------
- void SceneZoneSpace::_onZoneAddObject( SceneObject* object, const U32* zoneIDs, U32 numZones )
- {
- if( object->isVisualOccluder() )
- _addOccluder( object );
- // If this isn't the root zone and the object is zone space,
- // see if we should automatically connect the two.
-
- if( !isRootZone() && object->getTypeMask() & ZoneObjectType )
- {
- SceneZoneSpace* zoneSpace = dynamic_cast< SceneZoneSpace* >( object );
- // Don't connect a zone space that has the same closed off status
- // that we have except it is assigned to the same zone group.
- if( zoneSpace &&
- ( zoneSpace->mZoneFlags.test( ZoneFlag_IsClosedOffSpace ) != mZoneFlags.test( ZoneFlag_IsClosedOffSpace ) ||
- ( zoneSpace->getZoneGroup() == getZoneGroup() &&
- zoneSpace->getZoneGroup() != InvalidZoneGroup ) ) &&
- _automaticallyConnectZoneSpace( zoneSpace ) )
- {
- connectZoneSpace( zoneSpace );
- }
- }
- }
- //-----------------------------------------------------------------------------
- void SceneZoneSpace::_onZoneRemoveObject( SceneObject* object )
- {
- if( object->isVisualOccluder() )
- _removeOccluder( object );
- if( !isRootZone() && object->getTypeMask() & ZoneObjectType )
- {
- SceneZoneSpace* zoneSpace = dynamic_cast< SceneZoneSpace* >( object );
- if( zoneSpace )
- disconnectZoneSpace( zoneSpace );
- }
- }
- //-----------------------------------------------------------------------------
- bool SceneZoneSpace::_automaticallyConnectZoneSpace( SceneZoneSpace* zoneSpace ) const
- {
- //TODO: This is suboptimal. While it prevents the most blatantly wrong automatic connections,
- // we need a true polyhedron/polyhedron intersection to accurately determine zone intersection
- // when it comes to automatic connections.
- U32 numZones = 0;
- U32 zones[ SceneObject::MaxObjectZones ];
- zoneSpace->getOverlappingZones( getWorldBox(), zones, numZones );
-
- return ( numZones > 0 );
- }
- //-----------------------------------------------------------------------------
- void SceneZoneSpace::connectZoneSpace( SceneZoneSpace* zoneSpace )
- {
- // If the zone space is already in the list, do nothing.
- for( ZoneSpaceRef* ref = mConnectedZoneSpaces; ref != NULL; ref = ref->mNext )
- if( ref->mZoneSpace == zoneSpace )
- return;
- // Link the zone space to the zone space refs.
- ZoneSpaceRef* ref = smZoneSpaceRefChunker.alloc();
- ref->mZoneSpace = zoneSpace;
- ref->mNext = mConnectedZoneSpaces;
- mConnectedZoneSpaces = ref;
- #ifdef DEBUG_SPEW
- Platform::outputDebugString( "[SceneZoneSpace] Connecting %i-%i to %i-%i",
- getZoneRangeStart(), getZoneRangeStart() + getZoneRange(),
- zoneSpace->getZoneRangeStart(), zoneSpace->getZoneRangeStart() + zoneSpace->getZoneRange()
- );
- #endif
- }
- //-----------------------------------------------------------------------------
- void SceneZoneSpace::disconnectZoneSpace( SceneZoneSpace* zoneSpace )
- {
- ZoneSpaceRef* prev = NULL;
- for( ZoneSpaceRef* ref = mConnectedZoneSpaces; ref != NULL; prev = ref, ref = ref->mNext )
- if( ref->mZoneSpace == zoneSpace )
- {
- if( prev )
- prev->mNext = ref->mNext;
- else
- mConnectedZoneSpaces = ref->mNext;
- #ifdef DEBUG_SPEW
- Platform::outputDebugString( "[SceneZoneSpace] Disconnecting %i-%i from %i-%i",
- getZoneRangeStart(), getZoneRangeStart() + getZoneRange(),
- zoneSpace->getZoneRangeStart(), zoneSpace->getZoneRangeStart() + zoneSpace->getZoneRange()
- );
- #endif
- smZoneSpaceRefChunker.free( ref );
- break;
- }
- }
- //-----------------------------------------------------------------------------
- void SceneZoneSpace::_disconnectAllZoneSpaces()
- {
- #ifdef DEBUG_SPEW
- if( mConnectedZoneSpaces != NULL )
- Platform::outputDebugString( "[SceneZoneSpace] Disconnecting all from %i-%i",
- getZoneRangeStart(), getZoneRangeStart() + getZoneRange()
- );
- #endif
-
- for( ZoneSpaceRef* ref = mConnectedZoneSpaces; ref != NULL; )
- {
- ZoneSpaceRef* next = ref->mNext;
- smZoneSpaceRefChunker.free( ref );
- ref = next;
- }
- mConnectedZoneSpaces = NULL;
- }
- //-----------------------------------------------------------------------------
- void SceneZoneSpace::_addOccluder( SceneObject* object )
- {
- AssertFatal( !mOccluders.contains( object ), "SceneZoneSpace::_addOccluder - Occluder already added to this zone space!" );
- mOccluders.push_back( object );
- }
- //-----------------------------------------------------------------------------
- void SceneZoneSpace::_removeOccluder( SceneObject* object )
- {
- const U32 numOccluders = mOccluders.size();
- for( U32 i = 0; i < numOccluders; ++ i )
- if( mOccluders[ i ] == object )
- {
- mOccluders.erase_fast( i );
- break;
- }
- AssertFatal( !mOccluders.contains( object ), "SceneZoneSpace::_removeOccluder - Occluder still added to this zone space!" );
- }
- //-----------------------------------------------------------------------------
- void SceneZoneSpace::_addOccludersToCullingState( SceneCullingState* state ) const
- {
- const U32 numOccluders = mOccluders.size();
- for( U32 i = 0; i < numOccluders; ++ i )
- state->addOccluder( mOccluders[ i ] );
- }
- //-----------------------------------------------------------------------------
- void SceneZoneSpace::_traverseConnectedZoneSpaces( SceneTraversalState* state )
- {
- // Hand the traversal over to all connected zone spaces.
- for( ZoneSpaceRef* ref = mConnectedZoneSpaces; ref != NULL; ref = ref->mNext )
- {
- SceneZoneSpace* zoneSpace = ref->mZoneSpace;
- zoneSpace->traverseZones( state );
- }
- }
- //-----------------------------------------------------------------------------
- void SceneZoneSpace::dumpZoneState( bool update )
- {
- // Nothing to dump if not registered.
- if( !mManager )
- return;
- // If we should update, trigger rezoning for the space
- // we occupy.
- if( update )
- mManager->_rezoneObjects( getWorldBox() );
- Con::printf( "====== Zones in: %s =====", describeSelf().c_str() );
- // Dump connections.
- for( ZoneSpaceRef* ref = mConnectedZoneSpaces; ref != NULL; ref = ref->mNext )
- Con::printf( "Connected to: %s", ref->mZoneSpace->describeSelf().c_str() );
- // Dump objects.
- for( U32 i = 0; i < getZoneRange(); ++ i )
- {
- U32 zoneId = getZoneRangeStart() + i;
- Con::printf( "--- Zone %i", zoneId );
- for( SceneZoneSpaceManager::ZoneContentIterator iter( mManager, zoneId, false ); iter.isValid(); ++ iter )
- Con::printf( iter->describeSelf() );
- }
- }
- //-----------------------------------------------------------------------------
- bool SceneZoneSpace::_setZoneGroup( void* object, const char* index, const char* data )
- {
- SceneZoneSpace* zone = reinterpret_cast< SceneZoneSpace* >( object );
- zone->setZoneGroup( EngineUnmarshallData< S32 >()( data ) );
- return false;
- }
|