Divide Framework 0.1
A free and open-source 3D Framework under heavy development
Loading...
Searching...
No Matches
Scene.cpp
Go to the documentation of this file.
1
2
3#include "Headers/Scene.h"
5
12#include "Core/Headers/Kernel.h"
15
21
23
29
36
37#include "GUI/Headers/GUI.h"
40
42
50
53
56
64
65namespace Divide
66{
67
68 namespace
69 {
70 constexpr U16 BYTE_BUFFER_VERSION = 1u;
71 constexpr const char* const g_defaultPlayerName = "Player_{}";
72 }
73
75
77 : Resource( entry._name, "Scene" )
78 , PlatformContextComponent( context )
79 , _entry( entry )
80 , _parent( parent )
81 {
82 _loadingTasks.store( 0 );
83 _flashLight.fill( nullptr );
84 _currentHoverTarget.fill( -1 );
85 _cameraUpdateListeners.fill( 0u );
86 _sceneGraph = std::make_unique<SceneGraph>( *this );
87 _state = std::make_unique<SceneState>( *this );
88 _input = std::make_unique<SceneInput>( *this );
89 _aiManager = std::make_unique<AI::AIManager>( *this, _context.taskPool( TaskPoolType::HIGH_PRIORITY ) );
90 _lightPool = std::make_unique<LightPool>( *this, _context );
91 _envProbePool = std::make_unique<SceneEnvironmentProbePool>( *this );
92 _GUI = std::make_unique<SceneGUIElements>( *this, _context.gui() );
93
94 _linesPrimitive = _context.gfx().newIMP( "Generic Line Primitive" );
95
96 PipelineDescriptor pipeDesc;
97 pipeDesc._stateBlock._depthTestEnabled = false;
98 pipeDesc._shaderProgramHandle = _context.gfx().imShaders()->imShaderNoTexture();
100 }
101
103 {
104 if (_linesPrimitive != nullptr && !_context.gfx().destroyIMP( _linesPrimitive ))
105 {
106 DebugBreak();
107 }
108 }
109
111 {
112 Sky::OnStartup( context );
114 return true;
115 }
116
117 bool Scene::OnShutdown( [[maybe_unused]] PlatformContext& context )
118 {
120 return true;
121 }
122
124 {
125 return GetSceneRootFolder(scene.parent() ) / scene.resourceName();
126 }
127
129 {
130 return Paths::g_projectsLocation / project.id()._name / Paths::g_scenesLocation;
131 }
132
134 {
135 return true;
136 }
137
139 {
140 return true;
141 }
142
144 {
146 if ( !_tasks.empty() )
147 {
148 // Check again to avoid race conditions
149 {
151 if ( _tasks.empty() )
152 {
153 return true;
154 }
155 }
156
158 dvd_erase_if( _tasks, []( Task* handle ) -> bool
159 {
160 return handle != nullptr && Finished( *handle );
161 } );
162 }
163
164 return true;
165 }
166
167 void Scene::addMusic( const MusicType type, const std::string_view name, const ResourcePath& srcFile )
168 {
169 const auto [musicFile, musicFilePath] = splitPathToNameAndLocation( srcFile );
170
172 music.assetName( musicFile );
173 music.assetLocation( musicFilePath );
174 music.flag( true );
175
177
178 insert( state()->music( type ), _ID( name ), handle );
179 }
180
181 bool Scene::saveNodeToXML( const SceneGraphNode* node ) const
182 {
183 return sceneGraph()->saveNodeToXML( node );
184 }
185
187 {
188 return sceneGraph()->loadNodeFromXML( ResourcePath{ "assets.xml" }, node );
189 }
190
191 bool Scene::saveXML( const DELEGATE<void, std::string_view>& msgCallback, const DELEGATE<void, bool>& finishCallback ) const
192 {
193 using boost::property_tree::ptree;
194 const ResourcePath assetsFile{ "assets.xml" };
195
196 Console::printfn( LOCALE_STR( "XML_SAVE_SCENE_START" ), resourceName().c_str() );
197
198 const Str<256>& sceneName = resourceName();
199 const Str<256> sceneSaveFile = sceneName + ".xml";
200 const ResourcePath scenesLocation = GetSceneRootFolder( parent() );
201 const ResourcePath sceneLocation = GetSceneFullPath( *this );
202 const ResourcePath sceneDataFile = scenesLocation / sceneSaveFile;
203
204 if ( msgCallback )
205 {
206 msgCallback( "Validating directory structure ..." );
207 }
208
209 if ( createDirectory( sceneLocation / "collisionMeshes") != FileError::NONE )
210 {
211 NOP();
212 }
213 if ( createDirectory( sceneLocation / "navMeshes" ) != FileError::NONE )
214 {
215 NOP();
216 }
217 if ( createDirectory( sceneLocation / Paths::g_nodesSaveLocation ) != FileError::NONE )
218 {
219 NOP();
220 }
221
222 // A scene does not necessarily need external data files. Data can be added in code for simple scenes
223 {
224 if ( msgCallback )
225 {
226 msgCallback( "Saving scene settings ..." );
227 }
228
229 ptree pt;
230 pt.put( "assets", assetsFile.string() );
231 pt.put( "musicPlaylist", "musicPlaylist.xml" );
232
233 pt.put( "vegetation.grassVisibility", state()->renderState().grassVisibility() );
234 pt.put( "vegetation.treeVisibility", state()->renderState().treeVisibility() );
235
236 pt.put( "wind.windDirX", state()->windDirX() );
237 pt.put( "wind.windDirZ", state()->windDirZ() );
238 pt.put( "wind.windSpeed", state()->windSpeed() );
239
240 pt.put( "options.visibility", state()->renderState().generalVisibility() );
241
242 for ( U8 i = 0u; i < playerCount(); ++i )
243 {
244 playerCamera( i, true )->saveToXML( pt );
245 }
246
247 pt.put( "fog.fogDensity", state()->renderState().fogDetails()._colourAndDensity.a );
248 pt.put( "fog.fogScatter", state()->renderState().fogDetails()._colourSunScatter.a );
249 pt.put( "fog.fogColour.<xmlattr>.r", state()->renderState().fogDetails()._colourAndDensity.r );
250 pt.put( "fog.fogColour.<xmlattr>.g", state()->renderState().fogDetails()._colourAndDensity.g );
251 pt.put( "fog.fogColour.<xmlattr>.b", state()->renderState().fogDetails()._colourAndDensity.b );
252
253 pt.put( "lod.lodThresholds.<xmlattr>.x", state()->renderState().lodThresholds().x );
254 pt.put( "lod.lodThresholds.<xmlattr>.y", state()->renderState().lodThresholds().y );
255 pt.put( "lod.lodThresholds.<xmlattr>.z", state()->renderState().lodThresholds().z );
256 pt.put( "lod.lodThresholds.<xmlattr>.w", state()->renderState().lodThresholds().w );
257
258 pt.put( "shadowing.<xmlattr>.lightBleedBias", state()->lightBleedBias() );
259 pt.put( "shadowing.<xmlattr>.minShadowVariance", state()->minShadowVariance() );
260
261 pt.put( "dayNight.<xmlattr>.enabled", dayNightCycleEnabled() );
262 pt.put( "dayNight.timeOfDay.<xmlattr>.hour", _dayNightData._time._hour );
263 pt.put( "dayNight.timeOfDay.<xmlattr>.minute", _dayNightData._time._minutes );
264 pt.put( "dayNight.location.<xmlattr>.latitude", _dayNightData._location._latitude );
265 pt.put( "dayNight.location.<xmlattr>.longitude", _dayNightData._location._longitude );
266 pt.put( "dayNight.timeOfDay.<xmlattr>.timeFactor", _dayNightData._speedFactor );
267
268 const FileError backupReturnCode = copyFile( scenesLocation, sceneSaveFile.c_str(), scenesLocation, (sceneSaveFile + ".bak").c_str(), true );
269 if ( backupReturnCode != FileError::NONE &&
270 backupReturnCode != FileError::FILE_NOT_FOUND &&
271 backupReturnCode != FileError::FILE_EMPTY )
272 {
273 if constexpr( !Config::Build::IS_SHIPPING_BUILD )
274 {
276 }
277 }
278 else
279 {
280 XML::writeXML( sceneDataFile, pt );
281 }
282 }
283
284 if ( msgCallback )
285 {
286 msgCallback( "Saving scene graph data ..." );
287 }
288 sceneGraph()->saveToXML( assetsFile, msgCallback );
289
290 //save music
291 {
292 if ( msgCallback )
293 {
294 msgCallback( "Saving music data ..." );
295 }
296
297 ptree pt = {}; //ToDo: Save music data :)
298
299 if ( copyFile( sceneLocation, "musicPlaylist.xml", sceneLocation, "musicPlaylist.xml.bak", true ) == FileError::NONE )
300 {
301 XML::writeXML( sceneLocation / "musicPlaylist.xml.dev", pt );
302 }
303 }
304
305 Console::printfn( LOCALE_STR( "XML_SAVE_SCENE_END" ), sceneDataFile );
306
307 if ( finishCallback )
308 {
309 finishCallback( true );
310 }
311
312 return true;
313 }
314
316 {
317 const Configuration& config = _context.config();
318
319 const Str<256>& sceneName = resourceName();
320 const ResourcePath sceneLocation = GetSceneFullPath( *this );
321 const ResourcePath sceneDataFile( GetSceneRootFolder( parent() ) / (sceneName + ".xml") );
322
323 Console::printfn( LOCALE_STR( "XML_LOAD_SCENE" ), sceneName );
324
325 // A scene does not necessarily need external data files
326 // Data can be added in code for simple scenes
327 if ( !fileExists( sceneDataFile ) )
328 {
329 sceneGraph()->loadFromXML( ResourcePath{ "assets.xml" });
330 loadMusicPlaylist( sceneLocation, "musicPlaylist.xml", this, config );
331 return true;
332 }
333
334 boost::property_tree::ptree pt;
335 XML::readXML( sceneDataFile, pt );
336
337 state()->renderState().grassVisibility( pt.get( "vegetation.grassVisibility", state()->renderState().grassVisibility() ) );
338 state()->renderState().treeVisibility( pt.get( "vegetation.treeVisibility", state()->renderState().treeVisibility() ) );
339 state()->renderState().generalVisibility( pt.get( "options.visibility", state()->renderState().generalVisibility() ) );
340
341 state()->windDirX( pt.get( "wind.windDirX", state()->windDirX() ) );
342 state()->windDirZ( pt.get( "wind.windDirZ", state()->windDirZ() ) );
343 state()->windSpeed( pt.get( "wind.windSpeed", state()->windSpeed() ) );
344
345 state()->lightBleedBias( pt.get( "shadowing.<xmlattr>.lightBleedBias", state()->lightBleedBias() ) );
346 state()->minShadowVariance( pt.get( "shadowing.<xmlattr>.minShadowVariance", state()->minShadowVariance() ) );
347
348 dayNightCycleEnabled( pt.get( "dayNight.<xmlattr>.enabled", false ) );
349 _dayNightData._time._hour = pt.get<U8>( "dayNight.timeOfDay.<xmlattr>.hour", _dayNightData._time._hour );
350 _dayNightData._time._minutes = pt.get<U8>( "dayNight.timeOfDay.<xmlattr>.minute", _dayNightData._time._minutes );
351 _dayNightData._location._latitude = pt.get<F32>( "dayNight.location.<xmlattr>.latitude", _dayNightData._location._latitude );
352 _dayNightData._location._longitude = pt.get<F32>( "dayNight.location.<xmlattr>.longitude", _dayNightData._location._longitude );
353 _dayNightData._speedFactor = pt.get( "dayNight.timeOfDay.<xmlattr>.timeFactor", _dayNightData._speedFactor );
354 _dayNightData._resetTime = true;
355
356 FogDetails details = {};
358 details._colourSunScatter.a = config.rendering.fogScatter;
359
360 if ( pt.get_child_optional( "fog" ) )
361 {
362 details._colourAndDensity = {
363 pt.get<F32>( "fog.fogColour.<xmlattr>.r", details._colourAndDensity.r ),
364 pt.get<F32>( "fog.fogColour.<xmlattr>.g", details._colourAndDensity.g ),
365 pt.get<F32>( "fog.fogColour.<xmlattr>.b", details._colourAndDensity.b ),
366 pt.get( "fog.fogDensity", details._colourAndDensity.a )
367 };
368 details._colourSunScatter.a = pt.get( "fog.fogScatter", details._colourSunScatter.a );
369 }
370
371 state()->renderState().fogDetails( details );
372
373 vec4<U16> lodThresholds( config.rendering.lodThresholds );
374
375 if ( pt.get_child_optional( "lod" ) )
376 {
377 lodThresholds.set( pt.get<U16>( "lod.lodThresholds.<xmlattr>.x", lodThresholds.x ),
378 pt.get<U16>( "lod.lodThresholds.<xmlattr>.y", lodThresholds.y ),
379 pt.get<U16>( "lod.lodThresholds.<xmlattr>.z", lodThresholds.z ),
380 pt.get<U16>( "lod.lodThresholds.<xmlattr>.w", lodThresholds.w ) );
381 }
382
383 state()->renderState().lodThresholds().set( lodThresholds );
384 sceneGraph()->loadFromXML( ResourcePath{ pt.get( "assets", "assets.xml" ).c_str() } );
385 loadMusicPlaylist( sceneLocation, pt.get( "musicPlaylist", "" ).c_str(), this, config );
386
387 return true;
388 }
389
390
391 template<typename T>
392 SceneGraphNode* addSGN( SceneGraphNode* parent, const std::string_view name, const U32 componentMask, const Handle<T> handle, const bool nodeStatic, boost::property_tree::ptree& nodeTree)
393 {
394 SceneGraphNodeDescriptor nodeDescriptor = {};
395 nodeDescriptor._name = name;
396 nodeDescriptor._componentMask = componentMask;
397 nodeDescriptor._nodeHandle = FromHandle(handle);
399
400 for ( auto i = 1u; i < to_base( ComponentType::COUNT ) + 1; ++i )
401 {
402 const U32 componentBit = 1u << i;
403 const ComponentType type = static_cast<ComponentType>(componentBit);
404 if ( nodeTree.count( TypeUtil::ComponentTypeToString( type ) ) != 0 )
405 {
406 nodeDescriptor._componentMask |= componentBit;
407 }
408 }
409
410 SceneGraphNode* crtNode = parent->addChildNode( nodeDescriptor );
411
412 crtNode->loadFromXML( nodeTree );
413
414 return crtNode;
415 }
416
417 void Scene::loadAsset( const Task* parentTask, const XML::SceneNode& sceneNode, SceneGraphNode* parent )
418 {
419 assert( parent != nullptr );
420
421 const ResourcePath targetFile = ResourcePath{ Util::StringFormat("{}_{}", parent->name().c_str(), sceneNode.name.c_str()) };
422 const ResourcePath nodePath = GetSceneFullPath( *this ) / Paths::g_nodesSaveLocation / (Util::MakeXMLSafe( targetFile ).string() + ".xml");
423
424 SceneGraphNode* crtNode = parent;
425 if ( fileExists( nodePath ) )
426 {
427
428 U32 normalMask = to_base( ComponentType::TRANSFORM ) |
431
432
433 boost::property_tree::ptree nodeTree = {};
434 XML::readXML( nodePath, nodeTree );
435
436 const auto IsPrimitive = []( const U64 nameHash )
437 {
438 constexpr U64 primitiveNames[3]
439 {
440 _ID( "BOX_3D" ),
441 _ID( "QUAD_3D" ),
442 _ID( "SPHERE_3D" )
443 };
444
445 for ( const U64 it : primitiveNames )
446 {
447 if ( nameHash == it )
448 {
449 return true;
450 }
451 }
452
453 return false;
454 };
455
456 const std::string modelName = nodeTree.get( "model", "" );
457
458 bool nodeStatic = true;
459
460 if ( IsPrimitive( sceneNode.typeHash ) )
461 {// Primitive types (only top level)
462 normalMask |= to_base( ComponentType::RENDERING ) |
464
465 SceneNode* ret = nullptr;
466 if ( !modelName.empty() )
467 {
468 if ( Util::CompareIgnoreCase( modelName, "BOX_3D" ) )
469 {
470 ResourceDescriptor<Box3D> item( sceneNode.name.c_str() );
471 item.assetName( modelName.c_str() );
472 const Handle<Box3D> handle = CreateResource( item, _loadingTasks );
473 ret = Get(handle);
474
475 crtNode = addSGN( parent, sceneNode.name, normalMask, handle, nodeStatic, nodeTree );
476 }
477 else if ( Util::CompareIgnoreCase( modelName, "SPHERE_3D" ) )
478 {
479 ResourceDescriptor<Sphere3D> item( sceneNode.name.c_str() );
480 item.assetName( modelName.c_str() );
481 const Handle<Sphere3D> handle = CreateResource( item, _loadingTasks );
482 ret = Get(handle);
483
484 crtNode = addSGN( parent, sceneNode.name, normalMask, handle, nodeStatic, nodeTree );
485 }
486 else if ( Util::CompareIgnoreCase( modelName, "QUAD_3D" ) )
487 {
488 ResourceDescriptor<Quad3D> item( sceneNode.name.c_str() );
489 item.assetName( modelName.c_str() );
490
491 P32 quadMask;
492 quadMask.i = 0;
493 quadMask.b[0] = 1;
494 item.mask( quadMask );
496 ret = Get(handle);
497
498 Quad3D* quad = static_cast<Quad3D*>(ret);
499 quad->setCorner( Quad3D::CornerLocation::TOP_LEFT, vec3<F32>( 0, 1, 0 ) );
500 quad->setCorner( Quad3D::CornerLocation::TOP_RIGHT, vec3<F32>( 1, 1, 0 ) );
501 quad->setCorner( Quad3D::CornerLocation::BOTTOM_LEFT, vec3<F32>( 0, 0, 0 ) );
502 quad->setCorner( Quad3D::CornerLocation::BOTTOM_RIGHT, vec3<F32>( 1, 0, 0 ) );
503
504 crtNode = addSGN( parent, sceneNode.name, normalMask, handle, nodeStatic, nodeTree );
505 }
506 else
507 {
508 Console::errorfn( LOCALE_STR( "ERROR_SCENE_UNSUPPORTED_GEOM" ), sceneNode.name );
509 }
510 }
511 if ( ret != nullptr )
512 {
513 ResourceDescriptor<Material> materialDescriptor( (sceneNode.name + "_material").c_str() );
514 Handle<Material> tempMaterial = CreateResource( materialDescriptor, _loadingTasks );
515 Get(tempMaterial)->properties().shadingMode( ShadingMode::PBR_MR );
516 ret->setMaterialTpl( tempMaterial );
517 ret->loadFromXML( nodeTree );
518 }
519 }
520 else
521 {
522 switch ( sceneNode.typeHash )
523 {
524 case _ID( "ROOT" ):
525 {
526 // Nothing to do with the root. This hasn't been used for a while
527 } break;
528 case _ID( "TERRAIN" ):
529 {
530 normalMask |= to_base( ComponentType::RENDERING );
531 addTerrain( parent, nodeTree, sceneNode.name );
532 } break;
533 case _ID( "VEGETATION_GRASS" ):
534 {
535 normalMask |= to_base( ComponentType::RENDERING );
536 NOP(); //we rebuild grass every time
537 } break;
538 case _ID( "INFINITE_PLANE" ):
539 {
540 normalMask |= to_base( ComponentType::RENDERING );
541 if ( !addInfPlane( parent, nodeTree, sceneNode.name ) )
542 {
544 }
545 } break;
546 case _ID( "WATER" ):
547 {
548 normalMask |= to_base( ComponentType::RENDERING );
549 addWater( parent, nodeTree, sceneNode.name );
550 } break;
551 case _ID( "MESH" ):
552 {
553 if ( !modelName.empty() )
554 {
555 ResourceDescriptor<Mesh> model( modelName );
556 model.assetLocation( Paths::g_modelsLocation );
557 model.assetName( modelName.c_str() );
558 model.flag( true );
559 const Handle<Mesh> handle = CreateResource( model, _loadingTasks );
560
561 ResourcePtr<Mesh> meshPtr = Get(handle);
562 nodeStatic = meshPtr->animationCount() == 0u;
563 meshPtr->loadFromXML( nodeTree );
564
565 crtNode = addSGN( parent, sceneNode.name, normalMask, handle, nodeStatic, nodeTree );
566 }
567 } break;
568 // SubMesh (change component properties, as the meshes should already be loaded)
569 case _ID( "SUBMESH" ):
570 {
571 while ( parent->getNode().getState() != ResourceState::RES_LOADED )
572 {
573 if ( parentTask != nullptr && !idle() )
574 {
575 NOP();
576 }
577 }
578 SceneGraphNode* subMesh = parent->findChild( _ID( sceneNode.name.c_str() ), false, false );
579 if ( subMesh != nullptr )
580 {
581 subMesh->loadFromXML( nodeTree );
582 }
583 } break;
584 case _ID( "SKY" ):
585 {
586 //ToDo: Change this - Currently, just load the default sky.
587 normalMask |= to_base( ComponentType::RENDERING );
588 if ( !addSky( parent, nodeTree, sceneNode.name ) )
589 {
591 }
592 } break;
593 // Everything else
594 default:
595 case _ID( "TRANSFORM" ):
596 {
597 ResourceDescriptor<TransformNode> transform(sceneNode.name);
598 Handle<TransformNode> handle = CreateResource(transform);
599
600 normalMask &= ~to_base( ComponentType::BOUNDS );
601 crtNode = addSGN( parent, sceneNode.name, normalMask, handle, nodeStatic, nodeTree );
602 } break;
603 }
604 }
605 }
606
607 const U32 childCount = to_U32( sceneNode.children.size() );
608 if ( childCount == 1u )
609 {
610 loadAsset( parentTask, sceneNode.children.front(), crtNode );
611 }
612 else if ( childCount > 1u )
613 {
614 ParallelForDescriptor descriptor = {};
615 descriptor._iterCount = childCount;
616 descriptor._partitionSize = 3u;
618 descriptor._useCurrentThread = true;
619 Parallel_For( _context.taskPool( TaskPoolType::ASSET_LOADER ), descriptor, [this, &sceneNode, &crtNode]( const Task* innerTask, const U32 start, const U32 end )
620 {
621 for ( U32 i = start; i < end; ++i )
622 {
623 loadAsset( innerTask, sceneNode.children[i], crtNode );
624 }
625 });
626 }
627 }
628
629 SceneGraphNode* Scene::addParticleEmitter( const std::string_view name,
630 std::shared_ptr<ParticleData> data,
631 SceneGraphNode* parentNode )
632 {
633 DIVIDE_ASSERT( !name.empty(),
634 "Scene::addParticleEmitter error: invalid name specified!" );
635
636 const ResourceDescriptor<ParticleEmitter> particleEmitter( name );
637 Handle<ParticleEmitter> emitter = CreateResource( particleEmitter, _loadingTasks );
638
639 DIVIDE_ASSERT( emitter != INVALID_HANDLE<ParticleEmitter>, "Scene::addParticleEmitter error: Could not instantiate emitter!" );
640
641
642 auto initData = [emitter, data]( )
643 {
644 if ( !Get(emitter)->initData( data ) )
645 {
647 }
648 };
649
650 TaskPool& pool = _context.taskPool( TaskPoolType::ASSET_LOADER );
651 Task* initTask = CreateTask( TASK_NOP );
652 Start( *initTask, pool, TaskPriority::DONT_CARE, initData);
653 Wait( *initTask, pool);
654
655 SceneGraphNodeDescriptor particleNodeDescriptor;
656 particleNodeDescriptor._nodeHandle = FromHandle(emitter);
657 particleNodeDescriptor._usageContext = NodeUsageContext::NODE_DYNAMIC;
658 particleNodeDescriptor._componentMask = to_base( ComponentType::TRANSFORM ) |
659 to_base( ComponentType::BOUNDS ) |
660 to_base( ComponentType::RENDERING ) |
661 to_base( ComponentType::NETWORKING ) |
662 to_base( ComponentType::SELECTION );
663
664 return parentNode->addChildNode( particleNodeDescriptor );
665 }
666
667 void Scene::addTerrain( SceneGraphNode* parentNode, const boost::property_tree::ptree& pt, const Str<64>& nodeName )
668 {
669 Console::printfn( LOCALE_STR( "XML_LOAD_TERRAIN" ), nodeName.c_str() );
670
671 // Load the rest of the terrain
672 TerrainDescriptor ter{};
673 Init(ter, nodeName + "_descriptor" );
674
675 if ( !LoadFromXML( ter, pt, nodeName.c_str() ) )
676 {
677 return;
678 }
679
680 ResourceDescriptor<Terrain> descriptor( GetVariable( ter, "terrainName" ), ter );
681 descriptor.flag( ter._active );
682
683 Handle<Terrain> terrain = CreateResource( descriptor, _loadingTasks );
684 Get(terrain)->loadFromXML( pt );
685
686 SceneGraphNodeDescriptor terrainNodeDescriptor;
687 terrainNodeDescriptor._name = nodeName;
688 terrainNodeDescriptor._nodeHandle = FromHandle( terrain );
689 terrainNodeDescriptor._usageContext = NodeUsageContext::NODE_STATIC;
690 terrainNodeDescriptor._componentMask = to_base( ComponentType::NAVIGATION ) |
691 to_base( ComponentType::TRANSFORM ) |
692 to_base( ComponentType::RIGID_BODY ) |
693 to_base( ComponentType::BOUNDS ) |
694 to_base( ComponentType::RENDERING ) |
695 to_base( ComponentType::NETWORKING );
696
697 SceneGraphNode* terrainTemp = parentNode->addChildNode( terrainNodeDescriptor );
698
699 NavigationComponent* nComp = terrainTemp->get<NavigationComponent>();
700 nComp->navigationContext( NavigationComponent::NavigationContext::NODE_OBSTACLE );
701
702 terrainTemp->loadFromXML( pt );
703
704 }
705
706 void Scene::toggleFlashlight( const PlayerIndex idx )
707 {
708 SceneGraphNode*& flashLight = _flashLight[idx];
709 if ( !flashLight )
710 {
711 ResourceDescriptor<TransformNode> descriptor( Util::StringFormat( "Flashlight_{}", idx ) );
712
713 SceneGraphNodeDescriptor lightNodeDescriptor;
714 lightNodeDescriptor._serialize = false;
715 lightNodeDescriptor._nodeHandle = FromHandle( CreateResource(descriptor) );
716 lightNodeDescriptor._name = descriptor.resourceName().c_str();
717 lightNodeDescriptor._usageContext = NodeUsageContext::NODE_DYNAMIC;
718 lightNodeDescriptor._componentMask = to_base( ComponentType::TRANSFORM ) |
719 to_base( ComponentType::BOUNDS ) |
720 to_base( ComponentType::NETWORKING ) |
721 to_base( ComponentType::SPOT_LIGHT );
722 flashLight = _sceneGraph->getRoot()->addChildNode( lightNodeDescriptor );
723 SpotLightComponent* spotLight = flashLight->get<SpotLightComponent>();
724 spotLight->castsShadows( true );
725 spotLight->setDiffuseColour( DefaultColours::WHITE.rgb );
726 flashLight->get<BoundsComponent>()->collisionsEnabled(false);
727
728 _flashLight[idx] = flashLight;
729
730 _cameraUpdateListeners[idx] = playerCamera( idx )->addUpdateListener( [this, idx]( const Camera& cam )
731 {
732 if ( idx < Config::MAX_LOCAL_PLAYER_COUNT && idx < _flashLight.size() && _flashLight[idx] )
733 {
734 if ( cam.getGUID() == playerCamera( idx )->getGUID() )
735 {
736 TransformComponent* tComp = _flashLight[idx]->get<TransformComponent>();
737 tComp->setPosition( cam.snapshot()._eye );
738 tComp->setRotationEuler( cam.euler() );
739 }
740 }
741 } );
742 }
743
744 flashLight->get<SpotLightComponent>()->toggleEnabled();
745 }
746
747 SceneGraphNode* Scene::addSky( SceneGraphNode* parentNode, const boost::property_tree::ptree& pt, const Str<64>& nodeName )
748 {
749 ResourceDescriptor<Sky> skyDescriptor( ("DefaultSky_" + nodeName).c_str() );
750 //skyDescriptor.ID(2);
751
752 //ToDo: Double check that this diameter is correct, otherwise fall back to default of "2"
753 skyDescriptor.ID( to_U32( std::floor( Camera::GetUtilityCamera( Camera::UtilityCamera::DEFAULT )->snapshot()._zPlanes.max * 2 ) ) );
754
755 const Handle<Sky> handle = CreateResource( skyDescriptor, _loadingTasks );
756 ResourcePtr<Sky> skyItem = Get(handle);
757 skyItem->loadFromXML( pt );
758
759 SceneGraphNodeDescriptor skyNodeDescriptor;
760 skyNodeDescriptor._nodeHandle = FromHandle( handle );
761 skyNodeDescriptor._name = nodeName;
762 skyNodeDescriptor._usageContext = NodeUsageContext::NODE_STATIC;
763 skyNodeDescriptor._componentMask = to_base( ComponentType::TRANSFORM ) |
764 to_base( ComponentType::BOUNDS ) |
765 to_base( ComponentType::RENDERING ) |
766 to_base( ComponentType::NETWORKING );
767
768 SceneGraphNode* skyNode = parentNode->addChildNode( skyNodeDescriptor );
769 skyNode->setFlag( SceneGraphNode::Flags::VISIBILITY_LOCKED );
770 skyNode->loadFromXML( pt );
771 skyNode->get<BoundsComponent>()->collisionsEnabled( false );
772
773 return skyNode;
774 }
775
776 void Scene::addWater( SceneGraphNode* parentNode, const boost::property_tree::ptree& pt, const Str<64>& nodeName )
777 {
778
779 ResourceDescriptor<WaterPlane> waterDescriptor( ("Water_" + nodeName).c_str() );
780 Handle<WaterPlane> water = CreateResource( waterDescriptor, _loadingTasks );
781 Get(water)->loadFromXML( pt );
782
783 SceneGraphNodeDescriptor waterNodeDescriptor;
784 waterNodeDescriptor._name = nodeName;
785 waterNodeDescriptor._nodeHandle = FromHandle( water );
786 waterNodeDescriptor._usageContext = NodeUsageContext::NODE_STATIC;
787 waterNodeDescriptor._componentMask = to_base( ComponentType::NAVIGATION ) |
788 to_base( ComponentType::TRANSFORM ) |
789 to_base( ComponentType::RIGID_BODY ) |
790 to_base( ComponentType::BOUNDS ) |
791 to_base( ComponentType::RENDERING ) |
792 to_base( ComponentType::NETWORKING );
793
794
795 SceneGraphNode* waterNode = parentNode->addChildNode( waterNodeDescriptor );
796 waterNode->loadFromXML( pt );
797 }
798
799 SceneGraphNode* Scene::addInfPlane( SceneGraphNode* parentNode, const boost::property_tree::ptree& pt, const Str<64>& nodeName )
800 {
801 ResourceDescriptor<InfinitePlane> planeDescriptor( ("InfPlane_" + nodeName).c_str() );
802
803 const Camera* baseCamera = Camera::GetUtilityCamera( Camera::UtilityCamera::DEFAULT );
804
805 const U32 cameraFarPlane = to_U32( baseCamera->snapshot()._zPlanes.max );
806 planeDescriptor.data().set( cameraFarPlane, cameraFarPlane, 0u );
807 Handle<InfinitePlane> planeItem = CreateResource( planeDescriptor, _loadingTasks );
808 Get(planeItem)->loadFromXML( pt );
809
810 DIVIDE_ASSERT( planeItem != INVALID_HANDLE<InfinitePlane>, "Scene::addInfPlane error: Could not create infinite plane resource!" );
811
812 SceneGraphNodeDescriptor planeNodeDescriptor;
813 planeNodeDescriptor._nodeHandle = FromHandle( planeItem );
814 planeNodeDescriptor._name = nodeName;
815 planeNodeDescriptor._usageContext = NodeUsageContext::NODE_STATIC;
816 planeNodeDescriptor._componentMask = to_base( ComponentType::TRANSFORM ) |
817 to_base( ComponentType::BOUNDS ) |
818 to_base( ComponentType::RENDERING );
819
820 SceneGraphNode* ret = parentNode->addChildNode( planeNodeDescriptor );
821 ret->loadFromXML( pt );
822 return ret;
823 }
824
825 U16 Scene::registerInputActions()
826 {
827 _input->flushCache();
828
829 const auto none = []( [[maybe_unused]] const InputParams params) noexcept {};
830
831 const auto deleteSelection = [this]( const InputParams params )
832 {
833 const PlayerIndex idx = getPlayerIndexForDevice( params._deviceIndex );
834 Selections& playerSelections = _currentSelection[idx];
835 for ( U8 i = 0u; i < playerSelections._selectionCount; ++i )
836 {
837 _sceneGraph->removeNode( playerSelections._selections[i] );
838 }
839 playerSelections._selectionCount = 0u;
840 playerSelections._selections.fill( -1 );
841 };
842
843 const auto increaseCameraSpeed = [this]( const InputParams params )
844 {
845 Camera* cam = playerCamera( getPlayerIndexForDevice( params._deviceIndex ) );
846 if ( cam->mode() != Camera::Mode::STATIC &&
847 cam->mode() != Camera::Mode::SCRIPTED )
848 {
849 if ( cam->speedFactor().move < Camera::MAX_CAMERA_MOVE_SPEED )
850 {
851 cam->speedFactor().move += 1.f;
852 }
853 if ( cam->speedFactor().turn < Camera::MAX_CAMERA_TURN_SPEED )
854 {
855 cam->speedFactor().turn += 1.f;
856 }
857 }
858 };
859
860 const auto decreaseCameraSpeed = [this]( const InputParams params )
861 {
862 Camera* cam = playerCamera( getPlayerIndexForDevice( params._deviceIndex ) );
863 if ( cam->mode() != Camera::Mode::STATIC &&
864 cam->mode() != Camera::Mode::SCRIPTED )
865 {
866 if ( cam->speedFactor().move > 1.f )
867 {
868 cam->speedFactor().move -= 1.f;
869 }
870 if ( cam->speedFactor().turn > 1.f )
871 {
872 cam->speedFactor().turn -= 1.f;
873 }
874 }
875 };
876
877 const auto increaseResolution = [this]( [[maybe_unused]] const InputParams params )
878 {
879 _context.app().windowManager().increaseResolution();
880 };
881 const auto decreaseResolution = [this]( [[maybe_unused]] const InputParams params )
882 {
883 _context.app().windowManager().decreaseResolution();
884 };
885
886 const auto moveForward = [this]( const InputParams params )
887 {
888 state()->playerState( getPlayerIndexForDevice( params._deviceIndex ) )._moveFB.push( {255u, MoveDirection::POSITIVE} );
889 };
890
891 const auto moveBackwards = [this]( const InputParams params )
892 {
893 state()->playerState( getPlayerIndexForDevice( params._deviceIndex ) )._moveFB.push( {255u, MoveDirection::NEGATIVE} );
894 };
895
896 const auto stopMoveFWDBCK = [this]( const InputParams params )
897 {
898 state()->playerState( getPlayerIndexForDevice( params._deviceIndex ) )._moveFB.push( {255u, MoveDirection::NONE});
899 };
900
901 const auto strafeLeft = [this]( const InputParams params )
902 {
903 state()->playerState( getPlayerIndexForDevice( params._deviceIndex ) )._moveLR.push( {255u, MoveDirection::NEGATIVE} );
904 };
905
906 const auto strafeRight = [this]( const InputParams params )
907 {
908 state()->playerState( getPlayerIndexForDevice( params._deviceIndex ) )._moveLR.push( {255u, MoveDirection::POSITIVE} );
909 };
910
911 const auto stopStrafeLeftRight = [this]( const InputParams params )
912 {
913 state()->playerState( getPlayerIndexForDevice( params._deviceIndex ) )._moveLR.push( {255u, MoveDirection::NONE} );
914 };
915
916 const auto rollCCW = [this]( const InputParams params )
917 {
918 state()->playerState( getPlayerIndexForDevice( params._deviceIndex ) )._roll.push( {255u, MoveDirection::NEGATIVE} );
919 };
920
921 const auto rollCW = [this]( const InputParams params )
922 {
923 state()->playerState( getPlayerIndexForDevice( params._deviceIndex ) )._roll.push( {255u, MoveDirection::POSITIVE} );
924 };
925
926 const auto stopRollCCWCW = [this]( const InputParams params )
927 {
928 state()->playerState( getPlayerIndexForDevice( params._deviceIndex ) )._roll.push( {255u, MoveDirection::NONE} );
929 };
930
931 const auto turnLeft = [this]( const InputParams params )
932 {
933 state()->playerState( getPlayerIndexForDevice( params._deviceIndex ) )._angleLR.push( {255u, MoveDirection::NEGATIVE} );
934 };
935
936 const auto turnRight = [this]( const InputParams params )
937 {
938 state()->playerState( getPlayerIndexForDevice( params._deviceIndex ) )._angleLR.push( {255u, MoveDirection::POSITIVE} );
939 };
940
941 const auto stopTurnLeftRight = [this]( const InputParams params )
942 {
943 state()->playerState( getPlayerIndexForDevice( params._deviceIndex ) )._angleLR.push( {255u, MoveDirection::NONE} );
944 };
945
946 const auto turnUp = [this]( const InputParams params )
947 {
948 state()->playerState( getPlayerIndexForDevice( params._deviceIndex ) )._angleUD.push( {255u, MoveDirection::NEGATIVE} );
949 };
950
951 const auto turnDown = [this]( const InputParams params )
952 {
953 state()->playerState( getPlayerIndexForDevice( params._deviceIndex ) )._angleUD.push( {255u, MoveDirection::POSITIVE} );
954 };
955
956 const auto stopTurnUpDown = [this]( const InputParams params )
957 {
958 state()->playerState( getPlayerIndexForDevice( params._deviceIndex ) )._angleUD.push( {255u, MoveDirection::NONE} );
959 };
960
961 const auto togglePauseState = [this]( [[maybe_unused]] const InputParams params ) noexcept
962 {
963 _context.kernel().timingData().freezeGameTime( !_context.kernel().timingData().freezeGameTime() );
964 };
965
966 const auto takeScreenShot = [this]( [[maybe_unused]] const InputParams params )
967 {
968 state()->screenshotRequestQueued(true);
969 };
970
971 const auto toggleFullScreen = [this]( [[maybe_unused]] const InputParams params )
972 {
973 _context.app().windowManager().toggleFullScreen();
974 };
975
976 const auto toggleFlashLight = [this]( const InputParams params )
977 {
978 toggleFlashlight( getPlayerIndexForDevice( params._deviceIndex ) );
979 };
980
981 const auto lockCameraToMouse = [this]( const InputParams params )
982 {
983 if ( !lockCameraToPlayerMouse( getPlayerIndexForDevice( params._deviceIndex ), true ) )
984 {
985 NOP();
986 }
987 };
988 const auto releaseCameraFromMouse = [this]( const InputParams params )
989 {
990 if ( !lockCameraToPlayerMouse( getPlayerIndexForDevice( params._deviceIndex ), false ) )
991 {
992 NOP();
993 }
994 };
995
996 const auto shutdown = [this]( [[maybe_unused]] const InputParams params ) noexcept
997 {
998 _context.app().RequestShutdown(false);
999 };
1000
1001 const auto povNavigation = [this]( const InputParams params )
1002 {
1003 const U32 povMask = to_U32(params._var[0]); // cast back
1004
1005 if ( povMask & to_base( Input::JoystickPovDirection::UP ) )
1006 { // Going up
1007 state()->playerState( getPlayerIndexForDevice( params._deviceIndex ) )._moveFB.push( {255u, MoveDirection::POSITIVE} );
1008 }
1009 if ( povMask & to_base( Input::JoystickPovDirection::DOWN ) )
1010 { // Going down
1011 state()->playerState( getPlayerIndexForDevice( params._deviceIndex ) )._moveFB.push( { 255u, MoveDirection::NEGATIVE} );
1012 }
1013 if ( povMask & to_base( Input::JoystickPovDirection::RIGHT ) )
1014 { // Going right
1015 state()->playerState( getPlayerIndexForDevice( params._deviceIndex ) )._moveLR.push( { 255u, MoveDirection::POSITIVE} );
1016 }
1017 if ( povMask & to_base( Input::JoystickPovDirection::LEFT ) )
1018 { // Going left
1019 state()->playerState( getPlayerIndexForDevice( params._deviceIndex ) )._moveLR.push( { 255u, MoveDirection::NEGATIVE} );
1020 }
1021 if ( povMask == to_base( Input::JoystickPovDirection::CENTERED ) )
1022 { // stopped/centered out
1023 state()->playerState( getPlayerIndexForDevice( params._deviceIndex ) )._moveLR.push( { 255u, MoveDirection::NONE } );
1024 state()->playerState( getPlayerIndexForDevice( params._deviceIndex ) )._moveFB.push( { 255u, MoveDirection::NONE } );
1025 }
1026 };
1027
1028 const auto axisNavigation = [this]( const InputParams params )
1029 {
1030 const U8 axis = params._elementIndex;
1031
1032 [[maybe_unused]] const bool isGamePad = params._var[0] == 1;
1033
1034 const I32 deadZone = params._var[1];
1035 const I32 axisABS = params._var[2];
1036
1037 const U8 axisPercentage = to_U8((axisABS / to_F32(I16_MAX)) * 255.f);
1038
1039 switch ( axis )
1040 {
1041 case 0:
1042 {
1043 if ( axisABS > deadZone )
1044 {
1045 state()->playerState( getPlayerIndexForDevice( params._deviceIndex ) )._angleUD.push( { axisPercentage, MoveDirection::POSITIVE } );
1046 }
1047 else if ( axisABS < -deadZone )
1048 {
1049 state()->playerState( getPlayerIndexForDevice( params._deviceIndex ) )._angleUD.push( { axisPercentage, MoveDirection::NEGATIVE } );
1050 }
1051 else
1052 {
1053 state()->playerState( getPlayerIndexForDevice( params._deviceIndex ) )._angleUD.push( { 255u, MoveDirection::NONE} );
1054 }
1055 } break;
1056 case 1:
1057 {
1058 if ( axisABS > deadZone )
1059 {
1060 state()->playerState( getPlayerIndexForDevice( params._deviceIndex ) )._angleLR.push( { axisPercentage, MoveDirection::POSITIVE } );
1061 }
1062 else if ( axisABS < -deadZone )
1063 {
1064 state()->playerState( getPlayerIndexForDevice( params._deviceIndex ) )._angleLR.push( { axisPercentage, MoveDirection::NEGATIVE } );
1065 }
1066 else
1067 {
1068 state()->playerState( getPlayerIndexForDevice( params._deviceIndex ) )._angleLR.push( { 255u, MoveDirection::NONE} );
1069 }
1070 } break;
1071
1072 case 2:
1073 {
1074 if ( axisABS < -deadZone )
1075 {
1076 state()->playerState( getPlayerIndexForDevice( params._deviceIndex ) )._moveFB.push( { axisPercentage, MoveDirection::POSITIVE} );
1077 }
1078 else if ( axisABS > deadZone )
1079 {
1080 state()->playerState( getPlayerIndexForDevice( params._deviceIndex ) )._moveFB.push( { axisPercentage, MoveDirection::NEGATIVE} );
1081 }
1082 else
1083 {
1084 state()->playerState( getPlayerIndexForDevice( params._deviceIndex ) )._moveFB.push( { 255u, MoveDirection::NONE} );
1085 }
1086 } break;
1087 case 3:
1088 {
1089 if ( axisABS < -deadZone )
1090 {
1091 state()->playerState( getPlayerIndexForDevice( params._deviceIndex ) )._moveLR.push( { axisPercentage, MoveDirection::NEGATIVE} );
1092 }
1093 else if ( axisABS > deadZone )
1094 {
1095 state()->playerState( getPlayerIndexForDevice( params._deviceIndex ) )._moveLR.push( { axisPercentage, MoveDirection::POSITIVE} );
1096 }
1097 else
1098 {
1099 state()->playerState( getPlayerIndexForDevice( params._deviceIndex ) )._moveLR.push( { 255u, MoveDirection::NONE} );
1100 }
1101 } break;
1102 default: DIVIDE_UNEXPECTED_CALL(); break;
1103 }
1104 };
1105
1106 const auto toggleDebugInterface = [this]( [[maybe_unused]] const InputParams params ) noexcept
1107 {
1108 _context.debug().enabled( !_context.debug().enabled() );
1109 };
1110
1111 const auto toggleEditor = [this]( [[maybe_unused]] const InputParams params )
1112 {
1113 if constexpr( Config::Build::ENABLE_EDITOR )
1114 {
1115 _context.editor().toggle( !_context.editor().running() );
1116 }
1117 };
1118
1119 const auto toggleConsole = [this]( [[maybe_unused]] const InputParams params )
1120 {
1121 if constexpr( Config::Build::ENABLE_EDITOR )
1122 {
1123 _context.gui().getConsole().setVisible( !_context.gui().getConsole().isVisible() );
1124 }
1125 };
1126
1127 const auto dragSelectBegin = [this]( const InputParams params )
1128 {
1129 beginDragSelection( getPlayerIndexForDevice( params._deviceIndex ), vec2<I32>( params._var[0], params._var[1] ) );
1130 };
1131 const auto dragSelectEnd = [this]( const InputParams params )
1132 {
1133 endDragSelection( getPlayerIndexForDevice( params._deviceIndex ), true );
1134 };
1135
1136 InputActionList& actions = _input->actionList();
1137 bool ret = true;
1138 U16 actionID = 0;
1139 ret = actions.registerInputAction( actionID++, none ) && ret; // 0
1140 ret = actions.registerInputAction( actionID++, deleteSelection ) && ret; // 1
1141 ret = actions.registerInputAction( actionID++, increaseCameraSpeed ) && ret; // 2
1142 ret = actions.registerInputAction( actionID++, decreaseCameraSpeed ) && ret; // 3
1143 ret = actions.registerInputAction( actionID++, increaseResolution ) && ret; // 4
1144 ret = actions.registerInputAction( actionID++, decreaseResolution ) && ret; // 5
1145 ret = actions.registerInputAction( actionID++, moveForward ) && ret; // 6
1146 ret = actions.registerInputAction( actionID++, moveBackwards ) && ret; // 7
1147 ret = actions.registerInputAction( actionID++, stopMoveFWDBCK ) && ret; // 8
1148 ret = actions.registerInputAction( actionID++, strafeLeft ) && ret; // 9
1149 ret = actions.registerInputAction( actionID++, strafeRight ) && ret; // 10
1150 ret = actions.registerInputAction( actionID++, stopStrafeLeftRight ) && ret; // 11
1151 ret = actions.registerInputAction( actionID++, rollCCW ) && ret; // 12
1152 ret = actions.registerInputAction( actionID++, rollCW ) && ret; // 13
1153 ret = actions.registerInputAction( actionID++, stopRollCCWCW ) && ret; // 14
1154 ret = actions.registerInputAction( actionID++, turnLeft ) && ret; // 15
1155 ret = actions.registerInputAction( actionID++, turnRight ) && ret; // 16
1156 ret = actions.registerInputAction( actionID++, stopTurnLeftRight ) && ret; // 17
1157 ret = actions.registerInputAction( actionID++, turnUp ) && ret; // 18
1158 ret = actions.registerInputAction( actionID++, turnDown ) && ret; // 19
1159 ret = actions.registerInputAction( actionID++, stopTurnUpDown ) && ret; // 20
1160 ret = actions.registerInputAction( actionID++, togglePauseState ) && ret; // 21
1161 ret = actions.registerInputAction( actionID++, takeScreenShot ) && ret; // 22
1162 ret = actions.registerInputAction( actionID++, toggleFullScreen ) && ret; // 23
1163 ret = actions.registerInputAction( actionID++, toggleFlashLight ) && ret; // 24
1164 ret = actions.registerInputAction( actionID++, lockCameraToMouse ) && ret; // 25
1165 ret = actions.registerInputAction( actionID++, releaseCameraFromMouse ) && ret; // 26
1166 ret = actions.registerInputAction( actionID++, shutdown ) && ret; // 27
1167 ret = actions.registerInputAction( actionID++, povNavigation ) && ret; // 28
1168 ret = actions.registerInputAction( actionID++, axisNavigation ) && ret; // 29
1169 ret = actions.registerInputAction( actionID++, toggleDebugInterface ) && ret; // 30
1170 ret = actions.registerInputAction( actionID++, toggleEditor ) && ret; // 31
1171 ret = actions.registerInputAction( actionID++, toggleConsole ) && ret; // 32
1172 ret = actions.registerInputAction( actionID++, dragSelectBegin ) && ret; // 33
1173 ret = actions.registerInputAction( actionID++, dragSelectEnd ) && ret; // 34
1174
1175 if ( !ret )
1176 {
1178 }
1179
1180 return actionID;
1181 }
1182
1183 bool Scene::lockCameraToPlayerMouse( const PlayerIndex index, const bool lockState ) const noexcept
1184 {
1185 static bool hadWindowGrab = false;
1186 static vec2<I32> lastMousePosition;
1187
1188 state()->playerState( index ).cameraLockedToMouse( lockState );
1189
1190 const DisplayWindow* window = _context.app().windowManager().getFocusedWindow();
1191 if ( lockState )
1192 {
1193 if ( window != nullptr )
1194 {
1195 hadWindowGrab = window->grabState();
1196 }
1197 lastMousePosition = WindowManager::GetGlobalCursorPosition();
1198 WindowManager::ToggleRelativeMouseMode( true );
1199 }
1200 else
1201 {
1202 WindowManager::ToggleRelativeMouseMode( false );
1203 state()->playerState( index ).resetMoveDirections();
1204 if ( window != nullptr )
1205 {
1206 window->grabState( hadWindowGrab );
1207 }
1208 WindowManager::SetGlobalCursorPosition( lastMousePosition.x, lastMousePosition.y );
1209 }
1210
1211 return true;
1212 }
1213
1214 bool Scene::load()
1215 {
1216 // Load the main scene from XML
1217 _sceneGraph->load();
1218
1219 if ( !loadXML() )
1220 {
1221 return false;
1222 }
1223
1224 const bool errorState = false;
1225
1226 setState( ResourceState::RES_LOADING );
1227
1228 Camera* baseCamera = Camera::GetUtilityCamera( Camera::UtilityCamera::DEFAULT );
1229 const F32 hFoV = _context.config().runtime.horizontalFOV;
1230 const F32 vFoV = Angle::to_VerticalFoV( hFoV, to_D64( baseCamera->snapshot()._aspectRatio ) );
1231 baseCamera->setProjection( vFoV, { Camera::s_minNearZ, _context.config().runtime.cameraViewDistance } );
1232 baseCamera->speedFactor().move = Camera::DEFAULT_CAMERA_MOVE_SPEED;
1233 baseCamera->speedFactor().turn = Camera::DEFAULT_CAMERA_TURN_SPEED;
1234 baseCamera->updateLookAt();
1235
1236 SceneGraphNode* rootNode = _sceneGraph->getRoot();
1237 vector<XML::SceneNode>& rootChildren = _xmlSceneGraphRootNode.children;
1238 const size_t childCount = rootChildren.size();
1239
1240 ParallelForDescriptor descriptor = {};
1241 descriptor._iterCount = to_U32( childCount );
1242 descriptor._partitionSize = 3u;
1243
1244 if ( descriptor._iterCount > descriptor._partitionSize * 3u )
1245 {
1246 descriptor._priority = TaskPriority::DONT_CARE;
1247 descriptor._useCurrentThread = true;
1248 descriptor._allowPoolIdle = true;
1249 descriptor._waitForFinish = true;
1250 Parallel_For( _context.taskPool( TaskPoolType::ASSET_LOADER ), descriptor, [this, &rootNode, &rootChildren]( const Task* parentTask, const U32 start, const U32 end )
1251 {
1252 for ( U32 i = start; i < end; ++i )
1253 {
1254 loadAsset( parentTask, rootChildren[i], rootNode );
1255 }
1256 });
1257 }
1258 else
1259 {
1260 for ( U32 i = 0u; i < descriptor._iterCount; ++i )
1261 {
1262 loadAsset( nullptr, rootChildren[i], rootNode );
1263 }
1264 }
1265 WAIT_FOR_CONDITION( _loadingTasks.load() == 0u );
1266
1267 // We always add a sky
1268 const auto& skies = sceneGraph()->getNodesByType( SceneNodeType::TYPE_SKY );
1269 assert( !skies.empty() );
1270 Sky& currentSky = skies[0]->getNode<Sky>();
1271 const auto& dirLights = _lightPool->getLights( LightType::DIRECTIONAL );
1272
1273 DirectionalLightComponent* sun = nullptr;
1274 for ( auto light : dirLights )
1275 {
1276 const auto dirLight = light->sgn()->get<DirectionalLightComponent>();
1277 if ( dirLight->tag() == SUN_LIGHT_TAG )
1278 {
1279 sun = dirLight;
1280 break;
1281 }
1282 }
1283 if ( sun != nullptr )
1284 {
1285 sun->castsShadows( true );
1286 initDayNightCycle( currentSky, *sun );
1287 }
1288
1289 if ( errorState )
1290 {
1291 Console::errorfn( LOCALE_STR( "ERROR_SCENE_LOAD" ), "scene load function" );
1292 return false;
1293 }
1294
1295 loadComplete( true );
1296 [[maybe_unused]] const U16 lastActionID = registerInputActions();
1297
1298 XML::loadDefaultKeyBindings( Paths::g_xmlDataLocation / "keyBindings.xml", this );
1299
1300 return postLoad();
1301 }
1302
1303 bool Scene::unload()
1304 {
1305 _aiManager->stop();
1306 WAIT_FOR_CONDITION( !_aiManager->running() );
1307
1308 U32 totalLoadingTasks = _loadingTasks.load();
1309 while ( totalLoadingTasks > 0 )
1310 {
1311 const U32 actualTasks = _loadingTasks.load();
1312 if ( totalLoadingTasks != actualTasks )
1313 {
1314 totalLoadingTasks = actualTasks;
1315 }
1316 std::this_thread::yield();
1317 }
1318
1319 clearTasks();
1320
1321 for ( const size_t idx : _selectionCallbackIndices )
1322 {
1323 _parent.parent().removeSelectionCallback( idx );
1324 }
1325 _selectionCallbackIndices.clear();
1326
1327 _context.pfx().destroyPhysicsScene( *this );
1328
1330 _xmlSceneGraphRootNode = {};
1331 _flashLight.fill( nullptr );
1332 _sceneGraph->unload();
1333
1334 loadComplete( false );
1335 DIVIDE_ASSERT( playerCount() == 0u );
1336 for ( const Player_ptr& player : _scenePlayers )
1337 {
1338 DIVIDE_ASSERT( player == nullptr );
1339 }
1340
1341 return true;
1342 }
1343
1344 bool Scene::postLoad()
1345 {
1346 PROFILE_SCOPE_AUTO( Profiler::Category::Scene );
1347
1348 _sceneGraph->postLoad();
1349
1350 return _context.pfx().initPhysicsScene( *this );
1351 }
1352
1353 void Scene::postLoadMainThread()
1354 {
1355 assert( Runtime::isMainThread() );
1356 setState( ResourceState::RES_LOADED );
1357 }
1358
1359 string Scene::GetPlayerSGNName( const PlayerIndex idx )
1360 {
1361 return Util::StringFormat( g_defaultPlayerName, idx + 1 );
1362 }
1363
1364 void Scene::currentPlayerPass( const PlayerIndex idx )
1365 {
1366 PROFILE_SCOPE_AUTO( Profiler::Category::GameLogic );
1367
1368 //ToDo: These don't necessarily need to match -Ionut
1369 updateCameraControls( idx );
1370 state()->renderState().renderPass( idx );
1371 state()->playerPass( idx );
1372
1373 if ( state()->playerState().cameraUnderwater() )
1374 {
1375 _context.gfx().getRenderer().postFX().pushFilter( FilterType::FILTER_UNDERWATER );
1376 }
1377 else
1378 {
1379 _context.gfx().getRenderer().postFX().popFilter( FilterType::FILTER_UNDERWATER );
1380 }
1381 }
1382
1383 void Scene::onSetActive()
1384 {
1385 PROFILE_SCOPE_AUTO( Profiler::Category::Scene );
1386
1387 _aiManager->pauseUpdate( false );
1388
1389 input()->onSetActive();
1390 _context.sfx().stopMusic();
1391 _context.sfx().dumpPlaylists();
1392
1393 for ( U32 i = 0u; i < to_base( MusicType::COUNT ); ++i )
1394 {
1395 const SceneState::MusicPlaylist& playlist = state()->music( static_cast<MusicType>(i) );
1396 if ( !playlist.empty() )
1397 {
1398 for ( const auto& song : playlist )
1399 {
1400 _context.sfx().addMusic( i, song.second );
1401 }
1402 }
1403 }
1404 if ( !_context.sfx().playMusic( 0 ) )
1405 {
1406 //DIVIDE_UNEXPECTED_CALL();
1407 NOP();
1408 }
1409
1410 assert( playerCount() == 0 );
1411 addPlayerInternal( false );
1412 }
1413
1414 void Scene::onRemoveActive()
1415 {
1416 PROFILE_SCOPE_AUTO( Profiler::Category::GameLogic );
1417
1418 _aiManager->pauseUpdate( true );
1419
1420 for ( Player_ptr& player : _scenePlayers )
1421 {
1422 if ( player != nullptr )
1423 {
1424 Attorney::ProjectManagerScene::removePlayer( _parent.parent(), this, player->getBoundNode(), false );
1425 player = nullptr;
1426 }
1427 }
1428
1429 input()->onRemoveActive();
1430 }
1431
1432 void Scene::addPlayerInternal( const bool queue )
1433 {
1434 PROFILE_SCOPE_AUTO( Profiler::Category::GameLogic );
1435
1436 // Limit max player count
1437 if ( playerCount() == Config::MAX_LOCAL_PLAYER_COUNT )
1438 {
1439 return;
1440 }
1441
1442 const string playerName = GetPlayerSGNName( static_cast<PlayerIndex>(playerCount()) );
1443
1444 SceneGraphNode* playerSGN( _sceneGraph->findNode( playerName.c_str() ) );
1445 if ( !playerSGN )
1446 {
1447 ResourceDescriptor<TransformNode> playerDescriptor{ playerName };
1448 playerDescriptor.ID(playerCount());
1449
1450 SceneGraphNode* root = _sceneGraph->getRoot();
1451
1452 SceneGraphNodeDescriptor playerNodeDescriptor;
1453 playerNodeDescriptor._serialize = false;
1454 playerNodeDescriptor._nodeHandle = FromHandle( CreateResource( playerDescriptor ) );
1455 playerNodeDescriptor._name = playerName.c_str();
1456 playerNodeDescriptor._usageContext = NodeUsageContext::NODE_DYNAMIC;
1457 playerNodeDescriptor._componentMask = to_base( ComponentType::UNIT ) |
1458 to_base( ComponentType::TRANSFORM ) |
1459 to_base( ComponentType::BOUNDS ) |
1460 to_base( ComponentType::NETWORKING );
1461
1462 playerSGN = root->addChildNode( playerNodeDescriptor );
1463 }
1464
1465 Attorney::ProjectManagerScene::addPlayer( _parent.parent(), this, playerSGN, queue );
1466 DIVIDE_ASSERT( playerSGN->get<UnitComponent>()->getUnit() != nullptr );
1467 }
1468
1469 void Scene::removePlayerInternal( const PlayerIndex idx )
1470 {
1471 PROFILE_SCOPE_AUTO( Profiler::Category::GameLogic );
1472
1473 assert( idx < Config::MAX_LOCAL_PLAYER_COUNT);
1474 Player_ptr& player = _scenePlayers[getSceneIndexForPlayer( idx )];
1475 assert( player != nullptr );
1476
1477 Attorney::ProjectManagerScene::removePlayer( _parent.parent(), this, player->getBoundNode(), true );
1478 }
1479
1480 void Scene::onPlayerAdd( const Player_ptr& player )
1481 {
1482 DIVIDE_ASSERT( player != nullptr );
1483 state()->onPlayerAdd( player->index() );
1484 input()->onPlayerAdd( player->index() );
1485 ++_playerCount;
1486 }
1487
1488 void Scene::onPlayerRemove( const Player_ptr& player )
1489 {
1490 PROFILE_SCOPE_AUTO( Profiler::Category::GameLogic );
1491
1492 const PlayerIndex idx = player->index();
1493
1494 input()->onPlayerRemove( idx );
1495 state()->onPlayerRemove( idx );
1496 _cameraUpdateListeners[idx] = 0u;
1497 if ( _flashLight[idx] != nullptr )
1498 {
1499 _sceneGraph->getRoot()->removeChildNode( _flashLight[idx] );
1500 _flashLight[idx] = nullptr;
1501 }
1502 _sceneGraph->getRoot()->removeChildNode( player->getBoundNode() );
1503
1504 assert( idx < Config::MAX_LOCAL_PLAYER_COUNT);
1505 _scenePlayers[getSceneIndexForPlayer( idx )] = nullptr;
1506 --_playerCount;
1507 }
1508
1509 U8 Scene::getSceneIndexForPlayer( const PlayerIndex idx ) const
1510 {
1511 for ( U8 i = 0; i < Config::MAX_LOCAL_PLAYER_COUNT; ++i )
1512 {
1513 if ( _scenePlayers[i] != nullptr && _scenePlayers[i]->index() == idx )
1514 {
1515 return i;
1516 }
1517 }
1518
1520 return 0;
1521 }
1522
1523 Player* Scene::getPlayerForIndex( const PlayerIndex idx ) const
1524 {
1525 return _scenePlayers[getSceneIndexForPlayer( idx )].get();
1526 }
1527
1528 U8 Scene::getPlayerIndexForDevice( const U8 deviceIndex ) const
1529 {
1530 return input()->getPlayerIndexForDevice( deviceIndex );
1531 }
1532
1533 bool Scene::mouseMoved( const Input::MouseMoveEvent& arg )
1534 {
1535 if ( !arg._wheelEvent )
1536 {
1537 const PlayerIndex idx = getPlayerIndexForDevice( arg._deviceIndex );
1538 DragSelectData& data = _dragSelectData[idx];
1539 if ( data._isDragging )
1540 {
1541 data._endDragPos = vec2<U16>(arg.state().X.abs, arg.state().Y.abs);
1542 updateSelectionData( idx, data );
1543 }
1544 else
1545 {
1546 const bool sceneFocused = Config::Build::ENABLE_EDITOR ? !_context.editor().hasFocus() : true;
1547 const bool sceneHovered = Config::Build::ENABLE_EDITOR ? !_context.editor().isHovered() : true;
1548
1549 if ( sceneFocused && sceneHovered && !state()->playerState( idx ).cameraLockedToMouse() )
1550 {
1551 findHoverTarget( idx, vec2<U16>( arg.state().X.abs, arg.state().Y.abs ) );
1552 }
1553 else if ( !sceneHovered )
1554 {
1555 clearHoverTarget( idx );
1556 }
1557 }
1558 }
1559 return false;
1560 }
1561
1562 bool Scene::updateCameraControls( const PlayerIndex idx ) const
1563 {
1564 PROFILE_SCOPE_AUTO( Profiler::Category::GameLogic );
1565
1566 Camera* cam = playerCamera( idx );
1567 if ( cam->mode() == Camera::Mode::STATIC ||
1568 cam->mode() == Camera::Mode::SCRIPTED )
1569 {
1570 return false;
1571 }
1572
1573 SceneStatePerPlayer& playerState = state()->playerState( idx );
1574 const bool updated = cam->moveFromPlayerState(playerState);
1575 playerState.cameraUpdated( updated );
1576
1577 if ( updated )
1578 {
1579 playerState.cameraUnderwater( checkCameraUnderwater( *cam ) );
1580 }
1581
1582 return updated;
1583 }
1584
1585 void Scene::updateSceneState( const U64 deltaTimeUS )
1586 {
1587 PROFILE_SCOPE_AUTO( Profiler::Category::GameLogic );
1588
1589 sceneRuntimeUS( sceneRuntimeUS() + deltaTimeUS );
1590
1591 updateSceneStateInternal( deltaTimeUS );
1592 _state->waterBodies().resize(0);
1593 _sceneGraph->sceneUpdate( deltaTimeUS, *_state );
1594 _aiManager->update( deltaTimeUS );
1595 }
1596
1597 void Scene::updateSceneStateInternal( [[maybe_unused]] const U64 deltaTimeUS )
1598 {
1599 }
1600
1601 void Scene::onChangeFocus( const bool hasFocus )
1602 {
1603 if ( !hasFocus )
1604 {
1605 //Add a focus flag and ignore redundant calls
1606
1607 for ( const Player_ptr& player : _scenePlayers )
1608 {
1609 if (player != nullptr)
1610 {
1611 state()->playerState( player->index() ).resetMoveDirections();
1612 endDragSelection( player->index(), false );
1613 }
1614 }
1615 _parent.parent().wantsMouse( false );
1616 //_context.kernel().timingData().freezeGameTime(true);
1617 }
1618 else
1619 {
1620 NOP();
1621 }
1622 }
1623
1624 void Scene::registerTask( Task& taskItem, const bool start, const TaskPriority priority )
1625 {
1626 {
1627 LockGuard<SharedMutex> w_lock( _tasksMutex );
1628 _tasks.push_back( &taskItem );
1629 }
1630 if ( start )
1631 {
1632 Start( taskItem, _context.taskPool( TaskPoolType::HIGH_PRIORITY ), priority );
1633 }
1634 }
1635
1636 void Scene::clearTasks()
1637 {
1638 Console::printfn( LOCALE_STR( "STOP_SCENE_TASKS" ) );
1639 // Performance shouldn't be an issue here
1640 LockGuard<SharedMutex> w_lock( _tasksMutex );
1641 for ( const Task* task : _tasks )
1642 {
1643 Wait( *task, _context.taskPool( TaskPoolType::HIGH_PRIORITY ) );
1644 }
1645
1646 _tasks.clear();
1647 }
1648
1649 void Scene::removeTask( const Task& task )
1650 {
1651 LockGuard<SharedMutex> w_lock( _tasksMutex );
1652 for ( vector<Task*>::iterator it = begin( _tasks ); it != end( _tasks ); ++it )
1653 {
1654 if ( (*it)->_id == task._id )
1655 {
1656 Wait( **it, _context.taskPool( TaskPoolType::HIGH_PRIORITY ) );
1657 _tasks.erase( it );
1658 return;
1659 }
1660 }
1661 }
1662
1663 void Scene::processInput( [[maybe_unused]] PlayerIndex idx, [[maybe_unused]] const U64 gameDeltaTimeUS, [[maybe_unused]] const U64 appDeltaTimeUS )
1664 {
1665 }
1666
1667 void Scene::addGuiTimer( const TimerClass intervalClass, const U64 intervalUS, DELEGATE<void, U64/*elapsed time*/> cbk )
1668 {
1669 if ( !cbk )
1670 {
1672 }
1673
1674 _guiTimers.emplace_back( TimerStruct
1675 {
1676 ._callbackIntervalUS = intervalUS,
1677 ._timerClass = intervalClass,
1678 ._cbk = cbk
1679 });
1680 }
1681
1682 void Scene::addTaskTimer( const TimerClass intervalClass, const U64 intervalUS, DELEGATE<void, U64/*elapsed time*/> cbk )
1683 {
1684 if ( !cbk )
1685 {
1687 }
1688
1689 _taskTimers.emplace_back( TimerStruct
1690 {
1691 ._callbackIntervalUS = intervalUS,
1692 ._timerClass = intervalClass,
1693 ._cbk = cbk
1694 });
1695 }
1696
1697 void Scene::processInternalTimers( const U64 appDeltaUS, const U64 gameDeltaUS, vector<TimerStruct>& timers )
1698 {
1699 PROFILE_SCOPE_AUTO( Profiler::Category::Scene );
1700
1701 eastl::for_each( begin( timers ),
1702 end( timers ),
1703 [appDeltaUS, gameDeltaUS]( TimerStruct& timer)
1704 {
1705 const U64 delta = timer._timerClass == TimerClass::APP_TIME ? appDeltaUS : gameDeltaUS;
1706
1707 timer._internalTimer += delta;
1708 timer._internalTimerTotal += delta;
1709
1710 if ( timer._internalTimer >= timer._callbackIntervalUS )
1711 {
1712 timer._cbk(timer._internalTimerTotal );
1713 timer._internalTimer = 0u;
1714 }
1715
1716 });
1717 }
1718
1719 void Scene::processGUI( const U64 gameDeltaTimeUS, const U64 appDeltaTimeUS )
1720 {
1721 processInternalTimers( appDeltaTimeUS, gameDeltaTimeUS, _guiTimers);
1722 }
1723
1724 void Scene::processTasks( const U64 gameDeltaTimeUS, const U64 appDeltaTimeUS )
1725 {
1726 PROFILE_SCOPE_AUTO( Profiler::Category::Scene );
1727
1728 static bool increaseWeatherScale = true;
1729
1730 processInternalTimers( appDeltaTimeUS, gameDeltaTimeUS, _taskTimers );
1731
1732 if ( _dayNightData._skyInstance != nullptr )
1733 {
1734 static struct tm timeOfDay = {};
1735
1736 bool updateSun = false, updateProbes = false;
1737
1738 if ( _dayNightData._resetTime )
1739 {
1740 updateProbes = true;
1741 updateSun = true;
1742
1743 _dayNightData._resetTime = false;
1744 _dayNightData._timeAccumulatorSec = 0.f;
1745 _dayNightData._timeAccumulatorHour = 0.f;
1746
1747 const time_t t = time( nullptr );
1748 timeOfDay = *localtime( &t );
1749 timeOfDay.tm_hour = _dayNightData._time._hour;
1750 timeOfDay.tm_min = _dayNightData._time._minutes;
1751 }
1752
1753 if (!updateSun && !updateProbes)
1754 {
1755 const F32 speedFactor = dayNightCycleEnabled() ? _dayNightData._speedFactor : 0.f;
1756 const F32 deltaSeconds = Time::MillisecondsToSeconds<F32>( gameDeltaTimeUS );
1757 const F32 addTime = speedFactor * deltaSeconds;
1758 if ( addTime > 0.f )
1759 {
1760 _dayNightData._timeAccumulatorSec += addTime;
1761 _dayNightData._timeAccumulatorHour += addTime;
1762 Atmosphere atmosphere = _dayNightData._skyInstance->atmosphere();
1763 if ( atmosphere._cloudCoverage > 0.9f && increaseWeatherScale )
1764 {
1765 increaseWeatherScale = false;
1766 }
1767 else if ( atmosphere._cloudCoverage < 0.1f && !increaseWeatherScale )
1768 {
1769 increaseWeatherScale = true;
1770 }
1771 atmosphere._cloudCoverage += (deltaSeconds * (increaseWeatherScale ? 0.001f : -0.001f));
1772
1773 _dayNightData._skyInstance->setAtmosphere( atmosphere );
1774 }
1775 }
1776
1777 if ( std::abs( _dayNightData._timeAccumulatorSec ) > Time::Seconds( 1.f ) )
1778 {
1779 _dayNightData._timeAccumulatorSec = 0.f;
1780 updateSun = true;
1781
1782 timeOfDay.tm_sec += to_I32( _dayNightData._timeAccumulatorSec );
1783
1784 if ( std::abs(_dayNightData._timeAccumulatorHour) > Time::Hours( 1.f ) )
1785 {
1786 _dayNightData._timeAccumulatorHour = 0.f;
1787 updateProbes = true;
1788 }
1789 }
1790
1791 if (updateSun)
1792 {
1793 //Update day/night data
1794 _dayNightData._time._hour = to_U8( timeOfDay.tm_hour );
1795 _dayNightData._time._minutes = to_U8( timeOfDay.tm_min );
1796
1797 //Update sky instance with update day/night data
1798 const time_t now = mktime( &timeOfDay ); // normalize it
1799 const SunInfo details = _dayNightData._skyInstance->setDateTimeAndLocation( localtime( &now ), _dayNightData._location );
1800
1801 const Angle::DEGREES<F32> sunAltitude = Angle::RadiansToDegrees( details.altitude );
1802 const Angle::DEGREES<F32> sunAzimuth = Angle::RadiansToDegrees( details.azimuth );
1803
1804 constexpr Angle::DEGREES<F32> twilightDegrees{ -18.f };
1805 constexpr Angle::DEGREES<F32> sunriseAzimuth{ 70.f };
1806 constexpr Angle::DEGREES<F32> sunsetAzimuth{ 280.f };
1807
1808 const bool isNight = sunAltitude < twilightDegrees;
1809 const bool isTwilight = IS_IN_RANGE_INCLUSIVE( sunAltitude, twilightDegrees, 0.f );
1810 const bool isDawn = isTwilight && sunAzimuth < sunriseAzimuth;
1811 const bool isDusk = isTwilight && sunAzimuth > sunsetAzimuth;
1812
1813 //Update sky direction (inverse direction for night)
1814 const vec3<F32> sunPosition = _dayNightData._skyInstance->getSunPosition( _dayNightData._sunLight->range() );
1815 vec3<F32> sunDirection = Normalized( VECTOR3_ZERO - sunPosition );
1816
1817
1818 //Update sun/moon colour
1819 const FColour3 sunsetOrange{ 99.2f / 100.f, 36.9f / 100.f, 32.5f / 100.f };
1820 const FColour3 sunColour = DefaultColours::WHITE.rgb;
1821 const FColour3 moonColour = Normalized( _dayNightData._skyInstance->moonColour().rgb );
1822
1823 // Dawn
1824 if ( isDawn )
1825 {
1826 _dayNightData._sunLight->setDiffuseColour( Lerp( sunsetOrange, sunColour, std::abs(sunAltitude / twilightDegrees ) ) );
1827 //sunDirection *= -1.f;
1828 }
1829 // Dusk
1830 else if ( isDusk )
1831 {
1832 _dayNightData._sunLight->setDiffuseColour( Lerp( sunsetOrange, moonColour, std::abs( sunAltitude / twilightDegrees ) ) );
1833 //sunDirection *= -1.f;
1834 }
1835 // Night
1836 else if ( isNight )
1837 {
1838 _dayNightData._sunLight->setDiffuseColour( moonColour );
1839 //sunDirection *= -1.f;
1840 }
1841 // Day
1842 else
1843 {
1844 _dayNightData._sunLight->setDiffuseColour( sunColour );
1845 }
1846 _dayNightData._sunLight->sgn()->get<TransformComponent>()->setPosition( sunPosition );
1847 _dayNightData._sunLight->sgn()->get<TransformComponent>()->setDirection( sunDirection );
1848 }
1849
1850 if ( updateProbes && _envProbePool != nullptr )
1851 {
1852 SceneEnvironmentProbePool::OnTimeOfDayChange( *_envProbePool );
1853 }
1854 }
1855 }
1856
1857 void Scene::drawCustomUI( const Rect<I32>& targetViewport, GFX::CommandBuffer& bufferInOut, GFX::MemoryBarrierCommand& memCmdInOut )
1858 {
1859 PROFILE_SCOPE_AUTO( Profiler::Category::Scene );
1860
1861 if ( _linesPrimitive->hasBatch() )
1862 {
1863 GFX::EnqueueCommand<GFX::SetViewportCommand>( bufferInOut )->_viewport = targetViewport;
1864 _linesPrimitive->getCommandBuffer( bufferInOut, memCmdInOut );
1865 }
1866 }
1867
1868 void Scene::debugDraw( GFX::CommandBuffer& bufferInOut, GFX::MemoryBarrierCommand& memCmdInOut )
1869 {
1870 PROFILE_SCOPE_AUTO( Profiler::Category::Scene );
1871
1872 // Show NavMeshes
1873 _aiManager->debugDraw( bufferInOut, memCmdInOut, false );
1874 _lightPool->drawLightImpostors( bufferInOut );
1875 }
1876
1877 bool Scene::checkCameraUnderwater( const PlayerIndex idx ) const
1878 {
1879 PROFILE_SCOPE_AUTO( Profiler::Category::Scene );
1880
1881 const Camera* crtCamera = Attorney::ProjectManagerCameraAccessor::playerCamera( _parent.parent(), idx );
1882 return checkCameraUnderwater( *crtCamera );
1883 }
1884
1885 bool Scene::checkCameraUnderwater( const Camera& camera ) const noexcept
1886 {
1887 const vec3<F32>& eyePos = camera.snapshot()._eye;
1888 {
1889 const auto& waterBodies = state()->waterBodies();
1890 for ( const WaterBodyData& water : waterBodies )
1891 {
1892 const vec3<F32>& extents = water._extents;
1893 const vec3<F32>& position = water._positionW;
1894 const F32 halfWidth = (extents.x + position.x) * 0.5f;
1895 const F32 halfLength = (extents.z + position.z) * 0.5f;
1896 if ( eyePos.x >= -halfWidth && eyePos.x <= halfWidth &&
1897 eyePos.z >= -halfLength && eyePos.z <= halfLength )
1898 {
1899 const float depth = -extents.y + position.y;
1900 return eyePos.y < position.y&& eyePos.y > depth;
1901 }
1902 }
1903 }
1904
1905 return false;
1906 }
1907
1908 void Scene::findHoverTarget( PlayerIndex idx, const vec2<I32> aimPos )
1909 {
1910 PROFILE_SCOPE_AUTO( Profiler::Category::Scene );
1911
1912 constexpr SceneNodeType s_ignoredNodes[6]
1913 {
1914 SceneNodeType::TYPE_TRANSFORM,
1915 SceneNodeType::TYPE_WATER,
1916 SceneNodeType::TYPE_SKY,
1917 SceneNodeType::TYPE_PARTICLE_EMITTER,
1918 SceneNodeType::TYPE_INFINITEPLANE,
1919 SceneNodeType::TYPE_VEGETATION
1920 };
1921
1922 const Camera* crtCamera = playerCamera( idx );
1923 const vec2<U16> renderingResolution = _context.gfx().renderingResolution();
1924 const vec3<F32> direction = crtCamera->unProject( to_F32( aimPos.x ),
1925 renderingResolution.height - to_F32( aimPos.y ),
1926 {
1927 0,
1928 0,
1929 renderingResolution.width,
1930 renderingResolution.height
1931 } );
1932
1933 // see if we select another one
1934 _sceneSelectionCandidates.resize( 0 );
1935
1936 SGNIntersectionParams intersectionParams = {};
1937 intersectionParams._ray = { crtCamera->snapshot()._eye, direction };
1938 intersectionParams._range = crtCamera->snapshot()._zPlanes;
1939 intersectionParams._includeTransformNodes = true;
1940 intersectionParams._ignoredTypes = &s_ignoredNodes[0];
1941 intersectionParams._ignoredTypesCount = std::size(s_ignoredNodes);
1942
1943 // Cast the picking ray and find items between the nearPlane and far Plane
1944 sceneGraph()->intersect( intersectionParams, _sceneSelectionCandidates );
1945
1946 if ( !_sceneSelectionCandidates.empty() )
1947 {
1948 // If we don't force selections, remove all of the nodes that lack a SelectionComponent
1949 eastl::sort( begin( _sceneSelectionCandidates ),
1950 end( _sceneSelectionCandidates ),
1951 []( const SGNRayResult& A, const SGNRayResult& B ) noexcept -> bool
1952 {
1953 return A.dist < B.dist;
1954 } );
1955
1956 SceneGraphNode* target = nullptr;
1957 for ( const SGNRayResult& result : _sceneSelectionCandidates )
1958 {
1959 if ( result.inside || result.dist < 0.0f )
1960 {
1961 continue;
1962 }
1963
1964 SceneGraphNode* crtNode = _sceneGraph->findNode( result.sgnGUID );
1965 if ( DebugBreak( crtNode == nullptr ) )
1966 {
1967 continue;
1968 }
1969
1970 // In the editor, we can select ... anything ...
1971 if ( Config::Build::ENABLE_EDITOR && _context.editor().inEditMode() )
1972 {
1973 target = crtNode;
1974 break;
1975 }
1976
1977 testNode:
1978 // Outside of the editor, we only select nodes that have a selection component
1979 if ( crtNode->get<SelectionComponent>() != nullptr && crtNode->get<SelectionComponent>()->enabled() )
1980 {
1981 target = crtNode;
1982 break;
1983 }
1984
1985 if ( crtNode->getNode().type() == SceneNodeType::TYPE_SUBMESH )
1986 {
1987 // In normal gameplay, we need to top node for the selection (i.e. a mesh for submeshe intersections)
1988 // Because we use either the physics system or a recursive scenegraph intersection loop, we may end up with
1989 // child-node data as a result
1990 crtNode = crtNode->parent();
1991 if ( crtNode != nullptr )
1992 {
1993 goto testNode;
1994 }
1995 }
1996 }
1997
1998 clearHoverTarget( idx );
1999
2000 if ( target != nullptr )
2001 {
2002 _currentHoverTarget[idx] = target->getGUID();
2003 if ( !target->hasFlag( SceneGraphNode::Flags::SELECTED ) )
2004 {
2005 target->setFlag( SceneGraphNode::Flags::HOVERED, true );
2006 }
2007 }
2008 }
2009 else
2010 {
2011 clearHoverTarget( idx );
2012 }
2013 }
2014
2015 void Scene::clearHoverTarget( const PlayerIndex idx )
2016 {
2017 PROFILE_SCOPE_AUTO( Profiler::Category::Scene );
2018
2019 if ( _currentHoverTarget[idx] != -1 )
2020 {
2021 SceneGraphNode* oldTarget = _sceneGraph->findNode( _currentHoverTarget[idx] );
2022 if ( oldTarget != nullptr )
2023 {
2024 oldTarget->clearFlag( SceneGraphNode::Flags::HOVERED );
2025 }
2026 }
2027
2028 _currentHoverTarget[idx] = -1;
2029 }
2030
2031 void Scene::onNodeDestroy( SceneGraphNode* node )
2032 {
2033 const I64 guid = node->getGUID();
2034 for ( I64& target : _currentHoverTarget )
2035 {
2036 if ( target == guid )
2037 {
2038 target = -1;
2039 }
2040 }
2041
2042 for ( Selections& playerSelections : _currentSelection )
2043 {
2044 for ( I16 i = to_I16(playerSelections._selectionCount); i > 0; --i )
2045 {
2046 const size_t idx = to_size(i - 1);
2047
2048 const I64 crtGUID = playerSelections._selections[idx];
2049 if ( crtGUID == guid )
2050 {
2051 playerSelections._selections[idx] = -1;
2052 std::swap( playerSelections._selections[idx], playerSelections._selections[playerSelections._selectionCount--] );
2053 }
2054 }
2055 }
2056
2057 _parent.parent().onNodeDestroy( this, node );
2058 }
2059
2060 bool Scene::resetSelection( const PlayerIndex idx, const bool resetIfLocked )
2061 {
2062 Selections& tempSelections = _tempSelection[idx];
2063 Selections& playerSelections = _currentSelection[idx];
2064 const U8 selectionCount = playerSelections._selectionCount;
2065
2066 tempSelections = {};
2067
2068 for ( U8 i = 0; i < selectionCount; ++i )
2069 {
2070 SceneGraphNode* node = sceneGraph()->findNode( playerSelections._selections[i] );
2071 if ( node != nullptr && (!node->hasFlag( SceneGraphNode::Flags::SELECTION_LOCKED ) || resetIfLocked) )
2072 {
2073 node->clearFlag( SceneGraphNode::Flags::HOVERED, true );
2074 node->clearFlag( SceneGraphNode::Flags::SELECTED, true );
2075 }
2076 else if ( node != nullptr )
2077 {
2078 tempSelections._selections[tempSelections._selectionCount++] = node->getGUID();
2079 }
2080 }
2081
2082 playerSelections = tempSelections;
2083 return tempSelections._selectionCount == 0u;
2084 }
2085
2086 void Scene::setSelected( const PlayerIndex idx, const vector<SceneGraphNode*>& SGNs, const bool recursive )
2087 {
2088 Selections& playerSelections = _currentSelection[idx];
2089
2090 for ( SceneGraphNode* sgn : SGNs )
2091 {
2092 if ( !sgn->hasFlag( SceneGraphNode::Flags::SELECTED ) )
2093 {
2094 playerSelections._selections[playerSelections._selectionCount++] = sgn->getGUID();
2095 sgn->setFlag( SceneGraphNode::Flags::SELECTED, recursive );
2096 }
2097 }
2098 }
2099
2100 const Selections& Scene::getCurrentSelection( const PlayerIndex index ) const
2101 {
2102 return _currentSelection[index];
2103 }
2104
2105 bool Scene::findSelection( const PlayerIndex idx, const bool clearOld )
2106 {
2107 // Clear old selection
2108 if ( clearOld )
2109 {
2110 if ( !_parent.parent().resetSelection( idx, false ) )
2111 {
2112 return false;
2113 }
2114 }
2115
2116 const I64 hoverGUID = _currentHoverTarget[idx];
2117 // No hover target
2118 if ( hoverGUID == -1 )
2119 {
2120 return false;
2121 }
2122
2123 Selections& playerSelections = _currentSelection[idx];
2124 for ( U8 i = 0u; i < playerSelections._selectionCount; ++i )
2125 {
2126 if ( playerSelections._selections[i] == hoverGUID )
2127 {
2128 //Already selected
2129 return true;
2130 }
2131 }
2132
2133 SceneGraphNode* selectedNode = _sceneGraph->findNode( hoverGUID );
2134 if ( selectedNode != nullptr )
2135 {
2136 _parent.parent().setSelected( idx, { selectedNode }, false );
2137 return true;
2138 }
2139 if ( !_parent.parent().resetSelection( idx, false ) )
2140 {
2141 NOP();
2142 }
2143 return false;
2144 }
2145
2146 void Scene::beginDragSelection( const PlayerIndex idx, const vec2<I32> mousePos )
2147 {
2148 if constexpr( Config::Build::ENABLE_EDITOR )
2149 {
2150 if ( _context.editor().running() && _context.editor().isHovered() )
2151 {
2152 return;
2153 }
2154 }
2155
2156 DragSelectData& data = _dragSelectData[idx];
2157 data._startDragPos = mousePos;
2158 data._endDragPos = data._startDragPos;
2159 data._isDragging = true;
2160 }
2161
2162 void Scene::updateSelectionData( PlayerIndex idx, DragSelectData& data )
2163 {
2164 static std::array<Line, 4> s_lines = {
2165 Line{VECTOR3_ZERO, VECTOR3_UNIT, DefaultColours::GREEN_U8, DefaultColours::GREEN_U8, 2.0f, 1.0f},
2166 Line{VECTOR3_ZERO, VECTOR3_UNIT, DefaultColours::GREEN_U8, DefaultColours::GREEN_U8, 2.0f, 1.0f},
2167 Line{VECTOR3_ZERO, VECTOR3_UNIT, DefaultColours::GREEN_U8, DefaultColours::GREEN_U8, 2.0f, 1.0f},
2168 Line{VECTOR3_ZERO, VECTOR3_UNIT, DefaultColours::GREEN_U8, DefaultColours::GREEN_U8, 2.0f, 1.0f}
2169 };
2170
2171 if constexpr( Config::Build::ENABLE_EDITOR )
2172 {
2173 const Editor& editor = _context.editor();
2174 if ( editor.hasFocus() )
2175 {
2176 endDragSelection( idx, false );
2177 return;
2178 }
2179 }
2180
2181 _parent.parent().wantsMouse( true );
2182
2183 const vec2<U16> resolution = _context.gfx().renderingResolution();
2184
2185 const vec2<I32> startPos = { data._startDragPos.x, resolution.height - data._startDragPos.y };
2186
2187 const vec2<I32> endPos = { data._endDragPos.x, resolution.height - data._endDragPos.y };
2188
2189 const I32 startX = std::min( startPos.x, endPos.x );
2190 const I32 startY = std::min( startPos.y, endPos.y );
2191
2192 const Rect<I32> selectionRect {
2193 startX,
2194 startY,
2195 std::abs( endPos.x - startPos.x ),
2196 std::abs( endPos.y - startPos.y )
2197 };
2198
2199 //X0, Y0 -> X1, Y0
2200 s_lines[0]._positionStart = { selectionRect.x, selectionRect.y, 0 };
2201 s_lines[0]._positionEnd = { selectionRect.x + selectionRect.z, selectionRect.y, 0 };
2202
2203 //X1 , Y0 -> X1, Y1
2204 s_lines[1]._positionStart = { selectionRect.x + selectionRect.z, selectionRect.y, 0 };
2205 s_lines[1]._positionEnd = { selectionRect.x + selectionRect.z, selectionRect.y + selectionRect.w, 0 };
2206
2207 //X1, Y1 -> X0, Y1
2208 s_lines[2]._positionStart = s_lines[1]._positionEnd;
2209 s_lines[2]._positionEnd = { selectionRect.x, selectionRect.y + selectionRect.w, 0 };
2210
2211 //X0, Y1 -> X0, Y0
2212 s_lines[3]._positionStart = s_lines[2]._positionEnd;
2213 s_lines[3]._positionEnd = s_lines[0]._positionStart;
2214
2215 _linesPrimitive->fromLines( s_lines.data(), s_lines.size() );
2216
2217 if ( GFXDevice::FrameCount() % 2 == 0 )
2218 {
2219 clearHoverTarget( idx );
2220 if ( _parent.parent().resetSelection( idx, false ) )
2221 {
2222 const Camera* crtCamera = playerCamera( idx );
2223
2224 NO_DESTROY thread_local vector<SceneGraphNode*> nodes;
2225 Attorney::ProjectManagerScene::getNodesInScreenRect( _parent.parent(), selectionRect, *crtCamera, nodes );
2226
2227 _parent.parent().setSelected( idx, nodes, false );
2228 }
2229 }
2230 }
2231
2232 void Scene::endDragSelection( const PlayerIndex idx, const bool clearSelection )
2233 {
2234 constexpr F32 DRAG_SELECTION_THRESHOLD_PX_SQ = 9.f;
2235
2236 DragSelectData& data = _dragSelectData[idx];
2237
2238 _linesPrimitive->clearBatch();
2239 _parent.parent().wantsMouse( false );
2240 data._isDragging = false;
2241 if ( data._startDragPos.distanceSquared( data._endDragPos ) < DRAG_SELECTION_THRESHOLD_PX_SQ )
2242 {
2243 if ( !findSelection( idx, clearSelection ) )
2244 {
2245 NOP();
2246 }
2247 }
2248 }
2249
2250 void Scene::initDayNightCycle( Sky& skyInstance, DirectionalLightComponent& sunLight ) noexcept
2251 {
2252 _dayNightData._skyInstance = &skyInstance;
2253 _dayNightData._sunLight = &sunLight;
2254 if ( !_dayNightData._resetTime )
2255 {
2256 // Usually loaded from XML/save data
2257 _dayNightData._time = skyInstance.GetTimeOfDay();
2258 _dayNightData._resetTime = true;
2259 }
2260 _dayNightData._timeAccumulatorSec = Time::Seconds( 1.1f );
2261 _dayNightData._timeAccumulatorHour = 0.f;
2262 sunLight.lockDirection( true );
2263
2264 const vec3<F32> sunPosition = _dayNightData._skyInstance->getSunPosition( sunLight.range() );
2265 sunLight.sgn()->get<TransformComponent>()->setDirection( Normalized( VECTOR3_ZERO - sunPosition ) );
2266 }
2267
2268 void Scene::setDayNightCycleTimeFactor( const F32 factor ) noexcept
2269 {
2270 _dayNightData._speedFactor = factor;
2271 }
2272
2273 F32 Scene::getDayNightCycleTimeFactor() const noexcept
2274 {
2275 return _dayNightData._speedFactor;
2276 }
2277
2278 void Scene::setTimeOfDay( const SimpleTime& time ) noexcept
2279 {
2280 _dayNightData._time = time;
2281 _dayNightData._resetTime = true;
2282 }
2283
2284 const SimpleTime& Scene::getTimeOfDay() const noexcept
2285 {
2286 return _dayNightData._time;
2287 }
2288
2289 void Scene::setGeographicLocation( const SimpleLocation& location ) noexcept
2290 {
2291 _dayNightData._location = location;
2292 _dayNightData._resetTime = true;
2293 }
2294
2295 const SimpleLocation& Scene::getGeographicLocation() const noexcept
2296 {
2297 return _dayNightData._location;
2298 }
2299
2300 [[nodiscard]] vec3<F32> Scene::getSunPosition() const
2301 {
2302 if ( _dayNightData._sunLight != nullptr )
2303 {
2304 return _dayNightData._sunLight->sgn()->get<TransformComponent>()->getWorldPosition();
2305 }
2306 return vec3<F32>(500, 500, 500);
2307 }
2308
2309 [[nodiscard]] vec3<F32> Scene::getSunDirection() const
2310 {
2311 if ( _dayNightData._sunLight != nullptr )
2312 {
2313 return _dayNightData._sunLight->sgn()->get<TransformComponent>()->getLocalDirection();
2314 }
2315
2316 return vec3<F32>(WORLD_Y_NEG_AXIS);
2317 }
2318
2319 SunInfo Scene::getCurrentSunDetails() const noexcept
2320 {
2321 if ( _dayNightData._skyInstance != nullptr )
2322 {
2323 return _dayNightData._skyInstance->getCurrentDetails();
2324 }
2325
2326 return {};
2327 }
2328
2329 Atmosphere Scene::getCurrentAtmosphere() const noexcept
2330 {
2331 if ( _dayNightData._skyInstance != nullptr )
2332 {
2333 return _dayNightData._skyInstance->atmosphere();
2334 }
2335
2336 return {};
2337 }
2338
2339 void Scene::setCurrentAtmosphere( const Atmosphere& atmosphere ) const noexcept
2340 {
2341 if ( _dayNightData._skyInstance != nullptr )
2342 {
2343 return _dayNightData._skyInstance->setAtmosphere( atmosphere );
2344 }
2345 }
2346
2347 bool Scene::save( ByteBuffer& outputBuffer ) const
2348 {
2349 outputBuffer << BYTE_BUFFER_VERSION;
2350 const U8 plCount = playerCount();
2351 outputBuffer << plCount;
2352 for ( const Player_ptr& player : _scenePlayers )
2353 {
2354 if ( player != nullptr )
2355 {
2356 const Camera* cam = player->camera();
2357 outputBuffer << player->index() << cam->snapshot()._eye << cam->snapshot()._orientation;
2358 }
2359 }
2360
2361 return _sceneGraph->saveCache( outputBuffer );
2362 }
2363
2364 bool Scene::load( ByteBuffer& inputBuffer )
2365 {
2366
2367 if ( !inputBuffer.bufferEmpty() )
2368 {
2369 auto tempVer = decltype(BYTE_BUFFER_VERSION){0};
2370 inputBuffer >> tempVer;
2371 if ( tempVer == BYTE_BUFFER_VERSION )
2372 {
2373 const U8 currentPlayerCount = playerCount();
2374
2375 vec3<F32> camPos;
2376 Quaternion<F32> camOrientation;
2377
2378 U8 currentPlayerIndex = 0u;
2379 U8 previousPlayerCount = 0u;
2380 inputBuffer >> previousPlayerCount;
2381 for ( U8 i = 0; i < previousPlayerCount; ++i )
2382 {
2383 inputBuffer >> currentPlayerIndex >> camPos >> camOrientation;
2384 if ( currentPlayerIndex < currentPlayerCount )
2385 {
2386 Camera* cam = _scenePlayers[currentPlayerIndex]->camera();
2387 cam->setEye( camPos );
2388 cam->setRotation( camOrientation );
2389 state()->playerState( currentPlayerIndex ).cameraUnderwater( checkCameraUnderwater( *cam ) );
2390 }
2391 }
2392 }
2393 else
2394 {
2395 return false;
2396 }
2397 }
2398
2399 return _sceneGraph->loadCache( inputBuffer );
2400 }
2401
2402 Camera* Scene::playerCamera( const bool skipOverride ) const
2403 {
2404 return Attorney::ProjectManagerCameraAccessor::playerCamera( _parent.parent(), skipOverride );
2405 }
2406
2407 Camera* Scene::playerCamera( const U8 index, const bool skipOverride ) const
2408 {
2409 return Attorney::ProjectManagerCameraAccessor::playerCamera( _parent.parent(), index, skipOverride );
2410 }
2411
2412 void Attorney::SceneEnvironmentProbeComponent::registerProbe( Scene* scene, EnvironmentProbeComponent* probe )
2413 {
2414 DIVIDE_ASSERT( scene->_envProbePool != nullptr );
2415
2416 scene->_envProbePool->registerProbe( probe );
2417 }
2418
2419 void Attorney::SceneEnvironmentProbeComponent::unregisterProbe( Scene* scene, const EnvironmentProbeComponent* const probe )
2420 {
2421 DIVIDE_ASSERT( scene->_envProbePool != nullptr );
2422
2423 scene->_envProbePool->unregisterProbe( probe );
2424 }
2425
2426} //namespace Divide
#define WAIT_FOR_CONDITION(...)
#define LOCALE_STR(X)
Definition: Localization.h:91
#define DIVIDE_ASSERT(...)
#define NO_DESTROY
#define DIVIDE_UNEXPECTED_CALL()
#define NOP()
#define PROFILE_SCOPE_AUTO(CATEGORY)
Definition: Profiler.h:87
bool bufferEmpty() const noexcept
Returns true if the read position and the write position are identical.
Definition: ByteBuffer.inl:353
const mat4< F32 > & setProjection(vec2< F32 > zPlanes)
Definition: Camera.cpp:516
const CameraSnapshot & snapshot() const noexcept
Returns the internal camera snapshot data (eye, orientation, etc)
Definition: Camera.inl:43
bool updateLookAt()
Return true if the cached camera state wasn't up-to-date.
Definition: Camera.cpp:384
bool moveFromPlayerState(const SceneStatePerPlayer &playerState)
Definition: Camera.cpp:704
vec3< F32 > unProject(F32 winCoordsX, F32 winCoordsY, const Rect< I32 > &viewport) const noexcept
Definition: Camera.cpp:879
void setEye(const F32 x, const F32 y, const F32 z) noexcept
Sets the camera's eye position.
Definition: Camera.inl:100
void saveToXML(boost::property_tree::ptree &pt, std::string prefix="") const
Definition: Camera.cpp:969
void setRotation(const Quaternion< F32 > &q) noexcept
Sets the camera's orientation.
Definition: Camera.inl:111
void move(F32 dx, F32 dy, F32 dz) noexcept
Moves the camera by the specified offsets in each direction.
Definition: Camera.cpp:668
bool grabState() const noexcept
bool hasFocus() const
Definition: Editor.cpp:2122
IMPrimitive * newIMP(std::string_view name)
Create and return a new immediate mode emulation primitive.
Definition: GFXDevice.cpp:3055
bool destroyIMP(IMPrimitive *&primitive)
Definition: GFXDevice.cpp:3061
FORCE_INLINE I64 getGUID() const noexcept
Definition: GUIDWrapper.h:51
void setPipelineDescriptor(const PipelineDescriptor &descriptor)
bool registerInputAction(U16 id, const InputAction &action)
const NavigationContext & navigationContext() const noexcept
PlatformContext & context() noexcept
TaskPool & taskPool(const TaskPoolType type) noexcept
GFXDevice & gfx() noexcept
Configuration & config() noexcept
User controlled Unit.
Definition: Player.h:45
void clearFlag(Flags flag, bool recursive=true)
Clearing a flag might propagate to child nodes (e.g. selection).
void setFlag(Flags flag, bool recursive=true)
General purpose flag management. Certain flags propagate to children (e.g. selection)!
SceneGraphNode * addChildNode(const SceneGraphNodeDescriptor &descriptor)
Add child node increments the node's ref counter if the node was already added to the scene graph.
FORCE_INLINE T * get() const
Returns a pointer to a specific component. Returns null if the SGN does not have the component reques...
T & getNode() noexcept
bool hasFlag(const Flags flag) const noexcept
Returns true only if the current node has the specified flag. Does not check children!
void loadFromXML(const ResourcePath &sceneLocation)
static bool OnStartup(PlatformContext &context)
Definition: Scene.cpp:110
static I64 DEFAULT_SCENE_GUID
Definition: Scene.h:131
IMPrimitive * _linesPrimitive
Definition: Scene.h:335
virtual ~Scene() override
Definition: Scene.cpp:102
static ResourcePath GetSceneRootFolder(const Project &project)
Return the full path to the location of Scenes folder in the project (e.g. ./Projects/Foo/Scenes/ for...
Definition: Scene.cpp:128
std::array< U32, Config::MAX_LOCAL_PLAYER_COUNT > _cameraUpdateListeners
Definition: Scene.h:343
virtual bool frameEnded()
Definition: Scene.cpp:138
vector< Task * > _tasks
Definition: Scene.h:347
bool saveNodeToXML(const SceneGraphNode *node) const
Definition: Scene.cpp:181
static ResourcePath GetSceneFullPath(const Scene &scene)
Return the full path to the scene's location on disk. It's equivalent to GetSceneRootFolder(scene....
Definition: Scene.cpp:123
std::array< SceneGraphNode *, Config::MAX_LOCAL_PLAYER_COUNT > _flashLight
Definition: Scene.h:342
virtual bool frameStarted()
Definition: Scene.cpp:133
void loadAsset(const Task *parentTask, const XML::SceneNode &sceneNode, SceneGraphNode *parent)
Definition: Scene.cpp:417
void addWater(SceneGraphNode *parentNode, const boost::property_tree::ptree &pt, const Str< 64 > &nodeName="")
Definition: Scene.cpp:776
Project & parent() noexcept
Definition: Scene.h:163
virtual bool loadXML()
Definition: Scene.cpp:315
SceneGraphNode * addInfPlane(SceneGraphNode *parentNode, const boost::property_tree::ptree &pt, const Str< 64 > &nodeName="")
Definition: Scene.cpp:799
void addMusic(MusicType type, const std::string_view name, const ResourcePath &srcFile)
Definition: Scene.cpp:167
Camera * playerCamera(const bool skipOverride=false) const
Definition: Scene.cpp:2402
Scene(PlatformContext &context, Project &parent, const SceneEntry &entry)
Definition: Scene.cpp:76
virtual bool saveXML(const DELEGATE< void, std::string_view > &msgCallback, const DELEGATE< void, bool > &finishCallback) const
Can save at any time, I guess?
Definition: Scene.cpp:191
static bool OnShutdown(PlatformContext &context)
Definition: Scene.cpp:117
bool loadNodeFromXML(SceneGraphNode *node) const
Definition: Scene.cpp:186
SharedMutex _tasksMutex
Definition: Scene.h:346
SceneGraphNode * addSky(SceneGraphNode *parentNode, const boost::property_tree::ptree &pt, const Str< 64 > &nodeName="")
Definition: Scene.cpp:747
std::array< I64, Config::MAX_LOCAL_PLAYER_COUNT > _currentHoverTarget
Definition: Scene.h:340
bool idle()
Scene is rendering, so add intensive tasks here to save CPU cycles.
Definition: Scene.cpp:143
void addTerrain(SceneGraphNode *parentNode, const boost::property_tree::ptree &pt, const Str< 64 > &nodeName="")
Definition: Scene.cpp:667
std::atomic_uint _loadingTasks
Definition: Scene.h:333
virtual void setMaterialTpl(Handle< Material > material)
Definition: SceneNode.cpp:78
virtual void loadFromXML(const boost::property_tree::ptree &pt)
Definition: SceneNode.cpp:164
hashMap< U64, Handle< AudioDescriptor > > MusicPlaylist
Background music map : trackName - track.
Definition: SceneState.h:212
std::shared_ptr< T > getUnit() const noexcept
Definition: UnitComponent.h:49
T distanceSquared(const vec2 &v) const noexcept
compute the vector's squared distance to another specified vector
void get(T *v) const noexcept
void set(const T *v) noexcept
set the 4 components of the vector manually using a source pointer to a (large enough) array
Definition: MathVectors.h:1241
constexpr bool IS_SHIPPING_BUILD
Definition: config.h:60
void OnStartup(const PlatformContext &context)
constexpr Optick::Category::Type Scene
Definition: Profiler.h:66
const char * ComponentTypeToString(const ComponentType compType) noexcept
Str StringFormat(const char *fmt, Args &&...args)
bool CompareIgnoreCase(const char *a, const char *b) noexcept
string MakeXMLSafe(std::string_view subject)
void writeXML(const ResourcePath &path, const boost::property_tree::ptree &tree)
void readXML(const ResourcePath &path, boost::property_tree::ptree &tree)
constexpr const char *const g_defaultPlayerName
Definition: Scene.cpp:71
Handle console commands that start with a forward slash.
Definition: AIProcessor.cpp:7
vec2< T > Normalized(vec2< T > vector) noexcept
Definition: MathVectors.inl:98
DELEGATE_STD< Ret, Args... > DELEGATE
void updateSceneStateInternal(U64 deltaTimeUS) override
std::lock_guard< mutex > LockGuard
Definition: SharedMutex.h:55
constexpr U32 to_U32(const T value)
T Lerp(T v1, T v2, U t) noexcept
Definition: MathHelper.inl:240
void insert(eastl::vector< T, A1 > &target, const eastl::vector< T, A2 > &source)
Definition: Vector.h:97
FORCE_INLINE constexpr bool IsPrimitive(const SceneNodeType type) noexcept
Definition: SceneNodeFwd.h:85
bool IS_IN_RANGE_INCLUSIVE(const T x, const U min, const U max) noexcept
void Wait(const Task &task, TaskPool &pool)
Definition: Task.cpp:20
int32_t I32
T * ResourcePtr
Definition: Resource.h:112
uint8_t U8
bool DebugBreak(const bool condition) noexcept
@ RES_LOADED
The resource is available for usage.
int16_t I16
Task * CreateTask(Predicate &&threadedFunction, bool allowedInIdle=true)
Definition: TaskPool.inl:45
constexpr F32 to_F32(const T value)
bool Finished(const Task &task) noexcept
Definition: Task.inl:38
constexpr D64 to_D64(const T value)
SceneNodeType
ToDo: Move particle emitter to components (it will make them way more dynamic) - Ionut.
Definition: SceneNodeFwd.h:47
DirectionalLightComponent(SceneGraphNode *sgn, PlatformContext &context)
constexpr auto TASK_NOP
Definition: Task.h:57
constexpr U16 BYTE_BUFFER_VERSION
FileError createDirectory(const ResourcePath &path)
eastl::vector< Type > vector
Definition: Vector.h:42
constexpr I16 I16_MAX
std::shared_lock< mutex > SharedLock
Definition: SharedMutex.h:49
FileError copyFile(const ResourcePath &sourcePath, const std::string_view sourceName, const ResourcePath &targetPath, const std::string_view targetName, const bool overwrite)
TaskPriority
Definition: Task.h:41
bool LoadFromXML(TerrainDescriptor &descriptor, const boost::property_tree::ptree &pt, std::string_view name)
Project & parent
Definition: DefaultScene.h:41
uint16_t U16
constexpr U64 _ID(const char *const str, const U64 value=val_64_const) noexcept
FORCE_INLINE Handle< T > CreateResource(const ResourceDescriptor< T > &descriptor, bool &wasInCache, std::atomic_uint &taskCounter)
constexpr U8 to_U8(const T value)
constexpr I16 to_I16(const T value)
string GetVariable(const TerrainDescriptor &descriptor, std::string_view name)
void Start(Task &task, TaskPool &pool, TaskPriority priority=TaskPriority::DONT_CARE, const DELEGATE< void > &onCompletionFunction={})
Definition: Task.cpp:9
SceneNodeHandle FromHandle(const Handle< T > handle)
constexpr size_t to_size(const T value)
SceneGraphNode * addSGN(SceneGraphNode *parent, const std::string_view name, const U32 componentMask, const Handle< T > handle, const bool nodeStatic, boost::property_tree::ptree &nodeTree)
Definition: Scene.cpp:392
void Parallel_For(TaskPool &pool, const ParallelForDescriptor &descriptor, const DELEGATE< void, const Task *, U32, U32 > &cbk)
Definition: TaskPool.cpp:428
bool fileExists(const ResourcePath &filePathAndName)
constexpr I32 to_I32(const T value)
void setDirection(const vec3< F32 > &direction)
bool dvd_erase_if(eastl::vector< T, A > &vec, Predicate &&pred)
Definition: Vector.h:109
int64_t I64
FORCE_INLINE T * Get(const Handle< T > handle)
void Init(ImTextureID texture1, ImTextureID texture2, ImTextureID dockTexture)
uint32_t U32
Project const SceneEntry & entry
Definition: DefaultScene.h:41
uint64_t U64
U16 registerInputActions() override
FileNameAndPath splitPathToNameAndLocation(const ResourcePath &input)
constexpr auto to_base(const Type value) -> Type
F32 _cloudCoverage
Definition: Sun.h:85
Quaternion< F32 > _orientation
struct Divide::Configuration::Rendering rendering
static NO_INLINE void errorfn(const char *format, T &&... args)
static NO_INLINE void printfn(const char *format, T &&... args)
vec2< I32 > _endDragPos
Definition: Scene.h:107
vec2< I32 > _startDragPos
Definition: Scene.h:106
vec4< F32 > _colourAndDensity
Definition: SceneState.h:74
vec4< F32 > _colourSunScatter
Definition: SceneState.h:75
const MouseState & state() const noexcept
bool _allowPoolIdle
If true, we'll inform the thread pool to execute other tasks while waiting for the all async tasks to...
Definition: TaskPool.h:53
U32 _partitionSize
How many elements should we process per async task.
Definition: TaskPool.h:45
bool _useCurrentThread
If true, we'll process a for partition on the calling thread.
Definition: TaskPool.h:51
bool _waitForFinish
If this is false, the Parallel_For call won't block the current thread.
Definition: TaskPool.h:49
TaskPriority _priority
Each async task will start with the same priority specified here.
Definition: TaskPool.h:47
U32 _iterCount
For loop iteration count.
Definition: TaskPool.h:43
Handle< ShaderProgram > _shaderProgramHandle
Definition: Pipeline.h:47
RenderStateBlock _stateBlock
Definition: Pipeline.h:46
const SceneNodeType * _ignoredTypes
Definition: SceneNodeFwd.h:80
const TimerClass _timerClass
Definition: Scene.h:316
const U64 _callbackIntervalUS
Definition: Scene.h:315
Definition: Scene.h:112
std::array< I64, MAX_SELECTIONS > _selections
Definition: Scene.h:100
Angle::RADIANS< F32 > azimuth
Definition: Sun.h:45
Angle::RADIANS< F32 > altitude
Definition: Sun.h:44
U32 _id
Definition: Task.h:51
vector< SceneNode > children
Definition: XMLParser.h:114