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