sceneZoneSpace.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2012 GarageGames, LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to
  6. // deal in the Software without restriction, including without limitation the
  7. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. // sell copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. // IN THE SOFTWARE.
  21. //-----------------------------------------------------------------------------
  22. #include "platform/platform.h"
  23. #include "scene/zones/sceneZoneSpace.h"
  24. #include "scene/zones/sceneTraversalState.h"
  25. #include "scene/zones/sceneZoneSpaceManager.h"
  26. #include "scene/sceneRenderState.h"
  27. #include "sim/netConnection.h"
  28. #include "core/stream/bitStream.h"
  29. #include "console/engineAPI.h"
  30. //#define DEBUG_SPEW
  31. ClassChunker< SceneZoneSpace::ZoneSpaceRef > SceneZoneSpace::smZoneSpaceRefChunker;
  32. //-----------------------------------------------------------------------------
  33. SceneZoneSpace::SceneZoneSpace()
  34. : mManager( NULL ),
  35. mZoneRangeStart( SceneZoneSpaceManager::InvalidZoneId ),
  36. mZoneGroup( InvalidZoneGroup ),
  37. mNumZones( 0 ),
  38. mZoneFlags( ZoneFlag_IsClosedOffSpace ),
  39. mConnectedZoneSpaces( NULL )
  40. {
  41. VECTOR_SET_ASSOCIATION( mOccluders );
  42. }
  43. //-----------------------------------------------------------------------------
  44. SceneZoneSpace::~SceneZoneSpace()
  45. {
  46. AssertFatal( mConnectedZoneSpaces == NULL, "SceneZoneSpace::~SceneZoneSpace - Still have connected zone spaces!" );
  47. }
  48. //-----------------------------------------------------------------------------
  49. void SceneZoneSpace::onSceneRemove()
  50. {
  51. _disconnectAllZoneSpaces();
  52. Parent::onSceneRemove();
  53. }
  54. //-----------------------------------------------------------------------------
  55. void SceneZoneSpace::initPersistFields()
  56. {
  57. docsURL;
  58. addGroup( "Zoning" );
  59. addProtectedField( "zoneGroup", TypeS32, Offset( mZoneGroup, SceneZoneSpace ),
  60. &_setZoneGroup, &defaultProtectedGetFn,
  61. "ID of group the zone is part of." );
  62. endGroup( "Zoning" );
  63. Parent::initPersistFields();
  64. }
  65. //-----------------------------------------------------------------------------
  66. bool SceneZoneSpace::writeField( StringTableEntry fieldName, const char* value )
  67. {
  68. // Don't write zoneGroup field if at default.
  69. static StringTableEntry sZoneGroup = StringTable->insert( "zoneGroup" );
  70. if( fieldName == sZoneGroup && getZoneGroup() == InvalidZoneGroup )
  71. return false;
  72. return Parent::writeField( fieldName, value );
  73. }
  74. //-----------------------------------------------------------------------------
  75. void SceneZoneSpace::setZoneGroup( U32 group )
  76. {
  77. if( mZoneGroup == group )
  78. return;
  79. mZoneGroup = group;
  80. setMaskBits( ZoneGroupMask );
  81. // Rezone to establish new connectivity.
  82. if( mManager )
  83. mManager->notifyObjectChanged( this );
  84. }
  85. //-----------------------------------------------------------------------------
  86. U32 SceneZoneSpace::packUpdate( NetConnection* connection, U32 mask, BitStream* stream )
  87. {
  88. U32 retMask = Parent::packUpdate( connection, mask, stream );
  89. if( stream->writeFlag( mask & ZoneGroupMask ) )
  90. stream->write( mZoneGroup );
  91. return retMask;
  92. }
  93. //-----------------------------------------------------------------------------
  94. void SceneZoneSpace::unpackUpdate( NetConnection* connection, BitStream* stream )
  95. {
  96. Parent::unpackUpdate( connection, stream );
  97. if( stream->readFlag() ) // ZoneGroupMask
  98. {
  99. U32 zoneGroup;
  100. stream->read( &zoneGroup );
  101. setZoneGroup( zoneGroup );
  102. }
  103. }
  104. //-----------------------------------------------------------------------------
  105. bool SceneZoneSpace::getOverlappingZones( SceneObject* obj, U32* outZones, U32& outNumZones )
  106. {
  107. return getOverlappingZones( obj->getWorldBox(), outZones, outNumZones );
  108. }
  109. //-----------------------------------------------------------------------------
  110. void SceneZoneSpace::_onZoneAddObject( SceneObject* object, const U32* zoneIDs, U32 numZones )
  111. {
  112. if( object->isVisualOccluder() )
  113. _addOccluder( object );
  114. // If this isn't the root zone and the object is zone space,
  115. // see if we should automatically connect the two.
  116. if( !isRootZone() && object->getTypeMask() & ZoneObjectType )
  117. {
  118. SceneZoneSpace* zoneSpace = dynamic_cast< SceneZoneSpace* >( object );
  119. // Don't connect a zone space that has the same closed off status
  120. // that we have except it is assigned to the same zone group.
  121. if( zoneSpace &&
  122. ( zoneSpace->mZoneFlags.test( ZoneFlag_IsClosedOffSpace ) != mZoneFlags.test( ZoneFlag_IsClosedOffSpace ) ||
  123. ( zoneSpace->getZoneGroup() == getZoneGroup() &&
  124. zoneSpace->getZoneGroup() != InvalidZoneGroup ) ) &&
  125. _automaticallyConnectZoneSpace( zoneSpace ) )
  126. {
  127. connectZoneSpace( zoneSpace );
  128. }
  129. }
  130. }
  131. //-----------------------------------------------------------------------------
  132. void SceneZoneSpace::_onZoneRemoveObject( SceneObject* object )
  133. {
  134. if( object->isVisualOccluder() )
  135. _removeOccluder( object );
  136. if( !isRootZone() && object->getTypeMask() & ZoneObjectType )
  137. {
  138. SceneZoneSpace* zoneSpace = dynamic_cast< SceneZoneSpace* >( object );
  139. if( zoneSpace )
  140. disconnectZoneSpace( zoneSpace );
  141. }
  142. }
  143. //-----------------------------------------------------------------------------
  144. bool SceneZoneSpace::_automaticallyConnectZoneSpace( SceneZoneSpace* zoneSpace ) const
  145. {
  146. //TODO: This is suboptimal. While it prevents the most blatantly wrong automatic connections,
  147. // we need a true polyhedron/polyhedron intersection to accurately determine zone intersection
  148. // when it comes to automatic connections.
  149. U32 numZones = 0;
  150. U32 zones[ SceneObject::MaxObjectZones ];
  151. zoneSpace->getOverlappingZones( getWorldBox(), zones, numZones );
  152. return ( numZones > 0 );
  153. }
  154. //-----------------------------------------------------------------------------
  155. void SceneZoneSpace::connectZoneSpace( SceneZoneSpace* zoneSpace )
  156. {
  157. // If the zone space is already in the list, do nothing.
  158. for( ZoneSpaceRef* ref = mConnectedZoneSpaces; ref != NULL; ref = ref->mNext )
  159. if( ref->mZoneSpace == zoneSpace )
  160. return;
  161. // Link the zone space to the zone space refs.
  162. ZoneSpaceRef* ref = smZoneSpaceRefChunker.alloc();
  163. ref->mZoneSpace = zoneSpace;
  164. ref->mNext = mConnectedZoneSpaces;
  165. mConnectedZoneSpaces = ref;
  166. #ifdef DEBUG_SPEW
  167. Platform::outputDebugString( "[SceneZoneSpace] Connecting %i-%i to %i-%i",
  168. getZoneRangeStart(), getZoneRangeStart() + getZoneRange(),
  169. zoneSpace->getZoneRangeStart(), zoneSpace->getZoneRangeStart() + zoneSpace->getZoneRange()
  170. );
  171. #endif
  172. }
  173. //-----------------------------------------------------------------------------
  174. void SceneZoneSpace::disconnectZoneSpace( SceneZoneSpace* zoneSpace )
  175. {
  176. ZoneSpaceRef* prev = NULL;
  177. for( ZoneSpaceRef* ref = mConnectedZoneSpaces; ref != NULL; prev = ref, ref = ref->mNext )
  178. if( ref->mZoneSpace == zoneSpace )
  179. {
  180. if( prev )
  181. prev->mNext = ref->mNext;
  182. else
  183. mConnectedZoneSpaces = ref->mNext;
  184. #ifdef DEBUG_SPEW
  185. Platform::outputDebugString( "[SceneZoneSpace] Disconnecting %i-%i from %i-%i",
  186. getZoneRangeStart(), getZoneRangeStart() + getZoneRange(),
  187. zoneSpace->getZoneRangeStart(), zoneSpace->getZoneRangeStart() + zoneSpace->getZoneRange()
  188. );
  189. #endif
  190. smZoneSpaceRefChunker.free( ref );
  191. break;
  192. }
  193. }
  194. //-----------------------------------------------------------------------------
  195. void SceneZoneSpace::_disconnectAllZoneSpaces()
  196. {
  197. #ifdef DEBUG_SPEW
  198. if( mConnectedZoneSpaces != NULL )
  199. Platform::outputDebugString( "[SceneZoneSpace] Disconnecting all from %i-%i",
  200. getZoneRangeStart(), getZoneRangeStart() + getZoneRange()
  201. );
  202. #endif
  203. for( ZoneSpaceRef* ref = mConnectedZoneSpaces; ref != NULL; )
  204. {
  205. ZoneSpaceRef* next = ref->mNext;
  206. smZoneSpaceRefChunker.free( ref );
  207. ref = next;
  208. }
  209. mConnectedZoneSpaces = NULL;
  210. }
  211. //-----------------------------------------------------------------------------
  212. void SceneZoneSpace::_addOccluder( SceneObject* object )
  213. {
  214. AssertFatal( !mOccluders.contains( object ), "SceneZoneSpace::_addOccluder - Occluder already added to this zone space!" );
  215. mOccluders.push_back( object );
  216. }
  217. //-----------------------------------------------------------------------------
  218. void SceneZoneSpace::_removeOccluder( SceneObject* object )
  219. {
  220. const U32 numOccluders = mOccluders.size();
  221. for( U32 i = 0; i < numOccluders; ++ i )
  222. if( mOccluders[ i ] == object )
  223. {
  224. mOccluders.erase_fast( i );
  225. break;
  226. }
  227. AssertFatal( !mOccluders.contains( object ), "SceneZoneSpace::_removeOccluder - Occluder still added to this zone space!" );
  228. }
  229. //-----------------------------------------------------------------------------
  230. void SceneZoneSpace::_addOccludersToCullingState( SceneCullingState* state ) const
  231. {
  232. const U32 numOccluders = mOccluders.size();
  233. for( U32 i = 0; i < numOccluders; ++ i )
  234. state->addOccluder( mOccluders[ i ] );
  235. }
  236. //-----------------------------------------------------------------------------
  237. void SceneZoneSpace::_traverseConnectedZoneSpaces( SceneTraversalState* state )
  238. {
  239. // Hand the traversal over to all connected zone spaces.
  240. for( ZoneSpaceRef* ref = mConnectedZoneSpaces; ref != NULL; ref = ref->mNext )
  241. {
  242. SceneZoneSpace* zoneSpace = ref->mZoneSpace;
  243. zoneSpace->traverseZones( state );
  244. }
  245. }
  246. //-----------------------------------------------------------------------------
  247. void SceneZoneSpace::dumpZoneState( bool update )
  248. {
  249. // Nothing to dump if not registered.
  250. if( !mManager )
  251. return;
  252. // If we should update, trigger rezoning for the space
  253. // we occupy.
  254. if( update )
  255. mManager->_rezoneObjects( getWorldBox() );
  256. Con::printf( "====== Zones in: %s =====", describeSelf().c_str() );
  257. // Dump connections.
  258. for( ZoneSpaceRef* ref = mConnectedZoneSpaces; ref != NULL; ref = ref->mNext )
  259. Con::printf( "Connected to: %s", ref->mZoneSpace->describeSelf().c_str() );
  260. // Dump objects.
  261. for( U32 i = 0; i < getZoneRange(); ++ i )
  262. {
  263. U32 zoneId = getZoneRangeStart() + i;
  264. Con::printf( "--- Zone %i", zoneId );
  265. for( SceneZoneSpaceManager::ZoneContentIterator iter( mManager, zoneId, false ); iter.isValid(); ++ iter )
  266. Con::printf( iter->describeSelf() );
  267. }
  268. }
  269. //-----------------------------------------------------------------------------
  270. bool SceneZoneSpace::_setZoneGroup( void* object, const char* index, const char* data )
  271. {
  272. SceneZoneSpace* zone = reinterpret_cast< SceneZoneSpace* >( object );
  273. zone->setZoneGroup( EngineUnmarshallData< S32 >()( data ) );
  274. return false;
  275. }