sceneManager.cpp
Engine/source/scene/sceneManager.cpp
Classes:
class
Public Variables
The client-side scene graph.
bool
For frame signal.
The server-side scene graph.
Public Functions
_scopeCallback(SceneObject * object, void * data)
DefineEngineFunction(sceneDumpZoneStates , void , (bool updateFirst) , (true) , "Dump the current zoning states of all zone spaces in the scene <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">console.\n\n</a>" "@param updateFirst If true, zoning states are brought up <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> date first;<a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> false, the zoning states " "are dumped as <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">is.\n\n</a>" " @note Only valid on the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">client.\n</a>" " @ingroup Game" )
DefineEngineFunction(sceneGetZoneOwner , SceneObject * , (U32 zoneId) , "Return the <a href="/coding/class/classsceneobject/">SceneObject</a> that contains the given <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">zone.\n\n</a>" "@param zoneId ID of <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">zone.\n</a>" "@return A <a href="/coding/class/classsceneobject/">SceneObject</a> or <a href="/coding/file/typesx86unix_8h/#typesx86unix_8h_1a070d2ce7b6bb7e5c05602aa8c308d0c4">NULL</a> <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> the given @<a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> zoneId is <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">invalid.\n\n</a>" "@note Only valid on the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">client.\n</a>" "@ingroup Game" )
Detailed Description
Public Variables
SceneManager * gClientSceneGraph
The client-side scene graph.
Not used if the engine is running as a dedicated server.
bool gEditingMission
For frame signal.
gServerSceneGraph
The server-side scene graph.
Not used if the engine is running as a pure client.
MODULE_END
MODULE_INIT
MODULE_SHUTDOWN
Public Functions
_scopeCallback(SceneObject * object, void * data)
DefineEngineFunction(sceneDumpZoneStates , void , (bool updateFirst) , (true) , "Dump the current zoning states of all zone spaces in the scene <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">console.\n\n</a>" "@param updateFirst If true, zoning states are brought up <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> date first;<a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> false, the zoning states " "are dumped as <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">is.\n\n</a>" " @note Only valid on the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">client.\n</a>" " @ingroup Game" )
DefineEngineFunction(sceneGetZoneOwner , SceneObject * , (U32 zoneId) , "Return the <a href="/coding/class/classsceneobject/">SceneObject</a> that contains the given <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">zone.\n\n</a>" "@param zoneId ID of <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">zone.\n</a>" "@return A <a href="/coding/class/classsceneobject/">SceneObject</a> or <a href="/coding/file/typesx86unix_8h/#typesx86unix_8h_1a070d2ce7b6bb7e5c05602aa8c308d0c4">NULL</a> <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> the given @<a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> zoneId is <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">invalid.\n\n</a>" "@note Only valid on the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">client.\n</a>" "@ingroup Game" )
SAFE_DELETE(gServerSceneGraph )
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/sceneManager.h" 26 27#include "scene/sceneObject.h" 28#include "scene/zones/sceneTraversalState.h" 29#include "scene/sceneRenderState.h" 30#include "scene/zones/sceneRootZone.h" 31#include "scene/zones/sceneZoneSpace.h" 32#include "lighting/lightManager.h" 33#include "renderInstance/renderPassManager.h" 34#include "gfx/gfxDevice.h" 35#include "gfx/gfxDrawUtil.h" 36#include "gfx/gfxDebugEvent.h" 37#include "console/engineAPI.h" 38#include "sim/netConnection.h" 39#include "T3D/gameBase/gameConnection.h" 40#include "math/mathUtils.h" 41 42// For player object bounds workaround. 43#include "T3D/player.h" 44 45#include "postFx/postEffectManager.h" 46 47extern bool gEditingMission; 48 49 50MODULE_BEGIN( Scene ) 51 52 MODULE_INIT_AFTER( Sim ) 53 MODULE_SHUTDOWN_BEFORE( Sim ) 54 55 MODULE_INIT 56 { 57 // Client scene. 58 gClientSceneGraph = new SceneManager( true ); 59 60 // Server scene. 61 gServerSceneGraph = new SceneManager( false ); 62 63 Con::addVariable( "$Scene::lockCull", TypeBool, &SceneManager::smLockDiffuseFrustum, 64 "Debug tool which locks the frustum culling to the current camera location.\n" 65 "@ingroup Rendering\n" ); 66 67 Con::addVariable( "$Scene::disableTerrainOcclusion", TypeBool, &SceneCullingState::smDisableTerrainOcclusion, 68 "Used to disable the somewhat expensive terrain occlusion testing.\n" 69 "@ingroup Rendering\n" ); 70 71 Con::addVariable( "$Scene::disableZoneCulling", TypeBool, &SceneCullingState::smDisableZoneCulling, 72 "If true, zone culling will be disabled and the scene contents will only be culled against the root frustum.\n\n" 73 "@ingroup Rendering\n" ); 74 75 Con::addVariable( "$Scene::renderBoundingBoxes", TypeBool, &SceneManager::smRenderBoundingBoxes, 76 "If true, the bounding boxes of objects will be displayed.\n\n" 77 "@ingroup Rendering" ); 78 79 Con::addVariable( "$Scene::maxOccludersPerZone", TypeS32, &SceneCullingState::smMaxOccludersPerZone, 80 "Maximum number of occluders that will be concurrently allowed into the scene culling state of any given zone.\n\n" 81 "@ingroup Rendering" ); 82 83 Con::addVariable( "$Scene::occluderMinWidthPercentage", TypeF32, &SceneCullingState::smOccluderMinWidthPercentage, 84 "TODO\n\n" 85 "@ingroup Rendering" ); 86 87 Con::addVariable( "$Scene::occluderMinHeightPercentage", TypeF32, &SceneCullingState::smOccluderMinHeightPercentage, 88 "TODO\n\n" 89 "@ingroup Rendering" ); 90 } 91 92 MODULE_SHUTDOWN 93 { 94 SAFE_DELETE( gClientSceneGraph ); 95 SAFE_DELETE( gServerSceneGraph ); 96 } 97 98MODULE_END; 99 100 101bool SceneManager::smRenderBoundingBoxes; 102bool SceneManager::smLockDiffuseFrustum = false; 103SceneCameraState SceneManager::smLockedDiffuseCamera = SceneCameraState( RectI(), Frustum(), MatrixF(), MatrixF() ); 104 105SceneManager* gClientSceneGraph = NULL; 106SceneManager* gServerSceneGraph = NULL; 107 108 109//----------------------------------------------------------------------------- 110 111SceneManager::SceneManager( bool isClient ) 112 : mIsClient( isClient ), 113 mZoneManager( NULL ), 114 mUsePostEffectFog( true ), 115 mDisplayTargetResolution( 0, 0 ), 116 mCurrentRenderState( NULL ), 117 mVisibleDistance( 500.f ), 118 mVisibleGhostDistance( 0 ), 119 mNearClip( 0.1f ), 120 mLightManager( NULL ), 121 mAmbientLightColor( LinearColorF( 0.1f, 0.1f, 0.1f, 1.0f ) ), 122 mDefaultRenderPass( NULL ) 123{ 124 VECTOR_SET_ASSOCIATION( mBatchQueryList ); 125 126 // For the client, create a zone manager. 127 128 if( isClient ) 129 { 130 mZoneManager = new SceneZoneSpaceManager( getContainer() ); 131 132 // Add the root zone to the scene. 133 134 addObjectToScene( mZoneManager->getRootZone() ); 135 } 136} 137 138//----------------------------------------------------------------------------- 139 140SceneManager::~SceneManager() 141{ 142 SAFE_DELETE( mZoneManager ); 143 144 if( mLightManager ) 145 mLightManager->deactivate(); 146} 147 148//----------------------------------------------------------------------------- 149 150void SceneManager::renderScene( ScenePassType passType, U32 objectMask ) 151{ 152 SceneCameraState cameraState = SceneCameraState::fromGFX(); 153 154 // Handle frustum locking. 155 156 const bool lockedFrustum = ( smLockDiffuseFrustum && passType == SPT_Diffuse ); 157 if( lockedFrustum ) 158 cameraState = smLockedDiffuseCamera; 159 else if( passType == SPT_Diffuse ) 160 { 161 // Store the camera state so if we lock, this will become the 162 // locked state. 163 smLockedDiffuseCamera = cameraState; 164 } 165 166 // Create the render state. 167 168 SceneRenderState renderState( this, passType, cameraState ); 169 170 // If we have locked the frustum, reset the view transform 171 // on the render pass which the render state has just set 172 // to the view matrix corresponding to the locked frustum. For 173 // rendering, however, we need the true view matrix from the 174 // GFX state. 175 176 if( lockedFrustum ) 177 { 178 RenderPassManager* rpm = renderState.getRenderPass(); 179 rpm->assignSharedXform( RenderPassManager::View, GFX->getWorldMatrix() ); 180 } 181 182 // Render. 183 184 renderScene( &renderState, objectMask ); 185} 186 187//----------------------------------------------------------------------------- 188 189void SceneManager::renderScene( SceneRenderState* renderState, U32 objectMask, SceneZoneSpace* baseObject, U32 baseZone ) 190{ 191 PROFILE_SCOPE( SceneGraph_renderScene ); 192 193 // Get the lights for rendering the scene. 194 195 PROFILE_START( SceneGraph_registerLights ); 196 LIGHTMGR->registerGlobalLights( &renderState->getCullingFrustum(), false, renderState->isDiffusePass()); 197 PROFILE_END(); 198 199 // If its a diffuse pass, update the current ambient light level. 200 // To do that find the starting zone and determine whether it has a custom 201 // ambient light color. If so, pass it on to the ambient light manager. 202 // If not, use the ambient light color of the sunlight. 203 // 204 // Note that we retain the starting zone information here and pass it 205 // on to renderSceneNoLights so that we don't need to look it up twice. 206 207 if( renderState->isDiffusePass() ) 208 { 209 if( !baseObject && getZoneManager() ) 210 { 211 getZoneManager()->findZone( renderState->getCameraPosition(), baseObject, baseZone ); 212 AssertFatal( baseObject != NULL, "SceneManager::renderScene - findZone() did not return an object" ); 213 } 214 215 LinearColorF zoneAmbient; 216 if( baseObject && baseObject->getZoneAmbientLightColor( baseZone, zoneAmbient ) ) 217 mAmbientLightColor.setTargetValue( zoneAmbient ); 218 else 219 { 220 const LightInfo* sunlight = LIGHTMGR->getSpecialLight( LightManager::slSunLightType ); 221 if( sunlight ) 222 mAmbientLightColor.setTargetValue( sunlight->getAmbient() ); 223 } 224 225 renderState->setAmbientLightColor( mAmbientLightColor.getCurrentValue() ); 226 } 227 228 // Trigger the pre-render signal. 229 230 PROFILE_START( SceneGraph_preRenderSignal); 231 mCurrentRenderState = renderState; 232 getPreRenderSignal().trigger( this, renderState ); 233 mCurrentRenderState = NULL; 234 PROFILE_END(); 235 236 // Render the scene. 237 238 if(GFX->getCurrentRenderStyle() == GFXDevice::RS_StereoSideBySide) 239 { 240 // Store previous values 241 RectI originalVP = GFX->getViewport(); 242 MatrixF originalWorld = GFX->getWorldMatrix(); 243 Frustum originalFrustum = GFX->getFrustum(); 244 245 // Save PFX & SceneManager projections 246 MatrixF origNonClipProjection = renderState->getSceneManager()->getNonClipProjection(); 247 PFXFrameState origPFXState = PFXMGR->getFrameState(); 248 249 const FovPort *currentFovPort = GFX->getStereoFovPort(); 250 const MatrixF *worldEyeTransforms = GFX->getInverseStereoEyeTransforms(); 251 252 // Render left half of display 253 GFX->activateStereoTarget(0); 254 GFX->beginField(); 255 256 GFX->setWorldMatrix(worldEyeTransforms[0]); 257 258 Frustum gfxFrustum = originalFrustum; 259 MathUtils::makeFovPortFrustum(&gfxFrustum, gfxFrustum.isOrtho(), gfxFrustum.getNearDist(), gfxFrustum.getFarDist(), currentFovPort[0]); 260 GFX->setFrustum(gfxFrustum); 261 262 SceneCameraState cameraStateLeft = SceneCameraState::fromGFX(); 263 SceneRenderState renderStateLeft( this, renderState->getScenePassType(), cameraStateLeft ); 264 renderStateLeft.getSceneManager()->setNonClipProjection(GFX->getProjectionMatrix()); 265 renderStateLeft.setSceneRenderStyle(SRS_SideBySide); 266 PFXMGR->setFrameMatrices(GFX->getWorldMatrix(), GFX->getProjectionMatrix()); 267 268 renderSceneNoLights( &renderStateLeft, objectMask, baseObject, baseZone ); // left 269 270 // Indicate that we've just finished a field 271 //GFX->clear(GFXClearTarget | GFXClearZBuffer | GFXClearStencil, ColorI(255,0,0), 1.0f, 0); 272 GFX->endField(); 273 274 // Render right half of display 275 GFX->activateStereoTarget(1); 276 GFX->beginField(); 277 GFX->setWorldMatrix(worldEyeTransforms[1]); 278 279 gfxFrustum = originalFrustum; 280 MathUtils::makeFovPortFrustum(&gfxFrustum, gfxFrustum.isOrtho(), gfxFrustum.getNearDist(), gfxFrustum.getFarDist(), currentFovPort[1]); 281 GFX->setFrustum(gfxFrustum); 282 283 SceneCameraState cameraStateRight = SceneCameraState::fromGFX(); 284 SceneRenderState renderStateRight( this, renderState->getScenePassType(), cameraStateRight ); 285 renderStateRight.getSceneManager()->setNonClipProjection(GFX->getProjectionMatrix()); 286 renderStateRight.setSceneRenderStyle(SRS_SideBySide); 287 PFXMGR->setFrameMatrices(GFX->getWorldMatrix(), GFX->getProjectionMatrix()); 288 289 renderSceneNoLights( &renderStateRight, objectMask, baseObject, baseZone ); // right 290 291 // Indicate that we've just finished a field 292 //GFX->clear(GFXClearTarget | GFXClearZBuffer | GFXClearStencil, ColorI(0,255,0), 1.0f, 0); 293 GFX->endField(); 294 295 // Restore previous values 296 renderState->getSceneManager()->setNonClipProjection(origNonClipProjection); 297 PFXMGR->setFrameState(origPFXState); 298 299 GFX->setWorldMatrix(originalWorld); 300 GFX->setFrustum(originalFrustum); 301 GFX->setViewport(originalVP); 302 } 303 else 304 { 305 renderSceneNoLights( renderState, objectMask, baseObject, baseZone ); 306 } 307 308 // Trigger the post-render signal. 309 310 PROFILE_START( SceneGraphRender_postRenderSignal ); 311 mCurrentRenderState = renderState; 312 getPostRenderSignal().trigger( this, renderState ); 313 mCurrentRenderState = NULL; 314 PROFILE_END(); 315 316 // Remove the previously registered lights. 317 318 PROFILE_START( SceneGraph_unregisterLights); 319 LIGHTMGR->unregisterAllLights(); 320 PROFILE_END(); 321} 322 323//----------------------------------------------------------------------------- 324 325void SceneManager::renderSceneNoLights( SceneRenderState* renderState, U32 objectMask, SceneZoneSpace* baseObject, U32 baseZone ) 326{ 327 // Set the current state. 328 329 mCurrentRenderState = renderState; 330 331 // Render. 332 333 _renderScene( mCurrentRenderState, objectMask, baseObject, baseZone ); 334 335 #ifdef TORQUE_DEBUG 336 337 // If frustum is locked and this is a diffuse pass, render the culling volumes of 338 // zones that are selected (or the volumes of the outdoor zone if no zone is 339 // selected). 340 341 if( gEditingMission && renderState->isDiffusePass() && smLockDiffuseFrustum ) 342 renderState->getCullingState().debugRenderCullingVolumes(); 343 344 #endif 345 346 mCurrentRenderState = NULL; 347} 348 349//----------------------------------------------------------------------------- 350 351void SceneManager::_renderScene( SceneRenderState* state, U32 objectMask, SceneZoneSpace* baseObject, U32 baseZone ) 352{ 353 AssertFatal( this == gClientSceneGraph, "SceneManager::_buildSceneGraph - Only the client scenegraph can support this call!" ); 354 355 PROFILE_SCOPE( SceneGraph_batchRenderImages ); 356 357 // In the editor, override the type mask for diffuse passes. 358 359 if( gEditingMission && state->isDiffusePass() ) 360 objectMask = EDITOR_RENDER_TYPEMASK; 361 362 // Update the zoning state and traverse zones. 363 364 if( getZoneManager() ) 365 { 366 // Update. 367 368 getZoneManager()->updateZoningState(); 369 370 // If zone culling isn't disabled, traverse the 371 // zones now. 372 373 if( !state->getCullingState().disableZoneCulling() ) 374 { 375 // Find the start zone if we haven't already. 376 377 if( !baseObject ) 378 { 379 getZoneManager()->findZone( state->getCameraPosition(), baseObject, baseZone ); 380 AssertFatal( baseObject != NULL, "SceneManager::_renderScene - findZone() did not return an object" ); 381 } 382 383 // Traverse zones starting in base object. 384 385 SceneTraversalState traversalState( &state->getCullingState() ); 386 PROFILE_START( Scene_traverseZones ); 387 baseObject->traverseZones( &traversalState, baseZone ); 388 PROFILE_END(); 389 390 // Set the scene render box to the area we have traversed. 391 392 state->setRenderArea( traversalState.getTraversedArea() ); 393 } 394 } 395 396 // Set the query box for the container query. Never 397 // make it larger than the frustum's AABB. In the editor, 398 // always query the full frustum as that gives objects 399 // the opportunity to render editor visualizations even if 400 // they are otherwise not in view. 401 402 if( !state->getCullingFrustum().getBounds().isOverlapped( state->getRenderArea() ) ) 403 { 404 // This handles fringe cases like flying backwards into a zone where you 405 // end up pretty much standing on a zone border and looking directly into 406 // its "walls". In that case the traversal area will be behind the frustum 407 // (remember that the camera isn't where visibility starts, it's the near 408 // distance). 409 410 return; 411 } 412 413 Box3F queryBox = state->getCullingFrustum().getBounds(); 414 if( !gEditingMission ) 415 { 416 queryBox.minExtents.setMax( state->getRenderArea().minExtents ); 417 queryBox.maxExtents.setMin( state->getRenderArea().maxExtents ); 418 } 419 420 PROFILE_START( Scene_cullObjects ); 421 422 //TODO: We should split the codepaths here based on whether the outdoor zone has visible space. 423 // If it has, we should use the container query-based path. 424 // If it hasn't, we should fill the object list directly from the zone lists which will usually 425 // include way fewer objects. 426 427 // Gather all objects that intersect the scene render box. 428 429 mBatchQueryList.clear(); 430 getContainer()->findObjectList( queryBox, objectMask, &mBatchQueryList ); 431 432 // Cull the list. 433 434 U32 numRenderObjects = state->getCullingState().cullObjects( 435 mBatchQueryList.address(), 436 mBatchQueryList.size(), 437 !state->isDiffusePass() ? SceneCullingState::CullEditorOverrides : 0 // Keep forced editor stuff out of non-diffuse passes. 438 ); 439 440 //HACK: If the control object is a Player and it is not in the render list, force 441 // it into it. This really should be solved by collision bounds being separate from 442 // object bounds; only because the Player class is using bounds not encompassing 443 // the actual player object is it that we have this problem in the first place. 444 // Note that we are forcing the player object into ALL passes here but such 445 // is the power of proliferation of things done wrong. 446 447 GameConnection* connection = GameConnection::getConnectionToServer(); 448 if( connection ) 449 { 450 Player* player = dynamic_cast< Player* >( connection->getControlObject() ); 451 if( player ) 452 { 453 mBatchQueryList.setSize( numRenderObjects ); 454 if( !mBatchQueryList.contains( player ) ) 455 { 456 mBatchQueryList.push_back( player ); 457 numRenderObjects ++; 458 } 459 } 460 } 461 462 PROFILE_END(); 463 464 //store our rendered objects into a list we can easily look up against later if required 465 mRenderedObjectsList.clear(); 466 for (U32 i = 0; i < numRenderObjects; ++i) 467 { 468 mRenderedObjectsList.push_back(mBatchQueryList[i]); 469 } 470 471 // Render the remaining objects. 472 473 PROFILE_START( Scene_renderObjects ); 474 state->renderObjects( mBatchQueryList.address(), numRenderObjects ); 475 PROFILE_END(); 476 477 // Render bounding boxes, if enabled. 478 479 if( smRenderBoundingBoxes && state->isDiffusePass() ) 480 { 481 GFXDEBUGEVENT_SCOPE( Scene_renderBoundingBoxes, ColorI::WHITE ); 482 483 GameBase* cameraObject = 0; 484 if( connection ) 485 cameraObject = connection->getCameraObject(); 486 487 GFXStateBlockDesc desc; 488 desc.setFillModeWireframe(); 489 desc.setZReadWrite( true, false ); 490 491 for( U32 i = 0; i < numRenderObjects; ++ i ) 492 { 493 SceneObject* object = mBatchQueryList[ i ]; 494 495 // Skip global bounds object. 496 if( object->isGlobalBounds() ) 497 continue; 498 499 // Skip camera object as we're viewing the scene from it. 500 if( object == cameraObject ) 501 continue; 502 503 const Box3F& worldBox = object->getWorldBox(); 504 GFX->getDrawUtil()->drawObjectBox( 505 desc, 506 Point3F( worldBox.len_x(), worldBox.len_y(), worldBox.len_z() ), 507 worldBox.getCenter(), 508 MatrixF::Identity, 509 ColorI::WHITE 510 ); 511 } 512 } 513} 514 515//----------------------------------------------------------------------------- 516 517struct ScopingInfo 518{ 519 Point3F scopePoint; 520 F32 scopeDist; 521 F32 scopeDistSquared; 522 NetConnection* connection; 523}; 524 525static void _scopeCallback( SceneObject* object, void* data ) 526{ 527 if( !object->isScopeable() ) 528 return; 529 530 ScopingInfo* info = reinterpret_cast< ScopingInfo* >( data ); 531 NetConnection* connection = info->connection; 532 533 F32 difSq = ( object->getWorldSphere().center - info->scopePoint ).lenSquared(); 534 if( difSq < info->scopeDistSquared ) 535 { 536 // Not even close, it's in... 537 connection->objectInScope( object ); 538 } 539 else 540 { 541 // Check a little more closely... 542 F32 realDif = mSqrt( difSq ); 543 if( realDif - object->getWorldSphere().radius < info->scopeDist) 544 connection->objectInScope( object ); 545 } 546} 547 548void SceneManager::scopeScene( CameraScopeQuery* query, NetConnection* netConnection ) 549{ 550 PROFILE_SCOPE( SceneGraph_scopeScene ); 551 552 // Note that this method does not use the zoning information in the scene 553 // to scope objects. The reason is that with the way that scoping is implemented 554 // in the networking layer--i.e. by killing off ghosts of objects that are out 555 // of scope--, it doesn't make sense to let, for example, all objects in the outdoor 556 // zone go out of scope, just because there is no exterior portal that is visible from 557 // the current camera viewpoint (in any direction). 558 // 559 // So, we perform a simple box query on the area covered by the camera query 560 // and then scope in everything that is in range. 561 562 // Set up scoping info. 563 564 ScopingInfo info; 565 566 info.scopePoint = query->pos; 567 info.scopeDist = query->visibleDistance; 568 info.scopeDistSquared = info.scopeDist * info.scopeDist; 569 info.connection = netConnection; 570 571 // Scope all objects in the query area. 572 573 Box3F area( query->visibleDistance ); 574 area.setCenter( query->pos ); 575 576 getContainer()->findObjects( area, 0xFFFFFFFF, _scopeCallback, &info ); 577} 578 579//----------------------------------------------------------------------------- 580 581bool SceneManager::addObjectToScene( SceneObject* object ) 582{ 583 AssertFatal( !object->mSceneManager, "SceneManager::addObjectToScene - Object already part of a scene" ); 584 585 // Mark the object as belonging to us. 586 587 object->mSceneManager = this; 588 589 // Register with managers except its the root zone. 590 591 if( !dynamic_cast< SceneRootZone* >( object ) ) 592 { 593 // Add to container. 594 595 getContainer()->addObject( object ); 596 597 // Register the object with the zone manager. 598 599 if( getZoneManager() ) 600 getZoneManager()->registerObject( object ); 601 } 602 603 // Notify the object. 604 605 return object->onSceneAdd(); 606} 607 608//----------------------------------------------------------------------------- 609 610void SceneManager::removeObjectFromScene( SceneObject* obj ) 611{ 612 AssertFatal( obj, "SceneManager::removeObjectFromScene - Object is not declared" ); 613 AssertFatal( obj->getSceneManager() == this, "SceneManager::removeObjectFromScene - Object not part of SceneManager" ); 614 615 // Notify the object. 616 617 obj->onSceneRemove(); 618 619 // Remove the object from the container. 620 621 if( getContainer() ) 622 getContainer()->removeObject( obj ); 623 624 // Remove the object from the zoning system. 625 626 if( getZoneManager() ) 627 getZoneManager()->unregisterObject( obj ); 628 629 // Clear out the reference to us. 630 631 obj->mSceneManager = NULL; 632} 633 634//----------------------------------------------------------------------------- 635 636void SceneManager::notifyObjectDirty( SceneObject* object ) 637{ 638 // Update container state. 639 640 if( object->mContainer ) 641 object->mContainer->checkBins( object ); 642 643 // Mark zoning state as dirty. 644 645 if( getZoneManager() ) 646 getZoneManager()->notifyObjectChanged( object ); 647} 648 649//----------------------------------------------------------------------------- 650 651void SceneManager::setDisplayTargetResolution( const Point2I &size ) 652{ 653 mDisplayTargetResolution = size; 654} 655 656//----------------------------------------------------------------------------- 657 658const Point2I & SceneManager::getDisplayTargetResolution() const 659{ 660 return mDisplayTargetResolution; 661} 662 663//----------------------------------------------------------------------------- 664 665bool SceneManager::setLightManager( const char* lmName ) 666{ 667 LightManager *lm = LightManager::findByName( lmName ); 668 if ( !lm ) 669 return false; 670 671 return _setLightManager( lm ); 672} 673 674//----------------------------------------------------------------------------- 675 676bool SceneManager::_setLightManager( LightManager* lm ) 677{ 678 // Avoid unnecessary work reinitializing materials. 679 if ( lm == mLightManager ) 680 return true; 681 682 // Make sure its valid... else fail! 683 if ( !lm->isCompatible() ) 684 return false; 685 686 // We only deactivate it... all light managers are singletons 687 // and will manager their own lifetime. 688 if ( mLightManager ) 689 mLightManager->deactivate(); 690 691 mLightManager = lm; 692 693 if ( mLightManager ) 694 mLightManager->activate( this ); 695 696 return true; 697} 698 699//----------------------------------------------------------------------------- 700 701RenderPassManager* SceneManager::getDefaultRenderPass() const 702{ 703 if( !mDefaultRenderPass ) 704 { 705 Sim::findObject( "DiffuseRenderPassManager", mDefaultRenderPass ); 706 AssertISV( mDefaultRenderPass, "SceneManager::_setDefaultRenderPass - No DiffuseRenderPassManager defined! Must be set up in script!" ); 707 } 708 709 return mDefaultRenderPass; 710} 711 712//============================================================================= 713// Console API. 714//============================================================================= 715// MARK: ---- Console API ---- 716 717//----------------------------------------------------------------------------- 718 719DefineEngineFunction( sceneDumpZoneStates, void, ( bool updateFirst ), ( true ), 720 "Dump the current zoning states of all zone spaces in the scene to the console.\n\n" 721 "@param updateFirst If true, zoning states are brought up to date first; if false, the zoning states " 722 "are dumped as is.\n\n" 723 "@note Only valid on the client.\n" 724 "@ingroup Game" ) 725{ 726 if( !gClientSceneGraph ) 727 { 728 Con::errorf( "sceneDumpZoneStates - Only valid on client!" ); 729 return; 730 } 731 732 SceneZoneSpaceManager* manager = gClientSceneGraph->getZoneManager(); 733 if( !manager ) 734 { 735 Con::errorf( "sceneDumpZoneStates - Scene is not using zones!" ); 736 return; 737 } 738 739 manager->dumpZoneStates( updateFirst ); 740} 741 742//----------------------------------------------------------------------------- 743 744DefineEngineFunction( sceneGetZoneOwner, SceneObject*, ( U32 zoneId ),, 745 "Return the SceneObject that contains the given zone.\n\n" 746 "@param zoneId ID of zone.\n" 747 "@return A SceneObject or NULL if the given @a zoneId is invalid.\n\n" 748 "@note Only valid on the client.\n" 749 "@ingroup Game" ) 750{ 751 if( !gClientSceneGraph ) 752 { 753 Con::errorf( "sceneGetZoneOwner - Only valid on client!" ); 754 return NULL; 755 } 756 757 SceneZoneSpaceManager* manager = gClientSceneGraph->getZoneManager(); 758 if( !manager ) 759 { 760 Con::errorf( "sceneGetZoneOwner - Scene is not using zones!" ); 761 return NULL; 762 } 763 764 if( !manager->isValidZoneId( zoneId ) ) 765 { 766 Con::errorf( "sceneGetZoneOwner - Invalid zone ID: %i", zoneId ); 767 return NULL; 768 } 769 770 return manager->getZoneOwner( zoneId ); 771} 772