#include "mainloop.h" #include "iconcomponent.h" #include "modelsplittercomponent.h" #include "motionmodel.h" #include "orthomotionmodel.h" #include "playercomponent.h" #include "roomdestructioncomponent.h" #include "textcomponent.h" #include "viewerinterfaceimpl.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef BUILD_WITH_GSTREAMER #include "videocomponent.h" #endif namespace VCtrl { //////////////////////////////////////////////////////////////////////////////// class MyReadFileCallback : public osgDB::Registry::ReadFileCallback { public: virtual osgDB::ReaderWriter::ReadResult readNode(const std::string &fileName, const osgDB::ReaderWriter::Options *options) { osgDB::ReaderWriter::ReadResult res = osgDB::Registry::instance()->readNodeImplementation(fileName, options); if (res.notFound() || res.notHandled() || res.error()) { LOG_DEBUG("DatabasePager could not load file, stopped trying: " << fileName); return osgDB::ReaderWriter::ReadResult(nullptr, osgDB::ReaderWriter::ReadResult::FILE_LOADED); } return res; } }; //////////////////////////////////////////////////////////////////////////////// class MainLoop::Impl { public: osg::ref_ptr mViewer; CtrlImpl *mInterface = nullptr; dtEntityOSG::OSGSystemInterface *mSysIf = nullptr; dtEntityOSG::OSGInputInterface *mInputIf = nullptr; dtEntity::EntityManager *mEntityManager; osg::Timer_t mStartFrameTick = 0; dtEntity::Timer_t mSimulationClockTime = 0; bool mProfilingEnabled = false; Impl() : mEntityManager(new dtEntity::EntityManager) { dtEntity::MessageFunctor f(this, &MainLoop::Impl::onFastForward); mEntityManager->RegisterForMessages(dtEntity::SID("FastForwardMessage"), f); dtEntity::MessageFunctor s(this, &MainLoop::Impl::onEntitySelected); mEntityManager->RegisterForMessages(dtEntity::RequestEntitySelectMessage::TYPE, s); dtEntity::MessageFunctor s2(this, &MainLoop::Impl::onEntityDeselected); mEntityManager->RegisterForMessages(dtEntity::RequestEntityDeselectMessage::TYPE, s2); } ~Impl() { delete mEntityManager; delete mSysIf; delete mInputIf; delete mInterface; } // react to fast forward message by void onFastForward(const dtEntity::Message &msg) { // TODO Test! double endtime = msg.GetDouble(dtEntity::SID("EndTime")); while (mInterface->GetSimulationTime() < endtime) { constexpr auto step = 0.5; double t = mInterface->GetSimulationTime() + step; mSimulationClockTime += step / osg::Timer::instance()->getSecondsPerTick(); mInterface->SetSimulationTime(t); mSysIf->SetTimeValues(t, step, step, mSimulationClockTime); mViewer->advance(t); mViewer->eventTraversal(); mSysIf->EmitTickMessagesAndQueuedMessages(); mViewer->updateTraversal(); mSysIf->EmitPostUpdateMessage(); } } // translate RequestEntitySelectMessage to EntityDeselectedMessage // (normally this would be handled by a selection manager, but // selection in this viewer is so simple that it is not necessary // to have real selection management) void onEntitySelected(const dtEntity::Message &m) { const dtEntity::RequestEntitySelectMessage &msg = static_cast(m); dtEntity::EntitySelectedMessage mo; mo.SetAboutEntityId(msg.GetAboutEntityId()); mEntityManager->EmitMessage(mo); } // translate RequestEntityDeselectMessage to EntityDeselectedMessage void onEntityDeselected(const dtEntity::Message &m) { const dtEntity::RequestEntityDeselectMessage &msg = static_cast(m); dtEntity::EntityDeselectedMessage mo; mo.SetAboutEntityId(msg.GetAboutEntityId()); mEntityManager->EmitMessage(mo); } }; //////////////////////////////////////////////////////////////////////////////// MainLoop::MainLoop(int argc, char **argv, QWidget *parentWidget) : mImpl(new Impl) { osgDB::Registry::instance()->setReadFileCallback(new MyReadFileCallback()); assert(!mImpl->mViewer.valid()); //////////////////////////// Setup OSG Viewer ///////////////////////// // osg arguments parser wants to modify argc, argv { int argc_cpy = argc; std::vector argv_cpy; argv_cpy.reserve(argc); for (int i = 0; i < argc; ++i) argv_cpy.push_back(argv[i]); osg::ArgumentParser args(&argc_cpy, argv_cpy.data()); #ifdef _MSC_VER _putenv_s("OSG_RUN_FRAME_SCHEME", "ON_DEMAND"); #else setenv("OSG_RUN_FRAME_SCHEME", "ON_DEMAND", 1); #endif // turn on antialiasing osg::DisplaySettings::instance()->setNumMultiSamples(4); osg::DisplaySettings::instance()->setDepthBuffer(true); mImpl->mViewer = new osgViewer::CompositeViewer(args); // Qt5 is currently crashing and reporting "Cannot make QOpenGLContext current in a different // thread" when the viewer is run multi-threaded, this is regression from Qt4 mImpl->mViewer->setThreadingModel(osgViewer::CompositeViewer::SingleThreaded); // don't exit on escape mImpl->mViewer->setKeyEventSetsDone(0); //mImpl->mViewer->setEndBarrierPosition(osgViewer::ViewerBase::BeforeSwapBuffers); #ifdef LTS_DEBUG dtEntity::LogManager::GetInstance().AddListener(new dtEntity::ConsoleLogHandler()); #endif } //////////////////////// setup dtEntity -> OSG interfaces //////////////////////// { mImpl->mSysIf = new dtEntityOSG::OSGSystemInterface(mImpl->mEntityManager->GetMessagePump(), argc, (const char **)argv); mImpl->mInputIf = new dtEntityOSG::OSGInputInterface(mImpl->mEntityManager->GetMessagePump()); // give application system access to viewer mImpl->mSysIf->SetViewer(mImpl->mViewer); dtEntity::SetSystemInterface(mImpl->mSysIf); dtEntity::SetInputInterface(mImpl->mInputIf); dtEntity::SetWindowInterface(new dtEntityOSG::OSGWindowInterface(*mImpl->mEntityManager)); dtEntity::SetupDataPaths(argc, argv, false); } dtEntity::EntityManager &em = *mImpl->mEntityManager; //////////////////////////// Add entity systems ///////////////////////// { // this is a required component system, so add it immediately em.AddEntitySystem(*new dtEntity::MapSystem(em)); em.AddEntitySystem(*new dtEntityOSG::LayerSystem(em)); em.AddEntitySystem(*new dtEntityOSG::CameraSystem(em)); em.AddEntitySystem(*new dtEntityOSG::PositionAttitudeTransformSystem(em)); em.AddEntitySystem(*new dtEntityOSG::StaticMeshSystem(em)); em.AddEntitySystem(*new RoomDestructionSystem(em)); em.AddEntitySystem(*new MotionModelSystem(em)); em.AddEntitySystem(*new ModelSplitterSystem(em)); em.AddEntitySystem(*new OrthoMotionModelSystem(em)); em.AddEntitySystem(*new dtEntityOSG::LightSystem(em)); em.AddEntitySystem(*new PlayerSystem(em)); em.AddEntitySystem(*new IconSystem(em)); em.AddEntitySystem(*new TextSystem(em)); #ifdef BUILD_WITH_GSTREAMER em.AddEntitySystem(*new VideoSystem(em)); #endif dtEntityOSG::LayerAttachPointSystem *layerattach = new dtEntityOSG::LayerAttachPointSystem(em); em.AddEntitySystem(*layerattach); layerattach->CreateSceneGraphRootEntity(new osg::Group()); mImpl->mInterface = new CtrlImpl(em, parentWidget); // need layer system for debug draw interface, so set it up now dtEntity::SetDebugDrawInterface(new dtEntityOSG::OSGDebugDrawInterface(em)); } //////////////////////////// Init time tracking ///////////////////////// { mImpl->mStartFrameTick = osg::Timer::instance()->tick(); mImpl->mInterface->SetSimulationTime(0); time_t t; time(&t); mImpl->mSimulationClockTime = t / osg::Timer::instance()->getSecondsPerTick(); } for (int i = 1; i < argc; ++i) { std::string curArgv = argv[i]; if (!curArgv.empty() && curArgv == "--openOverview") mImpl->mInterface->OpenOverview(); } //////////////////////////// Start simulation ///////////////////////// { dtEntity::StartSystemMessage msg; em.EnqueueMessage(msg); } } //////////////////////////////////////////////////////////////////////////////// MainLoop::~MainLoop() { delete mImpl; } //////////////////////////////////////////////////////////////////////////////// dtEntity::EntityManager *MainLoop::getEntityManager() const { return mImpl->mEntityManager; } //////////////////////////////////////////////////////////////////////////////// ViewerInterface *MainLoop::getViewerInterface() const { return mImpl->mInterface; } //////////////////////////////////////////////////////////////////////////////// bool MainLoop::getProfilingEnabled() const { return mImpl->mProfilingEnabled; } //////////////////////////////////////////////////////////////////////////////// void MainLoop::setProfilingEnabled(bool v) { mImpl->mProfilingEnabled = v; } //////////////////////////////////////////////////////////////////////////////// void MainLoop::tick() { dtEntityOSG::OSGSystemInterface *iface = dtEntityOSG::GetOSGSystemInterface(); if (!mImpl->mInterface->allViewWindowsExposed()) return; if (getViewerInterface()->GetNonRealtimeMode()) { float deltaTime = 1.0f / mImpl->mInterface->GetFrameRate(); float deltaSimTime = deltaTime; mImpl->mSimulationClockTime += deltaSimTime / osg::Timer::instance()->getSecondsPerTick(); mImpl->mInterface->SetSimulationTime(mImpl->mInterface->GetSimulationTime() + deltaSimTime); iface->SetTimeValues(mImpl->mInterface->GetSimulationTime(), deltaSimTime, deltaTime, mImpl->mSimulationClockTime); } else { float deltaTime = 0; float deltaSimTime = 0; dtEntity::Timer_t tick = osg::Timer::instance()->tick(); deltaTime = static_cast(osg::Timer::instance()->delta_s(mImpl->mStartFrameTick, tick)); deltaSimTime = deltaTime * mImpl->mInterface->GetTimeScale(); mImpl->mStartFrameTick = tick; mImpl->mSimulationClockTime += deltaSimTime / osg::Timer::instance()->getSecondsPerTick(); mImpl->mInterface->SetSimulationTime(mImpl->mInterface->GetSimulationTime() + deltaSimTime); iface->SetTimeValues(mImpl->mInterface->GetSimulationTime(), deltaSimTime, deltaTime, mImpl->mSimulationClockTime); } if (mImpl->mProfilingEnabled) { static dtEntity::StringId frameId = dtEntity::SID("Frame"); static dtEntity::StringId frameAdvanceId = dtEntity::SID("Frame_Advance"); static dtEntity::StringId frameEvTrId = dtEntity::SID("Frame_EventTraversal"); static dtEntity::StringId frameUpTrId = dtEntity::SID("Frame_UpdateTraversal"); static dtEntity::StringId frameRenderTrId = dtEntity::SID("Frame_RenderingTraversals"); static unsigned int framecount = 0; CProfileManager::Increment_Frame_Counter(); CProfileManager::Start_Profile(frameId); CProfileManager::Start_Profile(frameAdvanceId); mImpl->mViewer->advance(); CProfileManager::Stop_Profile(); CProfileManager::Start_Profile(frameEvTrId); mImpl->mViewer->eventTraversal(); iface->EmitTickMessagesAndQueuedMessages(); CProfileManager::Stop_Profile(); if (mImpl->mViewer->getNumViews() > 0 && mImpl->mViewer->checkNeedToDoFrame()) { CProfileManager::Start_Profile(frameUpTrId); mImpl->mViewer->updateTraversal(); iface->EmitPostUpdateMessage(); CProfileManager::Stop_Profile(); CProfileManager::Start_Profile(frameRenderTrId); mImpl->mViewer->renderingTraversals(); CProfileManager::Stop_Profile(); } else iface->EmitPostUpdateMessage(); CProfileManager::Stop_Profile(); // frameId if (++framecount > 99) { CProfileManager::dumpAll(); fflush(stdout); framecount = 0; CProfileManager::Reset(); } } else { /** NASTY BUG WORKAROUND: Somehow, changes made to the frame are not immediately shown when a new frame is requested. This only happens when more than one view is opened. The workaround is to render again two more times even if no frame redraw was requested. */ static int renderagaincnt = 0; mImpl->mViewer->advance(DBL_MAX); mImpl->mViewer->eventTraversal(); iface->EmitTickMessagesAndQueuedMessages(); bool needFrame = mImpl->mViewer->checkNeedToDoFrame(); if (mImpl->mViewer->getNumViews() > 0 && (renderagaincnt != 0 || needFrame)) { if (mImpl->mViewer->getNumViews() == 1) renderagaincnt = 0; else if (needFrame) renderagaincnt = 2; else --renderagaincnt; mImpl->mViewer->updateTraversal(); iface->EmitPostUpdateMessage(); mImpl->mViewer->renderingTraversals(); } else iface->EmitPostUpdateMessage(); } if (!getViewerInterface()->GetNonRealtimeMode() && mImpl->mViewer->getRunMaxFrameRate() > 0.0) { if (mImpl->mProfilingEnabled) { static dtEntity::StringId sleepId = dtEntity::SID("Frame_Sleep"); CProfileManager::Start_Profile(sleepId); } osg::Timer_t endFrameTick = osg::Timer::instance()->tick(); double frameTime = osg::Timer::instance()->delta_s(mImpl->mStartFrameTick, endFrameTick); double minFrameTime = 1.0 / mImpl->mViewer->getRunMaxFrameRate(); if (frameTime < minFrameTime) OpenThreads::Thread::microSleep(1000000.0 * (minFrameTime - frameTime)); if (mImpl->mProfilingEnabled) CProfileManager::Stop_Profile(); } } //////////////////////////////////////////////////////////////////////////////// void MainLoop::GetCurrentPosition(Vec3d &position, Vec3d &lookAt) { position = Vec3d(); lookAt = Vec3d(); dtEntity::MapSystem *ms; mImpl->mEntityManager->GetES(ms); if (!ms) return; dtEntity::EntityId eid = ms->GetEntityIdByUniqueId("cam_overview"); dtEntityOSG::CameraComponent *camcomp; if (!mImpl->mEntityManager->GetComponent(eid, camcomp)) { LOG_ERROR("GetCurrentPosition: Error accessing camera"); return; } osg::Vec3d p = camcomp->GetPosition(); osg::Vec3d la = camcomp->GetEyeDirection() + camcomp->GetPosition(); position.x = p[0]; position.y = p[1]; position.z = p[2]; lookAt.x = la[0]; lookAt.y = la[1]; lookAt.z = la[2]; } }