Radium Engine  1.5.0
BaseApplication.cpp
1 #include <Gui/AboutDialog/AboutDialog.hpp>
2 #include <Gui/BaseApplication.hpp>
3 #include <Gui/MainWindowInterface.hpp>
4 #include <Gui/Viewer/Viewer.hpp>
5 
6 #include <Core/CoreMacros.hpp>
7 #include <Core/Resources/Resources.hpp>
8 #include <Core/Tasks/Task.hpp>
9 #include <Core/Tasks/TaskQueue.hpp>
10 #include <Core/Types.hpp>
11 #include <Core/Utils/Color.hpp>
12 #include <Core/Utils/Log.hpp>
13 #include <Core/Utils/StringUtils.hpp>
14 #include <Core/Utils/Version.hpp>
15 
16 #include <Engine/Data/Texture.hpp>
17 #include <Engine/Data/TextureManager.hpp>
18 #include <Engine/RadiumEngine.hpp>
19 #include <Engine/Rendering/RenderObject.hpp>
20 #include <Engine/Scene/CameraComponent.hpp>
21 #include <Engine/Scene/EntityManager.hpp>
22 #include <Engine/Scene/GeometrySystem.hpp>
23 #include <Engine/Scene/SkeletonBasedAnimationSystem.hpp>
24 #include <Engine/Scene/SystemDisplay.hpp>
25 
26 #include <PluginBase/RadiumPluginInterface.hpp>
27 
28 #include <IO/CameraLoader/CameraLoader.hpp>
29 #ifdef IO_HAS_TINYPLY
30 # include <IO/TinyPlyLoader/TinyPlyFileLoader.hpp>
31 #endif
32 #ifdef IO_HAS_ASSIMP
33 # include <IO/AssimpLoader/AssimpFileLoader.hpp>
34 #endif
35 #ifdef IO_HAS_VOLUMES
36 # include <IO/VolumesLoader/VolumeLoader.hpp>
37 #endif
38 #include <QCommandLineParser>
39 #include <QDir>
40 #include <QMenuBar>
41 #include <QMessageBox>
42 #include <QOpenGLContext>
43 #include <QPluginLoader>
44 #include <QTimer>
45 
46 // Const parameters : TODO : make config / command line options
47 
48 namespace Ra {
49 namespace Gui {
50 
51 using namespace Core::Utils; // log
52 using namespace Core::Asset;
53 
54 #ifdef GUI_IS_COMPILED_WITH_DEBUG_INFO
55 static const bool expectPluginsDebug = true;
56 #else
57 static const bool expectPluginsDebug = false;
58 #endif
59 // the default priority for systems created here.
60 constexpr int defaultSystemPriority = 1000;
61 
63  char** argv,
64  QString applicationName,
65  QString organizationName ) :
66  QApplication( argc, argv ),
67  m_mainWindow( nullptr ),
68  m_engine( nullptr ),
69  m_taskQueue( nullptr ),
70  m_viewer( nullptr ),
71  m_frameTimer( new QTimer( this ) ),
72  m_frameCounter( 0 ),
73  m_frameCountBeforeUpdate( 60 ),
74  m_numFrames( 0 ),
75  m_maxThreads( RA_MAX_THREAD ),
76  m_realFrameRate( false ),
77  m_recordFrames( false ),
78  m_recordTimings( false ),
79  m_recordGraph( false ),
80  m_isAboutToQuit( false ) {
81  // Set application and organization names in order to ensure uniform
82  // QSettings configurations.
83  // \see http://doc.qt.io/qt-5/qsettings.html#QSettings-4
84  QCoreApplication::setOrganizationName( organizationName );
85  QCoreApplication::setApplicationName( applicationName );
86 
87  m_targetFPS = 60; // Default
89  m_parser = new QCommandLineParser;
90 
91  QCommandLineParser& parser { *m_parser };
92  parser.setApplicationDescription( "Radium Engine RPZ, TMTC" );
93  parser.addHelpOption();
94  parser.addVersionOption();
95 
97  QCommandLineOption fpsOpt(
98  QStringList { "r", "framerate", "fps" },
99  "Control the application framerate, 0 to disable it (and run as fast as possible).",
100  "number",
101  "60" );
102  QCommandLineOption maxThreadsOpt(
103  QStringList { "m", "maxthreads", "max-threads" },
104  "Control the maximum number of threads. 0 will set to the number of cores available",
105  "number",
106  "0" );
107  QCommandLineOption numFramesOpt(
108  QStringList { "n", "numframes" }, "Run for a fixed number of frames.", "number", "0" );
109  QCommandLineOption pluginOpt( QStringList { "p", "plugins", "pluginsPath" },
110  "Set the path to the plugin dlls.",
111  "folder",
112  "Plugins" );
113  QCommandLineOption pluginLoadOpt(
114  QStringList { "l", "load", "loadPlugin" },
115  "Only load plugin with the given name (filename without the extension). If this option is "
116  "not used, all plugins in the plugins folder will be loaded. ",
117  "name" );
118  QCommandLineOption pluginIgnoreOpt( QStringList { "i", "ignore", "ignorePlugin" },
119  "Ignore plugins with the given name. If the name appears "
120  "within both load and ignore options, it will be ignored.",
121  "name" );
122  QCommandLineOption fileOpt( QStringList { "f", "file", "scene" },
123  "Open a scene file at startup.",
124  "file name",
125  "foo.bar" );
126 
127  QCommandLineOption camOpt( QStringList { "c", "camera", "cam" },
128  "Open a camera file at startup",
129  "file name",
130  "foo.bar" );
131  QCommandLineOption recordOpt( QStringList { "s", "recordFrames" },
132  "Enable snapshot recording." );
133 
134  QCommandLineOption datapathOpt( QStringList { "d", "data", "export" },
135  "Set the default data path and store it in the settings.",
136  "folder",
137  "./" );
139 
140  parser.addOptions( { fpsOpt,
141  pluginOpt,
142  pluginLoadOpt,
143  pluginIgnoreOpt,
144  fileOpt,
145  camOpt,
146  maxThreadsOpt,
147  numFramesOpt,
148  recordOpt,
149  datapathOpt } );
150 
151  if ( !parser.parse( this->arguments() ) ) {
152  LOG( logWARNING ) << "Command line parsing failed due to unsupported or missing options";
153  }
154  if ( parser.isSet( datapathOpt ) ) {
155  auto p = parser.value( datapathOpt ).toStdString();
157  QSettings settings;
158  settings.setValue( "data_path", p.c_str() );
159  }
160  else {
161  QSettings settings;
162  if ( settings.contains( "data_path" ) ) {
163  auto p = settings.value( "data_path" ).toString().toStdString();
165  }
166  }
167  if ( parser.isSet( fpsOpt ) ) m_targetFPS = parser.value( fpsOpt ).toUInt();
168  if ( parser.isSet( pluginOpt ) ) m_pluginPath = parser.value( pluginOpt ).toStdString();
169  if ( parser.isSet( numFramesOpt ) ) m_numFrames = parser.value( numFramesOpt ).toUInt();
170  if ( parser.isSet( maxThreadsOpt ) ) m_maxThreads = parser.value( maxThreadsOpt ).toUInt();
171  if ( parser.isSet( recordOpt ) ) {
172  m_recordFrames = true;
173  setContinuousUpdate( true );
174  }
175 
176  {
177  std::time_t startTime = std::time( nullptr );
178  std::tm* startTm = std::localtime( &startTime );
179  std::stringstream ssTp;
180  ssTp << std::put_time( startTm, "%Y%m%d-%H%M%S" );
182  }
183 
184  QDir().mkdir( m_exportFoldername.c_str() );
185 
186  // Boilerplate print.
187  LOG( logINFO ) << "*** Radium Engine Base Application ***";
188  std::stringstream config;
189 #if defined( CORE_DEBUG )
190  config << "Debug Build ";
191 #else
192  config << "Release Build ";
193 #endif
194 #if defined( CORE_ENABLE_ASSERT )
195  config << "(with asserts) --";
196 #else
197  config << " --";
198 #endif
199 
200 #if defined( ARCH_X86 )
201  config << " 32 bits x86";
202 #elif defined( ARCH_X64 )
203  config << " 64 bits x64";
204 #endif
205  LOG( logINFO ) << config.str();
206 
207  config.str( std::string() );
208  config << "Floating point format : ";
209 #if defined( CORE_USE_DOUBLE )
210  config << "double precision";
211 #else
212  config << "single precision";
213 #endif
214  LOG( logINFO ) << config.str();
215 
216  config.str( std::string() );
217  config << "core build: " << Version::compiler << " - " << Version::compileDate << " "
218  << Version::compileTime;
219  LOG( logINFO ) << config.str();
220 
221  LOG( logINFO ) << "Git changeset: " << Version::gitChangeSet;
222 
223  LOG( logINFO ) << "Qt Version: " << qVersion();
224 
225  LOG( logINFO ) << "Max Thread: " << m_maxThreads;
226 
227  LOG( logINFO ) << "Output data path : " << Ra::Core::Resources::getDataPath();
228 
229 #ifdef OS_MACOS
230  {
231  // Note on QSettings on macOS :
232  // macOS, since Maverick, cache the application settings and when changing the settings file
233  // outside of the application, one need to wait for the cache to being reloaded (reboot or
234  // wait a given duration). To force reload of QSettings, use `defaults read
235  // settingfile.plist` source :
236  // https://nethack.ch/2014/03/30/quick-tip-flush-os-x-mavericks-plist-file-cache/ That's why
237  // we display the QSettings filename here ...
238  QSettings settings;
239  LOG( logINFO ) << "Settings file : " << settings.fileName().toStdString();
240  }
241 #endif
242 
243  // Create the instance of the keymapping manager, before creating
244  // Qt main windows, which may throw events on Microsoft Windows
245  Gui::KeyMappingManager::createInstance();
246 }
247 
248 void BaseApplication::initialize( const WindowFactory& factory,
249  const glbinding::Version& version ) {
250  // Create default format for Qt.
251  QSurfaceFormat format;
252  format.setVersion( version.majorVersion(), version.minorVersion() );
253  format.setProfile( QSurfaceFormat::CoreProfile );
254  format.setDepthBufferSize( 24 );
255  format.setStencilBufferSize( 8 );
256  format.setSamples( 16 );
257  format.setSwapBehavior( QSurfaceFormat::DoubleBuffer );
258  format.setSwapInterval( 0 );
259  QSurfaceFormat::setDefaultFormat( format );
260 
261  // == Configure Engine and basic scene services //
262  // Create engine
263  m_engine = Engine::RadiumEngine::createInstance();
264  m_engine->initialize();
265 
266  // Configure Engine basic scene services (non openGL dependant)
268 
269  // == Configure OpenGL context and drawing/application window //
270  // Create main window.
271  m_mainWindow.reset( factory.createMainWindow() );
272  m_viewer = m_mainWindow->getViewer();
273  CORE_ASSERT( m_viewer != nullptr, "GUI was not initialized" );
275 
276  // == Configure event management on the drawing/application window
277  // Connect the signals and allow all pending events to be processed
278  // (thus the viewer should have initialized the OpenGL context..)
280  m_mainWindow->show();
281 
282  // processEvents();
283  CORE_ASSERT( m_viewer->getContext() != nullptr, "OpenGL context was not created" );
284  CORE_ASSERT( m_viewer->getContext()->isValid(), "OpenGL was not initialized" );
285 
286  // == Configure Application plugins == //
287  // Initialize plugin context
288  m_pluginContext.m_engine = m_engine;
289  m_pluginContext.m_selectionManager = m_mainWindow->getSelectionManager();
290  m_pluginContext.m_timeline = m_mainWindow->getTimeline();
291  m_pluginContext.m_pickingManager = m_viewer->getPickingManager();
292  m_pluginContext.m_viewer = m_viewer;
293  m_pluginContext.m_exportDir = m_exportFoldername;
294 
295  connect( &m_pluginContext,
297  this,
298  &BaseApplication::setContinuousUpdate );
299  connect(
300  &m_pluginContext, &Plugins::Context::askForUpdate, this, &BaseApplication::askForUpdate );
301 
302  QCommandLineParser& parser { *m_parser };
303  // TODO at startup, only load "standard plugins". This must be extended.
304  auto pluginsPathOptional { Core::Resources::getRadiumPluginsPath() };
305  auto pluginsPath = pluginsPathOptional.value_or( "[[Default plugin path not found]]" );
306 
307  // Load installed plugins plugins
308  if ( !loadPlugins(
309  pluginsPath, parser.values( "loadPlugin" ), parser.values( "ignorePlugin" ) ) ) {
310  LOG( logDEBUG ) << "No plugin found in default path " << pluginsPath;
311  }
312  // load supplemental plugins
313  {
314  QSettings settings;
315  QStringList pluginPaths = settings.value( "plugins/paths" ).value<QStringList>();
316  for ( const auto& s : pluginPaths ) {
317  loadPlugins(
318  s.toStdString(), parser.values( "loadPlugin" ), parser.values( "ignorePlugin" ) );
319  }
320  }
321  // == Configure bundled Radium::IO services == //
322  // Make builtin loaders the fallback if no plugins can load some file format
323 #ifdef IO_HAS_TINYPLY
324  // Register before AssimpFileLoader, in order to ease override of such
325  // custom loader (first loader able to load is taking the file)
327  std::shared_ptr<FileLoaderInterface>( new IO::TinyPlyFileLoader() ) );
328 #endif
330  std::shared_ptr<FileLoaderInterface>( new IO::CameraFileLoader() ) );
331 #ifdef IO_HAS_ASSIMP
333  std::shared_ptr<FileLoaderInterface>( new IO::AssimpFileLoader() ) );
334 #endif
335 #ifdef IO_HAS_VOLUMES
336  m_engine->registerFileLoader( std::shared_ptr<FileLoaderInterface>( new IO::VolumeLoader() ) );
337 #endif
338  // Allow derived application to add custom plugins and services
340 
341  // Create task queue with N-1 threads (we keep one for rendering),
342  // unless monothread CPU
343  uint numThreads =
344  std::max( m_maxThreads == 0 ? RA_MAX_THREAD : std::min( m_maxThreads, RA_MAX_THREAD ), 1u );
345  m_taskQueue = std::make_unique<Core::TaskQueue>( numThreads );
346 
347  setupScene();
348  emit starting();
349 
350  // Files have been required, load them.
351  if ( parser.isSet( "scene" ) ) {
352  for ( const auto& filename : parser.values( "scene" ) ) {
353  loadFile( filename );
354  }
355  }
356  // A camera has been required, load it.
357  if ( parser.isSet( "camera" ) ) {
358  if ( loadFile( parser.value( "camera" ) ) ) {
360  // loadFile call camera system handle asset loading, so cameras from the file are
361  // already loaded.
362  }
363  }
364 
365  m_mainWindow->configure();
366 
367  m_lastFrameStart = Core::Utils::Clock::now();
368 
369  connect( m_frameTimer, &QTimer::timeout, this, &BaseApplication::updateRadiumFrameIfNeeded );
370  const int deltaTime( m_targetFPS == 0 ? 1 : 1000 / m_targetFPS );
371  m_frameTimer->start( deltaTime );
372 }
373 
375  // TODO : modify interface to allow the app to ask for specific systems registration
376  // Register the GeometrySystem converting loaded assets to meshes
378  "GeometrySystem", new Ra::Engine::Scene::GeometrySystem, defaultSystemPriority );
379  // Register the TimeSystem managing time dependant systems
380  Scalar dt = ( m_targetFPS == 0 ? 1_ra / 60_ra : 1_ra / m_targetFPS );
382  // Register the SkeletonBasedAnimationSystem converting loaded assets to
383  // skeletons and skinning data
384  m_engine->registerSystem( "SkeletonBasedAnimationSystem",
386  defaultSystemPriority );
387 }
388 
389 void BaseApplication::addRadiumMenu() {
390  // add the "about" action
391  auto qtWnd = dynamic_cast<QMainWindow*>( m_mainWindow.get() );
392  if ( qtWnd ) {
393  auto windowMenuBar = m_mainWindow->menuBar();
394  auto mainMenu = windowMenuBar->findChild<QMenu*>( "Radium" );
395  if ( mainMenu == nullptr ) { mainMenu = windowMenuBar->addMenu( "Radium" ); }
396  auto aboutDiag = new AboutDialog( qtWnd );
397  aboutDiag->setModal( false );
398  mainMenu->addAction( "About", aboutDiag, &QDialog::show );
399  connect( aboutDiag, &AboutDialog::settings, this, &BaseApplication::editSettings );
400  connect( aboutDiag,
401  &AboutDialog::help,
402  m_mainWindow.get(),
403  &MainWindowInterface::displayHelpDialog );
404  }
405 }
406 
408  // initialize here the OpenGL part of the engine used by the application
410 }
411 
413  // Connect the signals to lambda calling virtual methods
414  // connect( m_viewer, &Gui::Viewer::requestEngineOpenGLInitialization, this,
415  // &BaseApplication::engineOpenGLInitialize );
417  this->engineOpenGLInitialize();
418  } );
419  connect( m_viewer, &Gui::Viewer::glInitialized, this, &BaseApplication::initializeGl );
420 
421  connect(
422  m_mainWindow.get(), &MainWindowInterface::closed, this, &BaseApplication::appNeedsToQuit );
423  connect( this, &QGuiApplication::lastWindowClosed, m_viewer, &Gui::WindowQt::cleanupGL );
424 
425  connect( m_viewer, &Gui::Viewer::needUpdate, this, &BaseApplication::askForUpdate );
426 }
427 
428 void BaseApplication::setupScene() {
429 
430  using namespace Engine::Data::DrawPrimitives;
431 
432  auto grid = Primitive( Engine::Scene::SystemEntity::uiCmp(),
433  Grid( Core::Vector3::Zero(),
434  Core::Vector3::UnitX(),
435  Core::Vector3::UnitZ(),
436  Core::Utils::Color::Grey( 0.6f ) ) );
437  grid->setPickable( false );
439 
440  auto frame = Primitive( Engine::Scene::SystemEntity::uiCmp(),
441  Frame( Ra::Core::Transform::Identity(), 0.05f ) );
442  frame->setPickable( false );
444 }
445 
446 bool BaseApplication::loadFile( QString path ) {
447  std::string filename = path.toLocal8Bit().data();
448  LOG( logINFO ) << "Loading file " << filename << "...";
449  bool res = m_engine->loadFile( filename );
450 
451  if ( !res ) {
452  LOG( logERROR ) << "Aborting file loading !";
453 
454  return false;
455  }
456 
458 
459  m_mainWindow->prepareDisplay();
460 
461  emit loadComplete();
462  return true;
463 }
464 
465 void BaseApplication::framesCountForStatsChanged( uint count ) {
466  m_frameCountBeforeUpdate = count;
467 }
468 
470  FrameTimerData timerData;
471  timerData.frameStart = Core::Utils::Clock::now();
472 
473  // ----------
474  // 0. Compute time since last frame.
475  const Scalar dt =
476  m_realFrameRate ? Core::Utils::getIntervalSeconds( m_lastFrameStart, timerData.frameStart )
477  : 1.f / Scalar( m_targetFPS );
478  m_lastFrameStart = timerData.frameStart;
479 
480  // ----------
481  // 1. Gather user input and dispatch it.
482  // Get picking results from last frame and forward it to the selection.
484 
485  timerData.tasksStart = Core::Utils::Clock::now();
486 
487  // ----------
488  // 2. Run the engine task queue.
489  m_engine->getTasks( m_taskQueue.get(), dt );
490 
491  if ( m_recordGraph ) { m_taskQueue->printTaskGraph( std::cout ); }
492 
493  // Run one frame of tasks
494  m_taskQueue->startTasks();
495  m_taskQueue->waitForTasks();
496  timerData.taskData = m_taskQueue->getTimerData();
497  m_taskQueue->flushTaskQueue();
498 
499  timerData.tasksEnd = Core::Utils::Clock::now();
500 
501  // run engine gpu tasks (need active context)
506 
507  // also update gizmo manager to deal with annimation playing / reset
508  // m_viewer->getGizmoManager()->updateValues();
509 
510  // update viewer internal time-dependant state
511  m_viewer->update( dt );
512 
513  // ----------
514  // 3. Kickoff rendering
515  m_viewer->startRendering( dt );
517 
518  timerData.renderData = m_viewer->getRenderer()->getTimerData();
519 
520  // ----------
521  // 4. Synchronize whatever needs synchronisation
523 
524  // ----------
525  // 5. Frame end.
526  timerData.frameEnd = Core::Utils::Clock::now();
527  timerData.numFrame = m_frameCounter;
528 
529  if ( m_recordTimings ) { timerData.print( std::cout ); }
530 
531  m_timerData.push_back( timerData );
532 
533  if ( m_recordFrames ) { recordFrame(); }
534 
535  ++m_frameCounter;
536 
537  if ( m_numFrames > 0 && m_frameCounter >= m_numFrames ) { appNeedsToQuit(); }
538 
539  if ( m_frameCounter % m_frameCountBeforeUpdate == 0 ) {
540  emit( updateFrameStats( m_timerData ) );
541  m_timerData.clear();
542  }
543  m_mainWindow->onFrameComplete();
544 }
545 
546 void BaseApplication::appNeedsToQuit() {
547  LOG( logDEBUG ) << "About to quit.";
548  m_isAboutToQuit = true;
549 }
550 
552  // Initialize opengl plugins added before openGL was ready
553  if ( !m_openGLPlugins.empty() ) {
554  for ( auto plugin : m_openGLPlugins ) {
555  plugin->openGlInitialize( m_pluginContext );
556  }
557  m_openGLPlugins.clear();
558  }
559 }
560 
561 void BaseApplication::setRealFrameRate( bool on ) {
562  m_realFrameRate = on;
563 }
564 
565 void BaseApplication::setRecordFrames( bool on ) {
566  setContinuousUpdate( on );
567  if ( on ) askForUpdate();
568  m_recordFrames = on;
569 }
570 
571 void BaseApplication::recordFrame() {
572  std::stringstream filename;
573  filename << m_exportFoldername << "/radiumframe_" << std::setw( 6 ) << std::setfill( '0' )
574  << m_frameCounter << ".png";
575  m_viewer->grabFrame( filename.str() );
576 }
577 
578 BaseApplication::~BaseApplication() {
579  emit stopping();
580  m_mainWindow->cleanup();
581  m_engine->cleanup();
582  Ra::Engine::RadiumEngine::destroyInstance();
583 
584  // This will remove the directory if empty.
585  QDir().rmdir( m_exportFoldername.c_str() );
586 }
587 
588 bool BaseApplication::loadPlugins( const std::string& pluginsPath,
589  const QStringList& loadList,
590  const QStringList& ignoreList ) {
591  QDir pluginsDir( qApp->applicationDirPath() );
592  bool result = pluginsDir.cd( pluginsPath.c_str() );
593 
594  if ( result ) {
595  LOG( logINFO ) << " *** Loading Plugins from " << pluginsDir.absolutePath().toStdString()
596  << " ***";
597  }
598  else {
599  LOG( logDEBUG ) << "Cannot open specified plugins directory "
600  << pluginsDir.absolutePath().toStdString();
601  return false;
602  }
603  bool res = true;
604  uint pluginCpt = 0;
605 
606  for ( const auto& filename : pluginsDir.entryList( QDir::Files ) ) {
607 
608  std::string ext = Core::Utils::getFileExt( filename.toStdString() );
609 #if defined( OS_WINDOWS )
610  std::string sysDllExt = "dll";
611 #elif defined( OS_LINUX )
612  std::string sysDllExt = "so";
613 #elif defined( OS_MACOS )
614  std::string sysDllExt = "dylib";
615 #else
616  static_assert( false, "System configuration not handled" );
617 #endif
618  if ( ext == sysDllExt ) {
619  std::string basename = Core::Utils::getBaseName( filename.toStdString(), false );
620 
621  auto stringCmp = [basename]( const QString& str ) {
622  return str.toStdString() == basename;
623  };
624 
625  if ( !loadList.empty() &&
626  std::find_if( loadList.begin(), loadList.end(), stringCmp ) == loadList.end() ) {
627  LOG( logDEBUG ) << "Ignoring " << filename.toStdString() << " (not on load list)";
628  continue;
629  }
630  if ( std::find_if( ignoreList.begin(), ignoreList.end(), stringCmp ) !=
631  ignoreList.end() ) {
632  LOG( logDEBUG ) << "Ignoring " << filename.toStdString() << " (on ignore list)";
633  continue;
634  }
635 
636  QPluginLoader pluginLoader( pluginsDir.absoluteFilePath( filename ) );
637  // Force symbol resolution at load time.
638  pluginLoader.setLoadHints( QLibrary::ResolveAllSymbolsHint );
639 
640  // Looking for Radium plugin signature on metadata
641  auto metadata = pluginLoader.metaData()["MetaData"].toObject();
642  if ( metadata.isEmpty() || !metadata.contains( "com.storm-irit.RadiumEngine" ) ||
643  metadata["com.storm-irit.RadiumEngine"].toString().compare( "plugin" ) != 0 ) {
644  LOG( logDEBUG ) << "File " << filename.toStdString() << " is not a Radium plugin.";
645  continue;
646  }
647 
648  // detect if the plugin meets the minimal requirements
649  // if not, triggers a QDialog explaining the error, and abort the application
650  // We choose to stop the application to force all the plugins to be updated
651  if ( !metadata.contains( "isDebug" ) ) {
652  QMessageBox::critical( m_mainWindow.get(),
653  "Invalid plugin loaded (see Q_RADIUM_PLUGIN_METADATA)",
654  QString( "The application tried to load an unsupported "
655  "plugin. The application will stop.\n" ) +
656  QString( "Plugin path: " ) +
657  pluginsDir.absoluteFilePath( filename ) );
658  appNeedsToQuit();
659  return false;
660  }
661  bool isPluginDebug = metadata["isDebug"].toString().compare( "true" ) == 0;
662  if ( expectPluginsDebug == isPluginDebug ) {
663  const auto [it, success] = m_loadedPlugins.insert( { basename, pluginsPath } );
664  if ( !success ) {
665  LOG( logDEBUG )
666  << "Unable to load plugin " << basename << " from " << pluginsPath
667  << ".\n\t\tPlugin was already loaded from " << it->second;
668  continue;
669  }
670  LOG( logINFO ) << "Found plugin " << filename.toStdString();
671  // load the plugin
672  QObject* plugin = pluginLoader.instance();
673  if ( plugin ) {
674  auto loadedPlugin = qobject_cast<Plugins::RadiumPluginInterface*>( plugin );
675  if ( loadedPlugin ) {
676  ++pluginCpt;
677  loadedPlugin->registerPlugin( m_pluginContext );
678  m_mainWindow->updateUi( loadedPlugin );
679 
680  if ( loadedPlugin->doAddRenderer() ) {
681  std::vector<std::shared_ptr<Engine::Rendering::Renderer>> tmpR;
682  loadedPlugin->addRenderers( &tmpR );
683  CORE_ASSERT( !tmpR.empty(),
684  "This plugin is expected to add a renderer" );
685  for ( const auto& ptr : tmpR ) {
686  std::string name =
687  ptr->getRendererName() + "(" + filename.toStdString() + ")";
688  m_mainWindow->addRenderer( name, ptr );
689  }
690  }
691 
692  if ( loadedPlugin->doAddFileLoader() ) {
693  std::vector<std::shared_ptr<FileLoaderInterface>> tmpL;
694  loadedPlugin->addFileLoaders( &tmpL );
695  CORE_ASSERT( !tmpL.empty(),
696  "This plugin is expected to add file loaders" );
697  for ( auto& ptr : tmpL ) {
699  }
700  }
701 
702  if ( loadedPlugin->doAddROpenGLInitializer() ) {
703  if ( m_viewer->isOpenGlInitialized() ) {
704  LOG( logDEBUG ) << "Direct OpenGL initialization for plugin "
705  << filename.toStdString();
706  // OpenGL is ready, initialize openGL part of the plugin
708  loadedPlugin->openGlInitialize( m_pluginContext );
710  }
711  else {
712  // Defer OpenGL initialisation
713  LOG( logDEBUG ) << "Defered OpenGL initialization for plugin "
714  << filename.toStdString();
715  m_openGLPlugins.push_back( loadedPlugin );
716  }
717  }
718  }
719  else {
720  LOG( logERROR ) << "Something went wrong while trying to cast plugin "
721  << filename.toStdString();
722  res = false;
723  }
724  }
725  else {
726  LOG( logERROR ) << "Something went wrong while trying to load plugin "
727  << filename.toStdString() << " : "
728  << pluginLoader.errorString().toStdString();
729  res = false;
730  }
731  }
732  else {
733  LOG( logERROR ) << "Skipped plugin " << filename.toStdString()
734  << " : invalid build mode. Full path: "
735  << pluginsDir.absoluteFilePath( filename ).toStdString();
736  res = false;
737  }
738  }
739  }
740 
741  if ( pluginCpt == 0 ) { LOG( logINFO ) << "No plugin found or loaded."; }
742  else { LOG( logINFO ) << "Loaded " << pluginCpt << " plugins."; }
743 
744  return res;
745 }
746 
747 void BaseApplication::setRecordTimings( bool on ) {
748  m_recordTimings = on;
749 }
750 
751 void BaseApplication::setRecordGraph( bool on ) {
752  m_recordGraph = on;
753 }
754 
755 void BaseApplication::addPluginDirectory( const std::string& pluginDir ) {
756  QSettings settings;
757  QStringList pluginPaths = settings.value( "plugins/paths" ).value<QStringList>();
758  LOG( logINFO ) << "Registered plugin paths are : ";
759  for ( const auto& s : pluginPaths ) {
760  LOG( logINFO ) << s.toStdString();
761  }
762  pluginPaths.append( pluginDir.c_str() );
763  settings.setValue( "plugins/paths", pluginPaths );
764  loadPlugins( pluginDir, QStringList(), QStringList() );
765 }
766 
768  QSettings settings;
769  settings.setValue( "plugins/paths", QStringList() );
770 }
771 
773  QMessageBox notYetImplemented;
774  notYetImplemented.setText( "Settings editor is not yet available !" );
775  notYetImplemented.exec();
776 }
777 
778 std::string BaseApplication::getHelpText() const {
779  std::string helpText { "<h1>BaseApplication command line parameters</h1><\n>" };
780  helpText += "<p>Not yet written.</p><br/><\n>";
781  return helpText;
782 }
783 
784 } // namespace Gui
785 } // namespace Ra
bool loadFile(const std::string &file)
void getTasks(Core::TaskQueue *taskQueue, Scalar dt)
void registerFileLoader(std::shared_ptr< Core::Asset::FileLoaderInterface > fileLoader)
bool setConstantTimeStep(Scalar dt, bool forceConstantTime=false)
Sets the time delta between two frames for Constant-time time flow.
bool registerSystem(const std::string &name, Scene::System *system, int priority=1)
const TimerData & getTimerData() const
Definition: Renderer.hpp:555
Core::Utils::Index addRenderObject(Rendering::RenderObject *renderObject)
Add a new render object to the component. This adds the RO to the manager for drawing.
Definition: Component.cpp:41
static UiComponent * uiCmp()
Access the UI Component.
void addPluginDirectory(const std::string &pluginDir)
Allow the user to register a specific plugin directory for the application.
std::unique_ptr< Gui::MainWindowInterface > m_mainWindow
Application main window and GUI root class.
bool m_recordGraph
If true, print the task graph;.
Engine::RadiumEngine * m_engine
Instance of the radium engine.
BaseApplication(int &argc, char **argv, QString applicationName="RadiumEngine", QString organizationName="STORM-IRIT")
bool m_recordFrames
If true, dump each frame to a PNG file.
virtual void addApplicationExtension()
void radiumFrame()
Advance the engine for one frame.
std::unique_ptr< Core::TaskQueue > m_taskQueue
Task queue for processing tasks.
std::map< std::string, std::string > m_loadedPlugins
maps name and paths of already loaded plugins
bool m_recordTimings
If true, print the detailed timings of each frame.
QTimer * m_frameTimer
Timer to wake us up at every frame start.
void clearPluginDirectories()
Remove all registered plugin directories (except the default Radium Bundle one)
Core::Utils::TimePoint m_lastFrameStart
Time since the last frame start.
uint m_targetFPS
Number of frames per second to generate.
void createConnections()
Create signal / slots connections.
void initialize(const WindowFactory &factory, const glbinding::Version &glVersion={ 4, 1 })
void stopping()
Fired when the engine is about to stop.
virtual std::string getHelpText() const
Get the html formatted help text.
std::vector< Ra::Plugins::RadiumPluginInterface * > m_openGLPlugins
Plugins that need to be initialized once OpenGL is ready.
bool m_realFrameRate
If true, use the wall clock to advance the engine. If false, use a fixed time step.
bool m_isAboutToQuit
True if the applicatioon is about to quit. prevent to use resources that are being released.
virtual void engineBaseInitialization()
Gui::Viewer * m_viewer
Pointer to OpenGL Viewer for render call (belongs to MainWindow).
bool loadPlugins(const std::string &pluginsPath, const QStringList &loadList, const QStringList &ignoreList)
void starting()
Fired when the engine has just started, before the frame timer is set.
std::string m_exportFoldername
Name of the folder where exported data goes.
virtual void engineOpenGLInitialize()
slot called when the OpenGL need to be initialized
void closed()
Emitted when the closed button has been hit.
void startRendering(const Scalar dt)
Start rendering (potentially asynchronously in a separate thread)
Definition: Viewer.cpp:217
QOpenGLContext * getContext() const
Access to the OpenGL context of the Viewer.
Definition: Viewer.hpp:79
void swapBuffers()
Blocks until rendering is finished.
Definition: Viewer.cpp:263
void requestEngineOpenGLInitialization()
virtual void update(const Scalar dt)
Update the internal viewer state to the (application) time dt.
Definition: Viewer.cpp:210
PickingManager * getPickingManager()
Access to the feature picking manager.
Definition: Viewer.cpp:206
const Engine::Rendering::Renderer * getRenderer() const
Read-only access to renderer.
Definition: Viewer.cpp:179
virtual void setupKeyMappingCallbacks()
add observers to keyMappingManager for gizmo, camera and viewer.
Definition: Viewer.cpp:74
void grabFrame(const std::string &filename)
Write the current frame as an image. Supports either BMP or PNG file names.
Definition: Viewer.cpp:317
void processPicking()
Emits signals corresponding to picking requests.
Definition: Viewer.cpp:268
void cleanupGL()
call deinitializeGL if needed, with context activated
Definition: WindowQt.cpp:160
void makeCurrent()
Definition: WindowQt.cpp:80
void doneCurrent()
Definition: WindowQt.cpp:89
This class loads scenes containing MESHES only (not point-clouds)
Loads density grid for volume data. This loader support 2 file formats for density grid data.
void askForUpdate()
ask for single-shot rendering update
void setContinuousUpdate(bool b)
enable continuous rendering update
std::string getDataPath()
Get the current data path.
Definition: Resources.cpp:70
optional< std::string > getRadiumPluginsPath()
Get the path of Radium embedded plugins.
Definition: Resources.cpp:44
void pushDataPath(std::string datapath)
Push a new data path.
Definition: Resources.cpp:82
Definition: Cage.cpp:3
This struct holds all timings for one frame of the engine.