#if defined(TARGET_HAS_ThirdPartyOgre3DTerrain) #include "ovamsCApplication.h" #include #include #include "fs/Files.h" namespace OpenViBE { namespace SSVEPMindShooter { CApplication::~CApplication() { delete m_StimulusSender; if (m_painter != nullptr) { (*m_logManager) << Kernel::LogLevel_Debug << "- m_painter\n"; delete m_painter; m_painter = nullptr; } for (auto it = m_commands.begin(); it != m_commands.end(); ++it) { (*m_logManager) << Kernel::LogLevel_Debug << "- ICommand\n"; if (*it != nullptr) { delete *it; *it = nullptr; } } (*m_logManager) << Kernel::LogLevel_Debug << "- m_root\n"; if (m_root != nullptr) { delete m_root; m_root = nullptr; } } bool CApplication::setup(Kernel::IKernelContext* poKernelContext) { m_kernelCtx = poKernelContext; m_logManager = &(m_kernelCtx->getLogManager()); Kernel::IConfigurationManager* configManager = &(m_kernelCtx->getConfigurationManager()); (*m_logManager) << Kernel::LogLevel_Debug << " * CApplication::setup()\n"; // Plugin config path setup Ogre::String pluginsPath; #if defined TARGET_OS_Windows #if defined TARGET_BUILDTYPE_Debug pluginsPath = std::string(getenv("OGRE_HOME")) + std::string("/bin/debug/plugins_d.cfg"); #else pluginsPath = std::string(getenv("OGRE_HOME")) + std::string("/bin/release/plugins.cfg"); #endif #elif defined TARGET_OS_Linux pluginsPath = std::string(configManager->expand("${Path_Data}/openvibe-ogre-plugins.cfg").toASCIIString()); #else #error "No OS defined." #endif // Create LogManager to stop Ogre flooding the console and creating random files (*m_logManager) << Kernel::LogLevel_Debug << "+ Creating Ogre logmanager\n"; Ogre::LogManager* logManager = new Ogre::LogManager(); (*m_logManager) << Kernel::LogLevel_Info << "Log level: " << configManager->expand("${Kernel_ConsoleLogLevel}") << "\n"; (*m_logManager) << Kernel::LogLevel_Info << "Application will output Ogre console log : " << configManager->expandAsBoolean( "${SSVEP_Ogre_LogToConsole}", false) << "\n"; const CString ogreLog = configManager->expand("${SSVEP_UserDataFolder}/mind-shooter-[$core{date}-$core{time}]-ogre.log"); (*m_logManager) << Kernel::LogLevel_Info << "Ogre log file : " << ogreLog << "\n"; FS::Files::createParentPath(ogreLog); logManager->createLog(ogreLog.toASCIIString(), true, configManager->expandAsBoolean("${SSVEP_Ogre_LogToConsole}", false), false); // Root creation const CString ogreCfg = configManager->expand("${SSVEP_MindShooterScenarioPath}") + "/appconf/mind-shooter-ogre.conf"; (*m_logManager) << Kernel::LogLevel_Debug << "+ m_root = new Ogre::Root(...)\n"; (*m_logManager) << Kernel::LogLevel_Info << "Ogre cfg file : " << ogreCfg << "\n"; m_root = new Ogre::Root(pluginsPath, ogreCfg.toASCIIString(), ogreLog.toASCIIString()); // Resource handling this->setupResources(); // Configuration from file or dialog window if needed if (!this->configure()) { (*m_logManager) << Kernel::LogLevel_Fatal << "The configuration process ended unexpectedly.\n"; return false; } // m_window = m_root->initialise(true); Ogre::NameValuePairList optionList; m_root->initialise(false); optionList["vsync"] = "1"; const int width = int(configManager->expandAsInteger("${SSVEP_Ogre_ScreenWidth}", 800)); const int height = int(configManager->expandAsInteger("${SSVEP_Ogre_ScreenHeight}", 600)); const bool fullScreen = configManager->expandAsBoolean("${SSVEP_Ogre_FullScreen}", false); (*m_logManager) << Kernel::LogLevel_Info << "Width : " << width << " Height : " << height << " Fullscreen : " << fullScreen << "\n"; //m_window = m_root->createRenderWindow("SSVEP Stimulator", 960, 600, false, &l_oOptionList); m_window = m_root->createRenderWindow("SSVEP Stimulator", width, height, fullScreen, &optionList); m_windowWidth = m_window->getWidth(); m_windowHeight = m_window->getHeight(); m_sceneManager = m_root->createSceneManager(Ogre::ST_GENERIC); m_camera = m_sceneManager->createCamera("SSVEPApplicationCamera"); m_cameraNode = m_sceneManager->getRootSceneNode()->createChildSceneNode(); m_cameraNode->attachObject(m_camera); Ogre::SceneManager* fillSceneManager = m_root->createSceneManager(Ogre::ST_GENERIC); Ogre::Camera* fillCamera = fillSceneManager->createCamera("SSVEPFillCamera"); m_window->addViewport(fillCamera, 0); m_viewport = m_window->addViewport(m_camera, 1); //this->resizeViewport(); // m_viewport->setBackgroundColour(Ogre::ColourValue(0.0, 0.5, 0.5)); m_camera->setAspectRatio(Ogre::Real(m_viewport->getActualWidth()) / Ogre::Real(m_viewport->getActualHeight())); m_sceneNode = m_sceneManager->getRootSceneNode()->createChildSceneNode("SSVEPApplicationNode"); // initialize the painter object (*m_logManager) << Kernel::LogLevel_Debug << "+ m_painter = new CBasicPainter(...)\n"; m_painter = new CBasicPainter(this); (*m_logManager) << Kernel::LogLevel_Debug << " * initializing CEGUI\n"; this->initCEGUI(configManager->expand("${SSVEP_UserDataFolder}/mind-shooter-[$core{date}-$core{time}]-cegui.log")); (*m_logManager) << Kernel::LogLevel_Debug << " * CEGUI initialized\n"; // create the vector of stimulation frequencies m_screenRefreshRate = double(configManager->expandAsUInteger("${SSVEP_ScreenRefreshRate}", 60)); (*m_logManager) << Kernel::LogLevel_Info << "Specified screen refresh rate :" << m_screenRefreshRate << "Hz\n"; if (m_screenRefreshRate > 64) { (*m_logManager) << Kernel::LogLevel_Error << "Screen refresh rate exceeds the max bit pattern length of 64\n"; return false; } size_t i = 1; CIdentifier frequencyId = configManager->createConfigurationToken("SSVEP_FrequencyId", "1"); m_frequencies.push_back(30); size_t patternsLoaded = 0; // TODO: Load patterns // Load pre-defined stimulation patterns (binary encoded dark/light frames inside a 64bit integer) while (configManager->lookUpConfigurationTokenIdentifier(configManager->expand("SSVEP_Pattern_${SSVEP_FrequencyId}")) != CIdentifier::undefined()) { size_t stimulationPattern = size_t(configManager->expandAsInteger("${SSVEP_Pattern_${SSVEP_FrequencyId}}")); (*m_logManager) << Kernel::LogLevel_Info << "Pattern number " << i << " pattern : " << stimulationPattern << "\n"; m_frequencies[i] = stimulationPattern; configManager->releaseConfigurationToken(frequencyId); frequencyId = configManager->createConfigurationToken("SSVEP_FrequencyId", std::to_string(++i).c_str()); patternsLoaded++; } // Generate patterns from frequencies if (patternsLoaded == 0) { // Load frequencies while (configManager->lookUpConfigurationTokenIdentifier(configManager->expand("SSVEP_Frequency_${SSVEP_FrequencyId}")) != CIdentifier::undefined()) { const double currentFrequency = double(configManager->expandAsFloat("${SSVEP_Frequency_${SSVEP_FrequencyId}}")); const double approximatedFrameCount = m_screenRefreshRate / currentFrequency; const size_t nRoundedFrame = size_t(floor(approximatedFrameCount + 0.5)); // test if the desired frequency can be reasonably created on the screen if (fabs(approximatedFrameCount - nRoundedFrame) < 0.003) { const size_t framesL = nRoundedFrame / 2 + nRoundedFrame % 2; const size_t framesD = nRoundedFrame / 2; // the pattern is procedurally generated and always starts by a 1, // following by as many 0s as there are Dark frames and finally as many 1s as there are Light frames. // The frame loop will consume one bit per frame from the right and then reset when the reset marker is hit. // Start with the reset marker uint64_t stimulationPattern = 1; // The dark zeroes stimulationPattern <<= framesD; // The light ones for (size_t j = 0; j < framesL; ++j) { stimulationPattern <<= 1; stimulationPattern += 1; } (*m_logManager) << Kernel::LogLevel_Info << "Frequency number " << i << ": " << currentFrequency << "Hz / " << floor(approximatedFrameCount + 0.5) << " ( " << framesL << " light, " << framesD << " dark) frames @ " << m_screenRefreshRate << "fps\n"; (*m_logManager) << Kernel::LogLevel_Info << "Frequency number " << i << " pattern : " << stimulationPattern << "\n"; std::stringstream binary; binary << std::bitset<64>(stimulationPattern); (*m_logManager) << Kernel::LogLevel_Info << "Frequency number " << i << " binary : " << binary.str() << "\n"; m_frequencies.push_back(stimulationPattern); patternsLoaded++; } else { (*m_logManager) << Kernel::LogLevel_Error << "The selected frequency (" << currentFrequency << "Hz) is not supported by your screen.\n"; } configManager->releaseConfigurationToken(frequencyId); frequencyId = configManager->createConfigurationToken("SSVEP_FrequencyId", std::to_string(++i).c_str()); } } if (!patternsLoaded) { (*m_logManager) << Kernel::LogLevel_Error << "No flashing frequencies loaded. Have you run the SSVEP Impact Shooter configuring scenario?\n"; (*m_logManager) << Kernel::LogLevel_Error << "Are you running this app from the correct scenario with the previous stages run properly?\n"; return false; } m_StimulusSender->connect("localhost", "15361"); return true; } // Set hard-coded parameters, VSync in particular, for all known render systems void CApplication::setOgreParameters() const { const bool fullScreen = m_kernelCtx->getConfigurationManager().expandAsBoolean("${SSVEP_Ogre_FullScreen}", false); const Ogre::RenderSystemList& list = m_root->getAvailableRenderers(); for (size_t i = 0; i < list.size(); ++i) { list[i]->setConfigOption("VSync", "Yes"); list[i]->setConfigOption("Full Screen", (fullScreen ? "Yes" : "No")); } } bool CApplication::configure() const { if (! m_root->restoreConfig()) { setOgreParameters(); if (! m_root->showConfigDialog()) { (*m_logManager) << Kernel::LogLevel_Error << "No configuration created from the dialog window.\n"; return false; } } // Override 'unsuitable' user choices (*m_logManager) << Kernel::LogLevel_Info << "Forcing vsync and using fullscreen settings from configuration scenario.\n"; setOgreParameters(); // Save the config again after we have forced the params, otherwise the conf file looks misleading m_root->saveConfig(); return true; } void CApplication::initCEGUI(const char* logFilename) { // Instantiate logger before bootstrapping the system, this way we will be able to get the log redirected if (!CEGUI::Logger::getSingletonPtr()) { new CEGUI::DefaultLogger(); } // singleton; instantiate only, no delete (*m_logManager) << Kernel::LogLevel_Info << "+ CEGUI log will be in '" << logFilename << "'\n"; FS::Files::createParentPath(logFilename); CEGUI::Logger::getSingleton().setLogFilename(logFilename, false); (*m_logManager) << Kernel::LogLevel_Debug << "+ Creating CEGUI Ogre bootstrap\n"; m_guiRenderer = &(CEGUI::OgreRenderer::bootstrapSystem(*m_window)); (*m_logManager) << Kernel::LogLevel_Debug << "+ Creating CEGUI Scheme Manager\n"; #if (CEGUI_VERSION_MAJOR > 0) || (CEGUI_VERSION_MINOR >= 8) CEGUI::SchemeManager::getSingleton().createFromFile(const_cast(reinterpret_cast("TaharezLook-ov-0.8.scheme"))); #else CEGUI::SchemeManager::getSingleton().create((CEGUI::utf8*)"TaharezLook-ov.scheme"); #endif (*m_logManager) << Kernel::LogLevel_Debug << "+ Creating CEGUI WindowManager\n"; m_guiWindowManager = CEGUI::WindowManager::getSingletonPtr(); m_sheet = m_guiWindowManager->createWindow("DefaultWindow", "RootSheet"); (*m_logManager) << Kernel::LogLevel_Debug << "+ Setting CEGUI StyleSheet\n"; #if (CEGUI_VERSION_MAJOR > 0) || (CEGUI_VERSION_MINOR >= 8) CEGUI::System::getSingleton().getDefaultGUIContext().setRootWindow(m_sheet); #else CEGUI::System::getSingleton().setGUISheet(m_sheet); #endif } void CApplication::resizeViewport() const { (*m_logManager) << Kernel::LogLevel_Trace << "Creating a new viewport\n"; const Ogre::uint32 viewportSize = std::min(m_windowWidth, m_windowHeight); (*m_logManager) << Kernel::LogLevel_Info << "New viewport size : " << viewportSize << "\n"; m_viewport->setDimensions(Ogre::Real(m_windowWidth - viewportSize) / Ogre::Real(m_windowWidth) / 2, Ogre::Real(m_windowHeight - viewportSize) / Ogre::Real(m_windowHeight) / 2, Ogre::Real(viewportSize) / Ogre::Real(m_windowWidth), Ogre::Real(viewportSize) / Ogre::Real(m_windowHeight)); } void CApplication::setupResources() const { Kernel::IConfigurationManager* configManager = &(m_kernelCtx->getConfigurationManager()); Ogre::ResourceGroupManager::getSingleton().addResourceLocation( configManager->expand("${Path_Data}/applications/${SSVEP_MindShooterFolderName}/resources").toASCIIString(), "FileSystem", "SSVEP"); Ogre::ResourceGroupManager::getSingleton().addResourceLocation( configManager->expand("${Path_Data}/applications/${SSVEP_MindShooterFolderName}/resources/generic").toASCIIString(), "FileSystem", "SSVEPGeneric"); Ogre::ResourceGroupManager::getSingleton().addResourceLocation( configManager->expand("${Path_Data}/applications/${SSVEP_MindShooterFolderName}/resources/generic/textures").toASCIIString(), "FileSystem", "SSVEPGeneric"); Ogre::ResourceGroupManager::getSingleton().addResourceLocation( configManager->expand("${Path_Data}/applications/${SSVEP_MindShooterFolderName}/resources/trainer").toASCIIString(), "FileSystem", "SSVEPTrainer"); Ogre::ResourceGroupManager::getSingleton().addResourceLocation( configManager->expand("${Path_Data}/applications/${SSVEP_MindShooterFolderName}/resources/gui").toASCIIString(), "FileSystem", "CEGUI"); Ogre::ResourceGroupManager::getSingleton().addResourceLocation(CString(m_ScenarioDir + "/appconf/materials").toASCIIString(), "FileSystem", "CEGUI"); Ogre::ResourceGroupManager::getSingleton().initialiseResourceGroup("SSVEP"); Ogre::ResourceGroupManager::getSingleton().initialiseResourceGroup("SSVEPTrainer"); Ogre::ResourceGroupManager::getSingleton().initialiseResourceGroup("SSVEPGeneric"); Ogre::ResourceGroupManager::getSingleton().initialiseResourceGroup("CEGUI"); } bool CApplication::frameStarted(const Ogre::FrameEvent& /*evt*/) { m_currentFrame++; m_currentFrame %= int(m_screenRefreshRate); for (size_t i = 0; i < m_commands.size(); ++i) { m_commands[i]->processFrame(); } this->processFrame(m_currentFrame); return true; } void CApplication::go() { (*m_logManager) << Kernel::LogLevel_Debug << "Associating application as Ogre frame listener\n"; m_root->addFrameListener(this); (*m_logManager) << Kernel::LogLevel_Debug << "Entering Ogre rendering loop\n"; m_root->startRendering(); (*m_logManager) << Kernel::LogLevel_Debug << "Ogre rendering loop finished ... exiting\n"; } void CApplication::stopExperiment() { (*m_logManager) << Kernel::LogLevel_Info << "[!] Experiment halting\n"; this->exit(); } } // namespace SSVEPMindShooter } // namespace OpenViBE #endif