1#include <Gui/AboutDialog/AboutDialog.hpp>
2#include <Gui/BaseApplication.hpp>
3#include <Gui/MainWindowInterface.hpp>
4#include <Gui/Viewer/Viewer.hpp>
6#include <Core/CoreMacros.hpp>
7#include <Core/Resources/Resources.hpp>
8#include <Core/Tasks/Task.hpp>
9#include <Core/Tasks/TaskQueue.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>
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>
26#include <PluginBase/RadiumPluginInterface.hpp>
28#include <IO/CameraLoader/CameraLoader.hpp>
30# include <IO/TinyPlyLoader/TinyPlyFileLoader.hpp>
33# include <IO/AssimpLoader/AssimpFileLoader.hpp>
36# include <IO/VolumesLoader/VolumeLoader.hpp>
38#include <QCommandLineParser>
42#include <QOpenGLContext>
43#include <QPluginLoader>
51using namespace Core::Utils;
52using namespace Core::Asset;
54#ifdef GUI_IS_COMPILED_WITH_DEBUG_INFO
55static const bool expectPluginsDebug =
true;
57static const bool expectPluginsDebug =
false;
60constexpr int defaultSystemPriority = 1000;
64 QString applicationName,
65 QString organizationName ) :
66 QApplication( argc, argv ),
67 m_mainWindow( nullptr ),
69 m_taskQueue( nullptr ),
71 m_frameTimer( new QTimer( this ) ),
73 m_frameCountBeforeUpdate( 60 ),
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 ) {
84 QCoreApplication::setOrganizationName( organizationName );
85 QCoreApplication::setApplicationName( applicationName );
89 m_parser =
new QCommandLineParser;
91 QCommandLineParser& parser { *m_parser };
92 parser.setApplicationDescription(
"Radium Engine RPZ, TMTC" );
93 parser.addHelpOption();
94 parser.addVersionOption();
97 QCommandLineOption fpsOpt(
98 QStringList {
"r",
"framerate",
"fps" },
99 "Control the application framerate, 0 to disable it (and run as fast as possible).",
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",
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.",
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. ",
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.",
122 QCommandLineOption fileOpt( QStringList {
"f",
"file",
"scene" },
123 "Open a scene file at startup.",
127 QCommandLineOption camOpt( QStringList {
"c",
"camera",
"cam" },
128 "Open a camera file at startup",
131 QCommandLineOption recordOpt( QStringList {
"s",
"recordFrames" },
132 "Enable snapshot recording." );
134 QCommandLineOption datapathOpt( QStringList {
"d",
"data",
"export" },
135 "Set the default data path and store it in the settings.",
140 parser.addOptions( { fpsOpt,
151 if ( !parser.parse( this->arguments() ) ) {
152 LOG( logWARNING ) <<
"Command line parsing failed due to unsupported or missing options";
154 if ( parser.isSet( datapathOpt ) ) {
155 auto p = parser.value( datapathOpt ).toStdString();
158 settings.setValue(
"data_path", p.c_str() );
162 if ( settings.contains(
"data_path" ) ) {
163 auto p = settings.value(
"data_path" ).toString().toStdString();
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 ) ) {
173 setContinuousUpdate(
true );
187 LOG( logINFO ) <<
"*** Radium Engine Base Application ***";
189#if defined( CORE_DEBUG )
190 config <<
"Debug Build ";
192 config <<
"Release Build ";
194#if defined( CORE_ENABLE_ASSERT )
195 config <<
"(with asserts) --";
200#if defined( ARCH_X86 )
201 config <<
" 32 bits x86";
202#elif defined( ARCH_X64 )
203 config <<
" 64 bits x64";
205 LOG( logINFO ) << config.
str();
208 config <<
"Floating point format : ";
209#if defined( CORE_USE_DOUBLE )
210 config <<
"double precision";
212 config <<
"single precision";
214 LOG( logINFO ) << config.
str();
217 config <<
"core build: " << Version::compiler <<
" - " << Version::compileDate <<
" "
218 << Version::compileTime;
219 LOG( logINFO ) << config.
str();
221 LOG( logINFO ) <<
"Git changeset: " << Version::gitChangeSet;
223 LOG( logINFO ) <<
"Qt Version: " << qVersion();
225 LOG( logINFO ) <<
"Max Thread: " << m_maxThreads;
239 LOG( logINFO ) <<
"Settings file : " << settings.fileName().toStdString();
245 Gui::KeyMappingManager::createInstance();
249 const glbinding::Version& version ) {
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 );
263 m_engine = Engine::RadiumEngine::createInstance();
273 CORE_ASSERT(
m_viewer !=
nullptr,
"GUI was not initialized" );
288 m_pluginContext.m_engine =
m_engine;
289 m_pluginContext.m_selectionManager =
m_mainWindow->getSelectionManager();
290 m_pluginContext.m_timeline =
m_mainWindow->getTimeline();
292 m_pluginContext.m_viewer =
m_viewer;
295 connect( &m_pluginContext,
298 &BaseApplication::setContinuousUpdate );
302 QCommandLineParser& parser { *m_parser };
305 auto pluginsPath = pluginsPathOptional.value_or(
"[[Default plugin path not found]]" );
309 pluginsPath, parser.values(
"loadPlugin" ), parser.values(
"ignorePlugin" ) ) ) {
310 LOG( logDEBUG ) <<
"No plugin found in default path " << pluginsPath;
315 QStringList pluginPaths = settings.value(
"plugins/paths" ).value<QStringList>();
316 for (
const auto& s : pluginPaths ) {
318 s.toStdString(), parser.values(
"loadPlugin" ), parser.values(
"ignorePlugin" ) );
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 );
351 if ( parser.isSet(
"scene" ) ) {
352 for (
const auto& filename : parser.values(
"scene" ) ) {
353 loadFile( filename );
357 if ( parser.isSet(
"camera" ) ) {
358 if ( loadFile( parser.value(
"camera" ) ) ) {
369 connect(
m_frameTimer, &QTimer::timeout,
this, &BaseApplication::updateRadiumFrameIfNeeded );
378 "GeometrySystem",
new Ra::Engine::Scene::GeometrySystem, defaultSystemPriority );
386 defaultSystemPriority );
389void BaseApplication::addRadiumMenu() {
391 auto qtWnd =
dynamic_cast<QMainWindow*
>(
m_mainWindow.get() );
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 );
403 &MainWindowInterface::displayHelpDialog );
425 connect(
m_viewer, &Gui::Viewer::needUpdate,
this, &BaseApplication::askForUpdate );
428void BaseApplication::setupScene() {
430 using namespace Engine::Data::DrawPrimitives;
433 Grid( Core::Vector3::Zero(),
434 Core::Vector3::UnitX(),
435 Core::Vector3::UnitZ(),
436 Core::Utils::Color::Grey( 0.6f ) ) );
437 grid->setPickable(
false );
441 Frame( Ra::Core::Transform::Identity(), 0.05f ) );
442 frame->setPickable(
false );
446bool BaseApplication::loadFile( QString path ) {
448 LOG( logINFO ) <<
"Loading file " << filename <<
"...";
452 LOG( logERROR ) <<
"Aborting file loading !";
465void BaseApplication::framesCountForStatsChanged( uint count ) {
466 m_frameCountBeforeUpdate =
count;
527 timerData.numFrame = m_frameCounter;
531 m_timerData.push_back( timerData );
537 if ( m_numFrames > 0 && m_frameCounter >= m_numFrames ) { appNeedsToQuit(); }
539 if ( m_frameCounter % m_frameCountBeforeUpdate == 0 ) {
540 emit( updateFrameStats( m_timerData ) );
546void BaseApplication::appNeedsToQuit() {
547 LOG( logDEBUG ) <<
"About to quit.";
555 plugin->openGlInitialize( m_pluginContext );
561void BaseApplication::setRealFrameRate(
bool on ) {
565void BaseApplication::setRecordFrames(
bool on ) {
566 setContinuousUpdate( on );
567 if ( on ) askForUpdate();
571void BaseApplication::recordFrame() {
574 << m_frameCounter <<
".png";
578BaseApplication::~BaseApplication() {
582 Ra::Engine::RadiumEngine::destroyInstance();
589 const QStringList& loadList,
590 const QStringList& ignoreList ) {
591 QDir pluginsDir( qApp->applicationDirPath() );
592 bool result = pluginsDir.cd( pluginsPath.
c_str() );
595 LOG( logINFO ) <<
" *** Loading Plugins from " << pluginsDir.absolutePath().toStdString()
599 LOG( logDEBUG ) <<
"Cannot open specified plugins directory "
600 << pluginsDir.absolutePath().toStdString();
606 for (
const auto& filename : pluginsDir.entryList( QDir::Files ) ) {
608 std::string ext = Core::Utils::getFileExt( filename.toStdString() );
609#if defined( OS_WINDOWS )
611#elif defined( OS_LINUX )
613#elif defined( OS_MACOS )
616 static_assert(
false,
"System configuration not handled" );
618 if ( ext == sysDllExt ) {
619 std::string basename = Core::Utils::getBaseName( filename.toStdString(),
false );
621 auto stringCmp = [basename](
const QString& str ) {
622 return str.toStdString() == basename;
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)";
630 if (
std::find_if( ignoreList.begin(), ignoreList.end(), stringCmp ) !=
632 LOG( logDEBUG ) <<
"Ignoring " << filename.toStdString() <<
" (on ignore list)";
636 QPluginLoader pluginLoader( pluginsDir.absoluteFilePath( filename ) );
638 pluginLoader.setLoadHints( QLibrary::ResolveAllSymbolsHint );
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.";
651 if ( !metadata.contains(
"isDebug" ) ) {
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 ) );
661 bool isPluginDebug = metadata[
"isDebug"].toString().compare(
"true" ) == 0;
662 if ( expectPluginsDebug == isPluginDebug ) {
666 <<
"Unable to load plugin " << basename <<
" from " << pluginsPath
667 <<
".\n\t\tPlugin was already loaded from " << it->second;
670 LOG( logINFO ) <<
"Found plugin " << filename.toStdString();
672 QObject* plugin = pluginLoader.instance();
674 auto loadedPlugin = qobject_cast<Plugins::RadiumPluginInterface*>( plugin );
675 if ( loadedPlugin ) {
677 loadedPlugin->registerPlugin( m_pluginContext );
680 if ( loadedPlugin->doAddRenderer() ) {
682 loadedPlugin->addRenderers( &tmpR );
683 CORE_ASSERT( !tmpR.
empty(),
684 "This plugin is expected to add a renderer" );
685 for (
const auto& ptr : tmpR ) {
687 ptr->getRendererName() +
"(" + filename.toStdString() +
")";
692 if ( loadedPlugin->doAddFileLoader() ) {
694 loadedPlugin->addFileLoaders( &tmpL );
695 CORE_ASSERT( !tmpL.
empty(),
696 "This plugin is expected to add file loaders" );
697 for (
auto& ptr : tmpL ) {
702 if ( loadedPlugin->doAddROpenGLInitializer() ) {
703 if (
m_viewer->isOpenGlInitialized() ) {
704 LOG( logDEBUG ) <<
"Direct OpenGL initialization for plugin "
705 << filename.toStdString();
708 loadedPlugin->openGlInitialize( m_pluginContext );
713 LOG( logDEBUG ) <<
"Defered OpenGL initialization for plugin "
714 << filename.toStdString();
720 LOG( logERROR ) <<
"Something went wrong while trying to cast plugin "
721 << filename.toStdString();
726 LOG( logERROR ) <<
"Something went wrong while trying to load plugin "
727 << filename.toStdString() <<
" : "
728 << pluginLoader.errorString().toStdString();
733 LOG( logERROR ) <<
"Skipped plugin " << filename.toStdString()
734 <<
" : invalid build mode. Full path: "
735 << pluginsDir.absoluteFilePath( filename ).toStdString();
741 if ( pluginCpt == 0 ) { LOG( logINFO ) <<
"No plugin found or loaded."; }
742 else { LOG( logINFO ) <<
"Loaded " << pluginCpt <<
" plugins."; }
747void BaseApplication::setRecordTimings(
bool on ) {
751void BaseApplication::setRecordGraph(
bool on ) {
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();
762 pluginPaths.append( pluginDir.
c_str() );
763 settings.setValue(
"plugins/paths", pluginPaths );
764 loadPlugins( pluginDir, QStringList(), QStringList() );
769 settings.setValue(
"plugins/paths", QStringList() );
773 QMessageBox notYetImplemented;
774 notYetImplemented.setText(
"Settings editor is not yet available !" );
775 notYetImplemented.exec();
779 std::string helpText {
"<h1>BaseApplication command line parameters</h1><\n>" };
780 helpText +=
"<p>Not yet written.</p><br/><\n>";
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
Core::Utils::Index addRenderObject(Rendering::RenderObject *renderObject)
Add a new render object to the component. This adds the RO to the manager for drawing.
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.
virtual void initializeGl()
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)
void swapBuffers()
Blocks until rendering is finished.
void requestEngineOpenGLInitialization()
virtual void update(const Scalar dt)
Update the internal viewer state to the (application) time dt.
PickingManager * getPickingManager()
Access to the feature picking manager.
QOpenGLContext * getContext() const
Access to the OpenGL context of the Viewer.
const Engine::Rendering::Renderer * getRenderer() const
Read-only access to renderer.
virtual void setupKeyMappingCallbacks()
add observers to keyMappingManager for gizmo, camera and viewer.
void grabFrame(const std::string &filename)
Write the current frame as an image. Supports either BMP or PNG file names.
void processPicking()
Emits signals corresponding to picking requests.
void cleanupGL()
call deinitializeGL if needed, with context activated
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.
optional< std::string > getRadiumPluginsPath()
Get the path of Radium embedded plugins.
void pushDataPath(std::string datapath)
Push a new data path.
hepler function to manage enum as underlying types in VariableSet
This struct holds all timings for one frame of the engine.