Files
qt5-legatus-legacy/Lts/Plugins/BuildingViewer/Common/BgViewer/viewerCtrl/mainloop.cpp
T
2026-08-13 18:33:10 +02:00

441 lines
15 KiB
C++
Executable File

#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 <dtEntity/core.h>
#include <dtEntity/commandmessages.h>
#include <dtEntity/init.h>
#include <dtEntity/mapcomponent.h>
#include <dtEntity/profile.h>
#include <dtEntity/systemmessages.h>
#include <dtEntity/systeminterface.h>
#include <dtEntity/entitymanager.h>
#include <dtEntityOSG/initosgviewer.h>
#include <dtEntityOSG/layerattachpointcomponent.h>
#include <dtEntityOSG/layercomponent.h>
#include <dtEntityOSG/lightcomponent.h>
#include <dtEntityOSG/cameracomponent.h>
#include <dtEntityOSG/osgsysteminterface.h>
#include <dtEntityOSG/osgwindowinterface.h>
#include <dtEntityOSG/osgdebugdrawinterface.h>
#include <dtEntityOSG/osginputinterface.h>
#include <dtEntityOSG/positionattitudetransformcomponent.h>
#include <dtEntityOSG/staticmeshcomponent.h>
#include <osgDB/Registry>
#include <osgViewer/CompositeViewer>
#include <osgViewer/ViewerEventHandlers>
#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<osgViewer::CompositeViewer> 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<const dtEntity::RequestEntitySelectMessage &>(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<const dtEntity::RequestEntityDeselectMessage &>(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<char *> 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<float>(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];
}
}