You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ovamsCApplication.cpp 15KB

3 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378
  1. #if defined(TARGET_HAS_ThirdPartyOgre3DTerrain)
  2. #include "ovamsCApplication.h"
  3. #include <cmath>
  4. #include <algorithm>
  5. #include "fs/Files.h"
  6. namespace OpenViBE {
  7. namespace SSVEPMindShooter {
  8. CApplication::~CApplication()
  9. {
  10. delete m_StimulusSender;
  11. if (m_painter != nullptr)
  12. {
  13. (*m_logManager) << Kernel::LogLevel_Debug << "- m_painter\n";
  14. delete m_painter;
  15. m_painter = nullptr;
  16. }
  17. for (auto it = m_commands.begin();
  18. it != m_commands.end(); ++it)
  19. {
  20. (*m_logManager) << Kernel::LogLevel_Debug << "- ICommand\n";
  21. if (*it != nullptr)
  22. {
  23. delete *it;
  24. *it = nullptr;
  25. }
  26. }
  27. (*m_logManager) << Kernel::LogLevel_Debug << "- m_root\n";
  28. if (m_root != nullptr)
  29. {
  30. delete m_root;
  31. m_root = nullptr;
  32. }
  33. }
  34. bool CApplication::setup(Kernel::IKernelContext* poKernelContext)
  35. {
  36. m_kernelCtx = poKernelContext;
  37. m_logManager = &(m_kernelCtx->getLogManager());
  38. Kernel::IConfigurationManager* configManager = &(m_kernelCtx->getConfigurationManager());
  39. (*m_logManager) << Kernel::LogLevel_Debug << " * CApplication::setup()\n";
  40. // Plugin config path setup
  41. Ogre::String pluginsPath;
  42. #if defined TARGET_OS_Windows
  43. #if defined TARGET_BUILDTYPE_Debug
  44. pluginsPath = std::string(getenv("OGRE_HOME")) + std::string("/bin/debug/plugins_d.cfg");
  45. #else
  46. pluginsPath = std::string(getenv("OGRE_HOME")) + std::string("/bin/release/plugins.cfg");
  47. #endif
  48. #elif defined TARGET_OS_Linux
  49. pluginsPath = std::string(configManager->expand("${Path_Data}/openvibe-ogre-plugins.cfg").toASCIIString());
  50. #else
  51. #error "No OS defined."
  52. #endif
  53. // Create LogManager to stop Ogre flooding the console and creating random files
  54. (*m_logManager) << Kernel::LogLevel_Debug << "+ Creating Ogre logmanager\n";
  55. Ogre::LogManager* logManager = new Ogre::LogManager();
  56. (*m_logManager) << Kernel::LogLevel_Info << "Log level: " << configManager->expand("${Kernel_ConsoleLogLevel}") << "\n";
  57. (*m_logManager) << Kernel::LogLevel_Info << "Application will output Ogre console log : " << configManager->expandAsBoolean(
  58. "${SSVEP_Ogre_LogToConsole}", false) << "\n";
  59. const CString ogreLog = configManager->expand("${SSVEP_UserDataFolder}/mind-shooter-[$core{date}-$core{time}]-ogre.log");
  60. (*m_logManager) << Kernel::LogLevel_Info << "Ogre log file : " << ogreLog << "\n";
  61. FS::Files::createParentPath(ogreLog);
  62. logManager->createLog(ogreLog.toASCIIString(), true, configManager->expandAsBoolean("${SSVEP_Ogre_LogToConsole}", false), false);
  63. // Root creation
  64. const CString ogreCfg = configManager->expand("${SSVEP_MindShooterScenarioPath}") + "/appconf/mind-shooter-ogre.conf";
  65. (*m_logManager) << Kernel::LogLevel_Debug << "+ m_root = new Ogre::Root(...)\n";
  66. (*m_logManager) << Kernel::LogLevel_Info << "Ogre cfg file : " << ogreCfg << "\n";
  67. m_root = new Ogre::Root(pluginsPath, ogreCfg.toASCIIString(), ogreLog.toASCIIString());
  68. // Resource handling
  69. this->setupResources();
  70. // Configuration from file or dialog window if needed
  71. if (!this->configure())
  72. {
  73. (*m_logManager) << Kernel::LogLevel_Fatal << "The configuration process ended unexpectedly.\n";
  74. return false;
  75. }
  76. // m_window = m_root->initialise(true);
  77. Ogre::NameValuePairList optionList;
  78. m_root->initialise(false);
  79. optionList["vsync"] = "1";
  80. const int width = int(configManager->expandAsInteger("${SSVEP_Ogre_ScreenWidth}", 800));
  81. const int height = int(configManager->expandAsInteger("${SSVEP_Ogre_ScreenHeight}", 600));
  82. const bool fullScreen = configManager->expandAsBoolean("${SSVEP_Ogre_FullScreen}", false);
  83. (*m_logManager) << Kernel::LogLevel_Info << "Width : " << width << " Height : " << height << " Fullscreen : " << fullScreen << "\n";
  84. //m_window = m_root->createRenderWindow("SSVEP Stimulator", 960, 600, false, &l_oOptionList);
  85. m_window = m_root->createRenderWindow("SSVEP Stimulator", width, height, fullScreen, &optionList);
  86. m_windowWidth = m_window->getWidth();
  87. m_windowHeight = m_window->getHeight();
  88. m_sceneManager = m_root->createSceneManager(Ogre::ST_GENERIC);
  89. m_camera = m_sceneManager->createCamera("SSVEPApplicationCamera");
  90. m_cameraNode = m_sceneManager->getRootSceneNode()->createChildSceneNode();
  91. m_cameraNode->attachObject(m_camera);
  92. Ogre::SceneManager* fillSceneManager = m_root->createSceneManager(Ogre::ST_GENERIC);
  93. Ogre::Camera* fillCamera = fillSceneManager->createCamera("SSVEPFillCamera");
  94. m_window->addViewport(fillCamera, 0);
  95. m_viewport = m_window->addViewport(m_camera, 1);
  96. //this->resizeViewport();
  97. // m_viewport->setBackgroundColour(Ogre::ColourValue(0.0, 0.5, 0.5));
  98. m_camera->setAspectRatio(Ogre::Real(m_viewport->getActualWidth()) / Ogre::Real(m_viewport->getActualHeight()));
  99. m_sceneNode = m_sceneManager->getRootSceneNode()->createChildSceneNode("SSVEPApplicationNode");
  100. // initialize the painter object
  101. (*m_logManager) << Kernel::LogLevel_Debug << "+ m_painter = new CBasicPainter(...)\n";
  102. m_painter = new CBasicPainter(this);
  103. (*m_logManager) << Kernel::LogLevel_Debug << " * initializing CEGUI\n";
  104. this->initCEGUI(configManager->expand("${SSVEP_UserDataFolder}/mind-shooter-[$core{date}-$core{time}]-cegui.log"));
  105. (*m_logManager) << Kernel::LogLevel_Debug << " * CEGUI initialized\n";
  106. // create the vector of stimulation frequencies
  107. m_screenRefreshRate = double(configManager->expandAsUInteger("${SSVEP_ScreenRefreshRate}", 60));
  108. (*m_logManager) << Kernel::LogLevel_Info << "Specified screen refresh rate :" << m_screenRefreshRate << "Hz\n";
  109. if (m_screenRefreshRate > 64)
  110. {
  111. (*m_logManager) << Kernel::LogLevel_Error << "Screen refresh rate exceeds the max bit pattern length of 64\n";
  112. return false;
  113. }
  114. size_t i = 1;
  115. CIdentifier frequencyId = configManager->createConfigurationToken("SSVEP_FrequencyId", "1");
  116. m_frequencies.push_back(30);
  117. size_t patternsLoaded = 0;
  118. // TODO: Load patterns
  119. // Load pre-defined stimulation patterns (binary encoded dark/light frames inside a 64bit integer)
  120. while (configManager->lookUpConfigurationTokenIdentifier(configManager->expand("SSVEP_Pattern_${SSVEP_FrequencyId}")) !=
  121. CIdentifier::undefined())
  122. {
  123. size_t stimulationPattern = size_t(configManager->expandAsInteger("${SSVEP_Pattern_${SSVEP_FrequencyId}}"));
  124. (*m_logManager) << Kernel::LogLevel_Info << "Pattern number " << i << " pattern : " << stimulationPattern << "\n";
  125. m_frequencies[i] = stimulationPattern;
  126. configManager->releaseConfigurationToken(frequencyId);
  127. frequencyId = configManager->createConfigurationToken("SSVEP_FrequencyId", std::to_string(++i).c_str());
  128. patternsLoaded++;
  129. }
  130. // Generate patterns from frequencies
  131. if (patternsLoaded == 0)
  132. {
  133. // Load frequencies
  134. while (configManager->lookUpConfigurationTokenIdentifier(configManager->expand("SSVEP_Frequency_${SSVEP_FrequencyId}")) != CIdentifier::undefined())
  135. {
  136. const double currentFrequency = double(configManager->expandAsFloat("${SSVEP_Frequency_${SSVEP_FrequencyId}}"));
  137. const double approximatedFrameCount = m_screenRefreshRate / currentFrequency;
  138. const size_t nRoundedFrame = size_t(floor(approximatedFrameCount + 0.5));
  139. // test if the desired frequency can be reasonably created on the screen
  140. if (fabs(approximatedFrameCount - nRoundedFrame) < 0.003)
  141. {
  142. const size_t framesL = nRoundedFrame / 2 + nRoundedFrame % 2;
  143. const size_t framesD = nRoundedFrame / 2;
  144. // the pattern is procedurally generated and always starts by a 1,
  145. // following by as many 0s as there are Dark frames and finally as many 1s as there are Light frames.
  146. // The frame loop will consume one bit per frame from the right and then reset when the reset marker is hit.
  147. // Start with the reset marker
  148. uint64_t stimulationPattern = 1;
  149. // The dark zeroes
  150. stimulationPattern <<= framesD;
  151. // The light ones
  152. for (size_t j = 0; j < framesL; ++j)
  153. {
  154. stimulationPattern <<= 1;
  155. stimulationPattern += 1;
  156. }
  157. (*m_logManager) << Kernel::LogLevel_Info << "Frequency number " << i << ": " << currentFrequency << "Hz / " <<
  158. floor(approximatedFrameCount + 0.5) << " ( " << framesL << " light, " << framesD << " dark) frames @ " <<
  159. m_screenRefreshRate << "fps\n";
  160. (*m_logManager) << Kernel::LogLevel_Info << "Frequency number " << i << " pattern : " << stimulationPattern << "\n";
  161. std::stringstream binary;
  162. binary << std::bitset<64>(stimulationPattern);
  163. (*m_logManager) << Kernel::LogLevel_Info << "Frequency number " << i << " binary : " << binary.str() << "\n";
  164. m_frequencies.push_back(stimulationPattern);
  165. patternsLoaded++;
  166. }
  167. else { (*m_logManager) << Kernel::LogLevel_Error << "The selected frequency (" << currentFrequency << "Hz) is not supported by your screen.\n"; }
  168. configManager->releaseConfigurationToken(frequencyId);
  169. frequencyId = configManager->createConfigurationToken("SSVEP_FrequencyId", std::to_string(++i).c_str());
  170. }
  171. }
  172. if (!patternsLoaded)
  173. {
  174. (*m_logManager) << Kernel::LogLevel_Error << "No flashing frequencies loaded. Have you run the SSVEP Impact Shooter configuring scenario?\n";
  175. (*m_logManager) << Kernel::LogLevel_Error << "Are you running this app from the correct scenario with the previous stages run properly?\n";
  176. return false;
  177. }
  178. m_StimulusSender->connect("localhost", "15361");
  179. return true;
  180. }
  181. // Set hard-coded parameters, VSync in particular, for all known render systems
  182. void CApplication::setOgreParameters() const
  183. {
  184. const bool fullScreen = m_kernelCtx->getConfigurationManager().expandAsBoolean("${SSVEP_Ogre_FullScreen}", false);
  185. const Ogre::RenderSystemList& list = m_root->getAvailableRenderers();
  186. for (size_t i = 0; i < list.size(); ++i)
  187. {
  188. list[i]->setConfigOption("VSync", "Yes");
  189. list[i]->setConfigOption("Full Screen", (fullScreen ? "Yes" : "No"));
  190. }
  191. }
  192. bool CApplication::configure() const
  193. {
  194. if (! m_root->restoreConfig())
  195. {
  196. setOgreParameters();
  197. if (! m_root->showConfigDialog())
  198. {
  199. (*m_logManager) << Kernel::LogLevel_Error << "No configuration created from the dialog window.\n";
  200. return false;
  201. }
  202. }
  203. // Override 'unsuitable' user choices
  204. (*m_logManager) << Kernel::LogLevel_Info << "Forcing vsync and using fullscreen settings from configuration scenario.\n";
  205. setOgreParameters();
  206. // Save the config again after we have forced the params, otherwise the conf file looks misleading
  207. m_root->saveConfig();
  208. return true;
  209. }
  210. void CApplication::initCEGUI(const char* logFilename)
  211. {
  212. // Instantiate logger before bootstrapping the system, this way we will be able to get the log redirected
  213. if (!CEGUI::Logger::getSingletonPtr()) { new CEGUI::DefaultLogger(); } // singleton; instantiate only, no delete
  214. (*m_logManager) << Kernel::LogLevel_Info << "+ CEGUI log will be in '" << logFilename << "'\n";
  215. FS::Files::createParentPath(logFilename);
  216. CEGUI::Logger::getSingleton().setLogFilename(logFilename, false);
  217. (*m_logManager) << Kernel::LogLevel_Debug << "+ Creating CEGUI Ogre bootstrap\n";
  218. m_guiRenderer = &(CEGUI::OgreRenderer::bootstrapSystem(*m_window));
  219. (*m_logManager) << Kernel::LogLevel_Debug << "+ Creating CEGUI Scheme Manager\n";
  220. #if (CEGUI_VERSION_MAJOR > 0) || (CEGUI_VERSION_MINOR >= 8)
  221. CEGUI::SchemeManager::getSingleton().createFromFile(const_cast<CEGUI::utf8*>(reinterpret_cast<const CEGUI::utf8*>("TaharezLook-ov-0.8.scheme")));
  222. #else
  223. CEGUI::SchemeManager::getSingleton().create((CEGUI::utf8*)"TaharezLook-ov.scheme");
  224. #endif
  225. (*m_logManager) << Kernel::LogLevel_Debug << "+ Creating CEGUI WindowManager\n";
  226. m_guiWindowManager = CEGUI::WindowManager::getSingletonPtr();
  227. m_sheet = m_guiWindowManager->createWindow("DefaultWindow", "RootSheet");
  228. (*m_logManager) << Kernel::LogLevel_Debug << "+ Setting CEGUI StyleSheet\n";
  229. #if (CEGUI_VERSION_MAJOR > 0) || (CEGUI_VERSION_MINOR >= 8)
  230. CEGUI::System::getSingleton().getDefaultGUIContext().setRootWindow(m_sheet);
  231. #else
  232. CEGUI::System::getSingleton().setGUISheet(m_sheet);
  233. #endif
  234. }
  235. void CApplication::resizeViewport() const
  236. {
  237. (*m_logManager) << Kernel::LogLevel_Trace << "Creating a new viewport\n";
  238. const Ogre::uint32 viewportSize = std::min(m_windowWidth, m_windowHeight);
  239. (*m_logManager) << Kernel::LogLevel_Info << "New viewport size : " << viewportSize << "\n";
  240. m_viewport->setDimensions(Ogre::Real(m_windowWidth - viewportSize) / Ogre::Real(m_windowWidth) / 2,
  241. Ogre::Real(m_windowHeight - viewportSize) / Ogre::Real(m_windowHeight) / 2,
  242. Ogre::Real(viewportSize) / Ogre::Real(m_windowWidth),
  243. Ogre::Real(viewportSize) / Ogre::Real(m_windowHeight));
  244. }
  245. void CApplication::setupResources() const
  246. {
  247. Kernel::IConfigurationManager* configManager = &(m_kernelCtx->getConfigurationManager());
  248. Ogre::ResourceGroupManager::getSingleton().addResourceLocation(
  249. configManager->expand("${Path_Data}/applications/${SSVEP_MindShooterFolderName}/resources").toASCIIString(), "FileSystem", "SSVEP");
  250. Ogre::ResourceGroupManager::getSingleton().addResourceLocation(
  251. configManager->expand("${Path_Data}/applications/${SSVEP_MindShooterFolderName}/resources/generic").toASCIIString(), "FileSystem", "SSVEPGeneric");
  252. Ogre::ResourceGroupManager::getSingleton().addResourceLocation(
  253. configManager->expand("${Path_Data}/applications/${SSVEP_MindShooterFolderName}/resources/generic/textures").toASCIIString(), "FileSystem",
  254. "SSVEPGeneric");
  255. Ogre::ResourceGroupManager::getSingleton().addResourceLocation(
  256. configManager->expand("${Path_Data}/applications/${SSVEP_MindShooterFolderName}/resources/trainer").toASCIIString(), "FileSystem", "SSVEPTrainer");
  257. Ogre::ResourceGroupManager::getSingleton().addResourceLocation(
  258. configManager->expand("${Path_Data}/applications/${SSVEP_MindShooterFolderName}/resources/gui").toASCIIString(), "FileSystem", "CEGUI");
  259. Ogre::ResourceGroupManager::getSingleton().addResourceLocation(CString(m_ScenarioDir + "/appconf/materials").toASCIIString(), "FileSystem", "CEGUI");
  260. Ogre::ResourceGroupManager::getSingleton().initialiseResourceGroup("SSVEP");
  261. Ogre::ResourceGroupManager::getSingleton().initialiseResourceGroup("SSVEPTrainer");
  262. Ogre::ResourceGroupManager::getSingleton().initialiseResourceGroup("SSVEPGeneric");
  263. Ogre::ResourceGroupManager::getSingleton().initialiseResourceGroup("CEGUI");
  264. }
  265. bool CApplication::frameStarted(const Ogre::FrameEvent& /*evt*/)
  266. {
  267. m_currentFrame++;
  268. m_currentFrame %= int(m_screenRefreshRate);
  269. for (size_t i = 0; i < m_commands.size(); ++i) { m_commands[i]->processFrame(); }
  270. this->processFrame(m_currentFrame);
  271. return true;
  272. }
  273. void CApplication::go()
  274. {
  275. (*m_logManager) << Kernel::LogLevel_Debug << "Associating application as Ogre frame listener\n";
  276. m_root->addFrameListener(this);
  277. (*m_logManager) << Kernel::LogLevel_Debug << "Entering Ogre rendering loop\n";
  278. m_root->startRendering();
  279. (*m_logManager) << Kernel::LogLevel_Debug << "Ogre rendering loop finished ... exiting\n";
  280. }
  281. void CApplication::stopExperiment()
  282. {
  283. (*m_logManager) << Kernel::LogLevel_Info << "[!] Experiment halting\n";
  284. this->exit();
  285. }
  286. } // namespace SSVEPMindShooter
  287. } // namespace OpenViBE
  288. #endif