#include "viewerinterfaceimpl.h" #include "viewermainwindow.h" #include "buildingmanager.h" #include "iconcomponent.h" #include "messages.h" #include "modelsplittercomponent.h" #include "playercomponent.h" #include "roomdestructioncomponent.h" #include "textcomponent.h" #include "viewwidget.h" #include "orthomotionmodel.h" #include "visitors.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "buildingmanager.h" #include "viewwidget.h" #include #include #include #include #ifdef BUILD_WITH_GSTREAMER #include "videocomponent.h" #endif #include #include #include #include namespace VCtrl { //////////////////////////////////////////////////////////////////////////////// class LogHandler final : public dtEntity::LogListener { public: explicit LogHandler(ViewerCallback *cb) : mCallback(cb) {} void LogMessage(dtEntity::LogLevel::e level, const std::string &filename, const std::string &methodname, int linenumber, const std::string &msg) override { ViewerCallback::Severity severity = ViewerCallback::Severity::Fatal; switch (level) { case dtEntity::LogLevel::e::LVL_ALWAYS: severity = ViewerCallback::Severity::Always; break; case dtEntity::LogLevel::e::LVL_DEBUG: severity = ViewerCallback::Severity::Debug; break; case dtEntity::LogLevel::e::LVL_INFO: severity = ViewerCallback::Severity::Info; break; case dtEntity::LogLevel::e::LVL_WARNING: severity = ViewerCallback::Severity::Warning; break; case dtEntity::LogLevel::e::LVL_ERROR: severity = ViewerCallback::Severity::Error; break; } mCallback->LogMessage(severity, filename, methodname, linenumber, msg); } private: ViewerCallback *mCallback; }; //////////////////////////////////////////////////////////////////////////////// ViewWidget *addViewWidget(osg::Camera *camera, unsigned int &contextid, dtEntity::EntityId bid = 0, const std::string &strFeatureID = std::string(), bool exactPos = false) { osgViewer::CompositeViewer *cv = static_cast(dtEntityOSG::GetOSGSystemInterface()->GetViewer()); cv->stopThreading(); cv->setDone(false); // ------------------------------------------------- osgViewer::View *view = new osgViewer::View; view->setCamera(camera); cv->addView(view); view->requestContinuousUpdate(false); osgQt::GraphicsWindowQt *gw = dynamic_cast(camera->getGraphicsContext()); contextid = gw->getState()->getContextID(); if (!gw->isRealized()) gw->realize(); ViewWidget *wv = new ViewWidget(contextid, bid, strFeatureID, exactPos); wv->setView(gw->getGLWidget()); // ------------------------------------------------- cv->startThreading(); return wv; } //////////////////////////////////////////////////////////////////////////////// osg::Camera *createCamera(int screennum, bool fullscreen, int x, int y, int w, int h, const std::string &name, bool windowDecoration) { const auto screens = QGuiApplication::screens(); const QRect rect = screens.size() > screennum ? screens.at(screennum)->geometry() : QRect(); if (fullscreen) { x = rect.x(); y = rect.y(); w = rect.width(); h = rect.height(); } else { x += rect.x(); y += rect.y(); } osg::DisplaySettings *ds = osg::DisplaySettings::instance().get(); osg::ref_ptr traits = new osg::GraphicsContext::Traits; traits->windowName = name; traits->windowDecoration = windowDecoration; traits->x = x; traits->y = y; traits->width = w; traits->height = h; traits->doubleBuffer = true; traits->alpha = ds->getMinimumNumAlphaBits(); traits->stencil = ds->getMinimumNumStencilBits(); traits->sampleBuffers = ds->getMultiSamples(); traits->samples = ds->getNumMultiSamples(); traits->screenNum = screennum; traits->vsync = false; osg::ref_ptr camera = new osg::Camera; camera->setGraphicsContext(new osgQt::GraphicsWindowQt(traits.get())); camera->setViewport(new osg::Viewport(0, 0, traits->width, traits->height)); camera->setProjectionMatrixAsPerspective( 30.0f, static_cast(traits->width) / static_cast(traits->height), 1.0f, 10000.0f); return camera.release(); } // Wrapper which provides dummy parameters osg::Camera *createCamera(const std::string &name) { return createCamera(0, false, 0, 0, 10, 10, name, false); } //////////////////////////////////////////////////////////////////////////////// /// \brief CtrlImpl::CtrlImpl /// \param em CtrlImpl::CtrlImpl(dtEntity::EntityManager &em, QWidget *parentWidget) : mEntityManager(&em) , mViewerCallback(nullptr) , mMapSystem(nullptr) , mPlayerSystem(nullptr) , mBuildingManager(new BuildingManager(em)) , mTimeScale(1) , mSimTime(0) , mPaused(false) , mNonRealtimeMode(false) , mFrameRate(30) { if (!mEntityManager->GetES(mMapSystem) || !mEntityManager->GetES(mPlayerSystem)) { assert(false); } // register for messages with callbacks dtEntity::MessageFunctor f1(this, &CtrlImpl::OnTransformSend); em.RegisterForMessages(TransformSendMessage::TYPE, f1); dtEntity::MessageFunctor f2(this, &CtrlImpl::OnNavMeshGroupChanged); em.RegisterForMessages(NavMeshGroupChangedMessage::TYPE, f2); dtEntity::MessageFunctor f3(this, &CtrlImpl::OnPlayerPicked); em.RegisterForMessages(PlayerPickedMessage::TYPE, f3); dtEntity::MessageFunctor f4(this, &CtrlImpl::OnHideSelection); em.RegisterForMessages(HideSelectionMessage::TYPE, f4); dtEntity::MessageFunctor f5(this, &CtrlImpl::OnShoot); em.RegisterForMessages(ShootMessage::TYPE, f5); dtEntity::MessageFunctor f6(this, &CtrlImpl::OnShowHidePlayer); em.RegisterForMessages(ShowHidePlayerMessage::TYPE, f6); dtEntity::MessageFunctor f7(this, &CtrlImpl::OnDisplayNameClicked); em.RegisterForMessages(DisplayNameClickedMessage::TYPE, f7); m_pMainWindow = new CtrlMainWindow(parentWidget); mBuildingManager->setCallbackCamPosChanged( [this](unsigned int contextId, const BuildingViewer::CameraViewPosition &viewPos) { if (mViewerCallback) { auto vw = m_openedViewWidgets.getViewWidget(contextId); if (vw) mViewerCallback->ViewZoomChanged(vw->featureID(), viewPos.m_top, viewPos.m_left, viewPos.m_bottom, viewPos.m_right); } }); } CtrlImpl::~CtrlImpl() { RemoveViewerCallback(); delete m_pMainWindow; } QWidget* CtrlImpl::getMainWindow() { return m_pMainWindow; } void CtrlImpl::openViews(const std::vector &bldIds, bool exactPos) { if (bldIds.size() == 1 && bldIds.front() == "all") { openAll(exactPos); return; } if (bldIds.size()) open(bldIds, exactPos); } bool CtrlImpl::isViewOpen(const std::string &strBldId) const { return m_openedViewWidgets.exist(strBldId); // strBldId == strFeatureID } void CtrlImpl::closeViews(const std::vector &vecFeatureIDs) { if (vecFeatureIDs.size() == 1 && vecFeatureIDs.front() == "all") { closeAllViews(); return; } m_pMainWindow->clearGridLayout(); // Close requested widgets (not necessarily all widgets) for (const auto &strFeatureID : vecFeatureIDs) { ViewWidget *pWidget = m_openedViewWidgets.getViewWidget(strFeatureID); if (pWidget) { // User may even remotely close the currently maximized widget if (pWidget->isMaximized()) m_isInFullscreenMode = false; CloseView(pWidget); m_openedViewWidgets.remove(pWidget->featureID()); } } // Is there still a maximized widget? If yes, show it. Otherwise show all widgets if (m_isInFullscreenMode) { for (const auto &pWidget : m_openedViewWidgets.getViewWidgets()) { if (pWidget->isMaximized()) { m_pMainWindow->openView(pWidget); break; } } } else m_pMainWindow->openViews(m_openedViewWidgets.getViewWidgets()); } void CtrlImpl::closeAllViews() { if(!m_openedViewWidgets.size() || !m_pMainWindow) return; m_pMainWindow->clearGridLayout(); // copy, CloseView modifes m_openedViewWidgets const auto viewWidgets = m_openedViewWidgets.getViewWidgets(); for (auto pWidget : viewWidgets) CloseView(pWidget); m_openedViewWidgets.clear(); } void CtrlImpl::setTransparency(double percent) { m_desiredTransparencyValue = percent; std::vector m_vecShownViews = m_openedViewWidgets.getViewWidgets(); for (const auto &e : m_vecShownViews) SetWallTransparency(e->contextID(), m_desiredTransparencyValue); } void CtrlImpl::setViewMode(ViewMode m) { m_desiredViewMode = m; std::vector m_vecShownViews = m_openedViewWidgets.getViewWidgets(); for (const auto &e : m_vecShownViews) ChangeViewMode(e->contextID(), m_desiredViewMode); } void CtrlImpl::setDisplayMode(PlayerDisplayMode m) { m_desiredPlayerDisplayMode = m; std::vector m_vecShownViews = m_openedViewWidgets.getViewWidgets(); for (const auto &e : m_vecShownViews) SetPlayerDisplayMode(e->contextID(), m_desiredPlayerDisplayMode); } std::vector CtrlImpl::getBuildingState() const { std::vector ret; std::vector vecEntityIDs; mMapSystem->GetEntitiesInMap("buildings", vecEntityIDs); ret.reserve(vecEntityIDs.size()); VCtrl::ViewerInterface::BuildingState bs; for (const auto id : vecEntityIDs) { bs.buildingId = mMapSystem->GetComponent(id)->GetUniqueId(); bs.description = mMapSystem->GetComponent(id)->GetDescription(); bs.entityId = id; bs.isShown = m_openedViewWidgets.exist(bs.buildingId); ret.push_back(bs); } return ret; } void CtrlImpl::restoreViewWidgetStateDeferred(ViewWidget *pViewWidget) { disconnect(pViewWidget, &ViewWidget::signal_ViewIsShown, this, &CtrlImpl::restoreViewWidgetStateDeferred); auto it = m_deferredViewStates.find(pViewWidget->featureID()); if (it == m_deferredViewStates.end()) { LOG_DEBUG("restoreViewWidgetStateDeferred... default values"); // workaround - sometimes the osg::Camera position gets lost during // creation and show - therefore refresh the values here mBuildingManager->setDefaultCameraPosition(pViewWidget->contextID()); return; } const BuildingViewer::ViewWidgetState &state = it->second; LOG_DEBUG("restore view state for " << QString::fromStdString(pViewWidget->featureID()) << ": " << "top=" << state.m_posCamera.m_top << "/ Left=" << state.m_posCamera.m_left << " - " << " bottom=" << state.m_posCamera.m_bottom << "/ right=" << state.m_posCamera.m_right << ", save size: " << state.m_windowWidth << "/" << state.m_windowHeight << ", cur size: " << pViewWidget->accessView()->width() << "/" << pViewWidget->accessView()->height() << ", is visible: " << pViewWidget->accessView()->isVisible() ); // top and bottom do not change when the window size changes / when zooming is performed // only left and right, they both get the half of the new available space // the ratio is calculated from the height and width... don't know exactly why // const double ratio = (pViewWidget->accessView()->height() / double(state.m_windowHeight)) * // (pViewWidget->accessView()->width() / double(state.m_windowWidth)); // const double oldWidth = state.m_posCamera.m_right - state.m_posCamera.m_left; // const double addWidth = oldWidth - (oldWidth / ratio); // strange ctor - left,right,bottom,top // const BuildingViewer::CameraViewPosition adjustedPos(state.m_posCamera.m_left - addWidth / 2, // state.m_posCamera.m_right + addWidth / 2, // state.m_posCamera.m_bottom, // state.m_posCamera.m_top); // new ratio (oldW/oldH) / (/)newW/newH) const double ratio = ((1.0*pViewWidget->accessView()->width())/pViewWidget->accessView()->height()) / ((1.0*state.m_windowWidth)/state.m_windowHeight); const double oldWidth = state.m_posCamera.m_right - state.m_posCamera.m_left; const double addWidth = oldWidth - (oldWidth / ratio); const BuildingViewer::CameraViewPosition adjustedPos(state.m_posCamera.m_left - addWidth / 2, state.m_posCamera.m_right + addWidth / 2, state.m_posCamera.m_bottom, state.m_posCamera.m_top); const double Oldratio = (pViewWidget->accessView()->height() / double(state.m_windowHeight)) * (pViewWidget->accessView()->width() / double(state.m_windowWidth)); // if (ratio < 0.55 || ratio > 3) LOG_DEBUG("restore view calculated for " << QString::fromStdString(pViewWidget->featureID()) << ": top=" << adjustedPos.m_top << "/ left=" << adjustedPos.m_left << " - bottom=" << adjustedPos.m_bottom << "/ right:" << adjustedPos.m_right << ", " << "new ratio= " <setDefaultCameraPosition(pViewWidget->contextID()); m_deferredViewStates.erase(it); return; } mBuildingManager->setCameraPosition(pViewWidget->contextID(), adjustedPos); m_deferredViewStates.erase(it); } void CtrlImpl::restoreViewWidgetStates(const std::vector &vecStates) { bool allAlreadyOpened = vecStates.size() == m_openedViewWidgets.size(); for (size_t i = 0; i < vecStates.size() && i < m_openedViewWidgets.getViewWidgets().size() && allAlreadyOpened; ++i) { if (vecStates.at(i).m_featureID != m_openedViewWidgets.getViewWidgets().at(i)->featureID()) allAlreadyOpened = false; } if (allAlreadyOpened) { // return; LOG_DEBUG("ALL OPEN"); } //closeAllViews(); /* * Open all ViewWidgets of this state first. * This step is important to restore the right aspect ratio of the building since the * aspect ratio depends on the ViewWidget dimensions. The ViewWidget dimensions itself depend * on the amount of opened ViewWidgets since the layout is adjusted accordingly. */ bool useExact = vecStates.empty() ? true : vecStates.front().m_usesExactPos; std::vector vecFeatureIDs; vecFeatureIDs.reserve(vecStates.size()); for (const auto &s : vecStates) vecFeatureIDs.push_back(s.m_featureID); if(!allAlreadyOpened) { closeAllViews(); LOG_DEBUG("Close all and Reopen everything!"); open(vecFeatureIDs, useExact); } // Set state properties for (const auto &savedState : vecStates) { ViewWidget *pViewWidget = m_openedViewWidgets.getViewWidget(savedState.m_featureID); if (!pViewWidget) { Q_ASSERT(false); continue; } LockViewRotationToMapRotation(pViewWidget->contextID(), savedState.m_lockActive); SetPlayerDisplayMode(pViewWidget->contextID(), savedState.m_dotsActive ? PlayerDisplayMode::Dots : PlayerDisplayMode::Icons); SetWallTransparency(pViewWidget->contextID(), savedState.m_usedTransparencyValue); if (savedState.m_isMaximized) onViewWidget_MaximizeRequest(pViewWidget); ChangeViewMode(pViewWidget->contextID(), savedState.m_usesOrtho ? ViewMode::ORTHOGRAPHIC : ViewMode::ISOMETRIC); ChangeNameMode(pViewWidget->contextID(), savedState.m_namesActive); m_deferredViewStates.insert(std::make_pair(savedState.m_featureID, savedState)); if (pViewWidget->accessView()->isVisible()) restoreViewWidgetStateDeferred(pViewWidget); else connect(pViewWidget, &ViewWidget::signal_ViewIsShown, this, &CtrlImpl::restoreViewWidgetStateDeferred); } } static QString strAssetImagePath_IndoorCamera() { return QDir::tempPath() + QStringLiteral("/bgviewer_asset_indoorcamera.png"); } static QString strAssetImagePath_IndoorCameraWithSpeaker() { return QDir::tempPath() + QStringLiteral("/bgviewer_asset_indoorcamera_with_speaker.png"); } static QString strAssetImagePath_AnnouncementUnit() { return QDir::tempPath() + QStringLiteral("/bgviewer_asset_announcement_unit.png"); } static QString strAssetImagePath_EffectGenerationUnit() { return QDir::tempPath() + QStringLiteral("/bgviewer_asset_effect_generation_unit.png"); } /* * OpenSceneGraph requires *.png files with absolute paths. Hence, we * create png files from the required *.svg images located within * our qrc file in /tmp/. */ void CtrlImpl::convertAssetsToPngFiles() { static bool isUpToDate{false}; if (isUpToDate) return; QImage img(QStringLiteral("icon:bgv_camera.svg")); img.save(strAssetImagePath_IndoorCamera()); img.load(QStringLiteral("icon:bgv_camera_with_speaker.svg")); img.save(strAssetImagePath_IndoorCameraWithSpeaker()); img.load(QStringLiteral("icon:bgv_announcement.svg")); img.save(strAssetImagePath_AnnouncementUnit()); img.load(QStringLiteral("icon:bgv_effect_generation_unit_white_bg.svg")); img.save(strAssetImagePath_EffectGenerationUnit()); isUpToDate = true; } void CtrlImpl::setMountingPointAsset(const MountingPointDescription &asset) { QString strAssetImagePath; switch (asset.assetType) { case BuildingViewer::AssetTypes::IndoorCamera: strAssetImagePath = strAssetImagePath_IndoorCamera(); break; case BuildingViewer::AssetTypes::IndoorCameraWithSpeaker: strAssetImagePath = strAssetImagePath_IndoorCameraWithSpeaker(); break; case BuildingViewer::AssetTypes::AnnouncementUnit: strAssetImagePath = strAssetImagePath_AnnouncementUnit(); break; case BuildingViewer::AssetTypes::EffectGenerationUnit: strAssetImagePath = strAssetImagePath_EffectGenerationUnit(); break; } if (!strAssetImagePath.size()) return; convertAssetsToPngFiles(); // if (!QFile::exists(strAssetImagePath)) // convertAssetsToPngFiles(); ShowDeviceIcon(asset.mountingPointId, asset.buildingId, asset.position, strAssetImagePath.toStdString()); } QJsonValue CtrlImpl::getViewState() const { QJsonArray arr; for (const auto &pView : m_openedViewWidgets.getViewWidgets()) { BuildingViewer::ViewWidgetState state(pView); bool success = mBuildingManager->getCameraPosition(pView->contextID(), state.m_posCamera); LOG_DEBUG("save view state for " << pView->featureID() << ": " << state.m_posCamera.m_top << "/" << state.m_posCamera.m_left << " - " << state.m_posCamera.m_bottom << "/" << state.m_posCamera.m_right << ", save size: " << state.m_windowWidth << "/" << state.m_windowHeight); assert(success); if (success) arr += state.toJson(); } return arr; } void CtrlImpl::setViewState(const QJsonValue &val) { const QJsonArray arr = val.toArray(); std::vector vecStates; vecStates.reserve(arr.size()); for (const auto &ele : arr) vecStates.push_back(BuildingViewer::ViewWidgetState(ele.toObject())); restoreViewWidgetStates(vecStates); } //////////////////////////////////////////////////////////////////////////////// void CtrlImpl::highlightView(const std::string &buildingId) { // If applicable, remove last selection drawing for (auto pWidget : m_openedViewWidgets.getViewWidgets()) pWidget->setSelected(false); ViewWidget *pWidget = m_openedViewWidgets.getViewWidget(buildingId); if (pWidget) pWidget->setSelected(true); } void CtrlImpl::setBuildingDisplayName(const std::string &buildingId, const std::string &displayName) { dtEntity::EntityId bid = mMapSystem->GetEntityIdByUniqueId(buildingId); if (bid) { if (m_openedViewWidgets.getViewWidget(buildingId)) m_openedViewWidgets.getViewWidget(buildingId)->setText(QString::fromStdString(displayName)); mMapSystem->GetComponent(bid)->SetDescription(displayName); } } //////////////////////////////////////////////////////////////////////////////// ViewWidget *CtrlImpl::GetViewWidget(unsigned int contextid) { auto it = mViewWidgets.find(contextid); return it == mViewWidgets.end() ? nullptr : it->second; } //////////////////////////////////////////////////////////////////////////////// // when player in world space changed its position through teleport void CtrlImpl::OnTransformSend(const dtEntity::Message &m) { const TransformSendMessage &msg = static_cast(m); // forward to simulator callback if (mViewerCallback != nullptr) { dtEntity::MapComponent *mc = mMapSystem->GetComponent(msg.GetAboutEntity()); assert(mc); if (mc) { const osg::Vec3d p = msg.GetPosition(); const osg::Quat a = msg.GetAttitude(); mViewerCallback->TransformChanged(mc->GetUniqueId(), Vec3d(p[0], p[1], p[2]), Quat(a[0], a[1], a[2], a[3])); } } // now update icons of player in the various building views PlayerComponent *pc; mEntityManager->GetComponent(msg.GetAboutEntity(), pc); assert(pc); if (!pc) return; std::string cellgrp = dtEntity::GetStringFromSID(pc->GetCurrentCellGroup()); mBuildingManager->updatePlayerPosition(cellgrp, msg.GetAboutEntity(), msg.GetPosition(), msg.GetRoomNumber()); } //////////////////////////////////////////////////////////////////////////////// // when player enters or exits a building void CtrlImpl::OnNavMeshGroupChanged(const dtEntity::Message &m) { const NavMeshGroupChangedMessage &msg = static_cast(m); // forward to simulator callback if (mViewerCallback != nullptr) { dtEntity::MapComponent *mc = mMapSystem->GetComponent(msg.GetAboutEntityId()); assert(mc); if (mc) mViewerCallback->NavMeshGroupChanged(mc->GetUniqueId(), msg.GetGroupName()); } std::string gn = msg.GetGroupName(); std::string pgn = msg.GetPreviousGroupName(); PlayerComponent *pc; mEntityManager->GetComponent(msg.GetAboutEntityId(), pc); assert(pc); if (!pc) return; std::string roomname = dtEntity::GetStringFromSID(pc->GetCurrentRoom()); // remove from previous building if (!pgn.empty()) mBuildingManager->removePlayerFromBuilding(pgn, msg.GetAboutEntityId(), roomname); // add to current building if (!gn.empty()) { IconComponent *ic; mEntityManager->GetComponent(msg.GetAboutEntityId(), ic); assert(ic); if (ic) mBuildingManager->addPlayerToBuildings(gn, msg.GetAboutEntityId(), pc->GetColor(), ic->GetPath(), ic->GetTranslation(), ic->GetVisible()); } } //////////////////////////////////////////////////////////////////////////////// // user clicked on player icon in building view void CtrlImpl::OnPlayerPicked(const dtEntity::Message &m) { // forward to simulator if (mViewerCallback != nullptr) { const PlayerPickedMessage &msg = static_cast(m); dtEntity::EntityId worldid = mBuildingManager->getWorldPlayerIdFromLocalPlayer(msg.GetAboutEntityId()); if (worldid == 0) { LOG_ERROR("Could not resolve proxy player to world player!"); } else { dtEntity::MapComponent *mc = mMapSystem->GetComponent(worldid); assert(mc); if (mc) mViewerCallback->PlayerPicked(mc->GetUniqueId()); } } } //////////////////////////////////////////////////////////////////////////////// // This message is sent by the showSelection code to hide the selection after a // defined time interval. void CtrlImpl::OnHideSelection(const dtEntity::Message& m) { const HideSelectionMessage& msg = static_cast(m); if (msg.GetNameFlagId() == false) { mBuildingManager->setShowPlayerSelection(msg.GetAboutEntityId(), false); } else { mBuildingManager->setShowDisplayNamesSelection(msg.GetAboutEntityId(), false); } } //////////////////////////////////////////////////////////////////////////////// // this message is sent from player component. We can't call showShoot directly // from viewer interface call because shots may be displayed in future sim time. // Shots are handled as a player command. void CtrlImpl::OnShoot(const dtEntity::Message &m) { const ShootMessage &msg = static_cast(m); mBuildingManager->showShoot(msg.GetId(), msg.GetShooter(), msg.GetTarget(), msg.GetShooterPos(), msg.GetTargetPos(), msg.GetBuildingIdSrc(), msg.GetRoomNumberSrc(), msg.GetBuildingIdDst(), msg.GetRoomNumberDst(), msg.GetLineType(), msg.GetDuration(), msg.GetSimTimeCreated()); } //////////////////////////////////////////////////////////////////////////////// // this message is sent from player component. We can't call showHidePlayer directly // from viewer interface call because hide events may be displayed in future sim time. // Hide events are handled as a player command. void CtrlImpl::OnShowHidePlayer(const dtEntity::Message &m) { const ShowHidePlayerMessage &msg = static_cast(m); mBuildingManager->setShowHidePlayer(msg.GetAboutEntityId(), msg.GetShow()); } //////////////////////////////////////////////////////////////////////////////// // user clicked on displayName in building view void CtrlImpl::OnDisplayNameClicked(const dtEntity::Message& m) { // forward to simulator if(mViewerCallback != nullptr) { const DisplayNameClickedMessage& msg = static_cast(m); std::string building; std::string room; dtEntity::EntityId worldid = mBuildingManager->getWorldNameIdFromLocalName(msg.GetAboutEntityId(), building, room); if(worldid == 0) { LOG_ERROR("Could not resolve name to world name!"); } else { mViewerCallback->DisplayNamePicked( building, room ); mViewerCallback->HandleViewerEvent(BuildingViewer::BgViewerEvents::RoomNameClicked, {building, room}); } } } //////////////////////////////////////////////////////////////////////////////// void CtrlImpl::SetViewerCallback(ViewerCallback *vc) { assert(mViewerCallback == nullptr); assert(vc != nullptr); mViewerCallback = vc; // delete mLogHandler; // osg::referenced mLogHandler = new LogHandler(mViewerCallback); dtEntity::LogManager::GetInstance().AddListener(mLogHandler); } //////////////////////////////////////////////////////////////////////////////// void CtrlImpl::RemoveViewerCallback() { dtEntity::LogManager::GetInstance().RemoveListener(mLogHandler); mViewerCallback = nullptr; // delete mLogHandler; // osg::referenced mLogHandler = nullptr; } //////////////////////////////////////////////////////////////////////////////// bool CtrlImpl::SpawnBuilding(const BuildingData &b, bool useStaticMeshesFolder) { dtEntity::EntityManager &em = *mEntityManager; std::ostringstream meshpath; if(useStaticMeshesFolder) { assert(osgDB::getFileExtension(b.mFileName) == "flt"); // replace .flt ending with .ive meshpath << b.mFileName << ".ive"; } else { meshpath << b.mFileName; } if (osgDB::findDataFile(meshpath.str()).empty()) { osgDB::FilePathList filePathList = osgDB::getDataFilePathList(); LOG_ERROR("Cannot find building mesh file " << meshpath.str() << " Path=" << meshpath.str() << " xmlPath=" << b.mBuildingXMLPath << " osgDB=" << osgDB::getCurrentWorkingDirectory() << " getPathRoot=" << osgDB::getPathRoot(meshpath.str()) << " useStaticMeshesFolder=" << useStaticMeshesFolder << " b.mFileName=" << b.mFileName << " b.mDescription" << b.mDescription << " b.mDisplayName" << b.mDisplayName << " filePathList.size=" << filePathList.size()); for ( unsigned int i = 0; i < filePathList.size(); i++) { LOG_ERROR(" filePathList[" << i << "]=" << filePathList[i]); } return false; } // create building entity dtEntity::Entity *entity; em.CreateEntity(entity); // for internal bookkeeping dtEntity::MapComponent *mapcomp; if (!entity->CreateComponent(mapcomp)) return false; mapcomp->SetUniqueId(b.mFeatureId); mapcomp->SetDescription(b.mDescription); mapcomp->SetSaveWithMap(false); mapcomp->SetMapName("buildings"); mapcomp->SetEntityName(b.mFileName + "_" + b.mFeatureId); // load building mesh dtEntityOSG::StaticMeshComponent *smc; if (!entity->CreateComponent(smc)) return false; smc->SetMesh(meshpath.str()); smc->Finished(); // Remove doors from model // place in scene dtEntityOSG::PositionAttitudeTransformComponent *pat; if (!entity->CreateComponent(pat)) return false; pat->SetPosition(osg::Vec3d(b.mX, b.mY, b.mZ)); osg::Quat q; q.makeRotate(-b.mOrientation / 180.0 * osg::PI, osg::Vec3(0, 0, 1)); pat->SetAttitude(q); pat->AddChildComponent(dtEntityOSG::StaticMeshComponent::TYPE); pat->Finished(); // add to world scene dtEntityOSG::LayerComponent *layer; if (!entity->CreateComponent(layer)) return false; layer->SetAttachedComponent(dtEntityOSG::PositionAttitudeTransformComponent::TYPE); layer->SetLayer(dtEntity::SID("default")); layer->Finished(); // used for splitting model for building views ModelSplitterComponent *msc; if (!entity->CreateComponent(msc)) return false; msc->SetBuildingId(dtEntity::SID(b.mFeatureId)); // for coloring rooms by destruction RoomDestructionComponent *btc; if (!entity->CreateComponent(btc)) return false; // add building entity to scene graph etc mMapSystem->AddToScene(entity->GetId()); mBuildingManager->addBuilding(entity->GetId(), b.mFeatureId); return true; } //////////////////////////////////////////////////////////////////////////////// void CtrlImpl::LoadBuilding(BuildingData& bd, bool useStaticMeshesFolder) { // The workaround: Add only if not already opened if (m_loadedBuildings.find(bd.mFeatureId) == m_loadedBuildings.end()) { bd.properlySpawned = SpawnBuilding(bd, useStaticMeshesFolder); bd.roomBBoxedLoaded = mBuildingManager->AddRoomOBBs(bd); m_loadedBuildings[bd.mFeatureId] = bd; } } //////////////////////////////////////////////////////////////////////////////// void CtrlImpl::deleteAllBuildings() { closeAllViews(); m_openedViewWidgets.clear(); m_loadedBuildings.clear(); mBuildingManager->deleteAllBuildings(); std::vector vecEntityIDs; mMapSystem->GetEntitiesInMap("buildings", vecEntityIDs); for (const auto &id : vecEntityIDs) { mMapSystem->DeleteComponent(id); } } //////////////////////////////////////////////////////////////////////////////// /// \brief CtrlImpl::LoadAllBuildings /// Loads all buildings contained within the lastly added system path. /// A StaticMeshes-folder is not required void CtrlImpl::LoadAllBuildings() { // Path list has been filled, e.g., in 'SetProjectDataPaths' osgDB::FilePathList l = osgDB::getDataFilePathList(); QFileInfo fileInfo; for (const std::string &path : l) { QDirIterator it(QString::fromStdString(path), {QLatin1String("*.ive")}, QDir::Files, QDirIterator::Subdirectories); while (it.hasNext()) { QString pathToFile = it.next(); fileInfo.setFile(pathToFile); BuildingData bd; bd.mFileName = pathToFile.toStdString(); bd.mDescription = fileInfo.absoluteFilePath().toStdString(); bd.mFeatureId = fileInfo.fileName().toStdString(); bd.mOrientation = 70.0; // 45.0 bd.mX = 5063.9998768380610; bd.mY = 3331.0000427849591; bd.mZ = 0.0; LoadBuilding(bd, false); } } } //////////////////////////////////////////////////////////////////////////////// // parse building xml file bool CtrlImpl::LoadBuildingPositionMap(const std::string &path, bool loadAllAvailableBuildings) { if (path.empty()) { deleteAllBuildings(); return true; } if(loadAllAvailableBuildings) { LoadAllBuildings(); } else { std::vector buildings; if (!mBuildingManager->LoadBuildings(path, buildings)) return false; for (BuildingData &b : buildings) LoadBuilding(b, true); } if (mViewerCallback) mViewerCallback->SystemReady(); return true; } //////////////////////////////////////////////////////////////////////////////// void CtrlImpl::CreatePlayer(const std::string &id, const std::string &iconpath, const Color &color) { // create player entity for display in world view dtEntity::Entity *e; mEntityManager->CreateEntity(e); // for bookkeeping dtEntity::MapComponent *mc; if (!e->CreateComponent(mc)) return; mc->SetEntityName(id); mc->SetUniqueId(id); mc->SetMapName("player"); // displays texture or dot IconComponent *bc; if (!e->CreateComponent(bc)) return; bc->SetPath(iconpath); bc->SetColor(osg::Vec4(color.x, color.y, color.z, color.w)); bc->SetOriginAtBottom(true); bc->Finished(); // where to attach icon to scene graph dtEntityOSG::LayerComponent *layer; if (!e->CreateComponent(layer)) return; layer->SetAttachedComponent(IconComponent::TYPE); layer->Finished(); // player steering PlayerComponent *pc; if (!e->CreateComponent(pc)) return; pc->SetColor(osg::Vec4(color.x, color.y, color.z, color.w)); pc->Finished(); // add to scene! mMapSystem->AddToScene(e->GetId()); } //////////////////////////////////////////////////////////////////////////////// void CtrlImpl::DeletePlayer(const std::string &id, float seconds) { dtEntity::DeleteEntityMessage msg; msg.SetUniqueId(id); if (qFuzzyIsNull(seconds)) mEntityManager->EmitMessage(msg); else mEntityManager->EnqueueMessage(msg, seconds + GetSimulationTime()); } //////////////////////////////////////////////////////////////////////////////// size_t CtrlImpl::GetNumPlayers() const { return mPlayerSystem->GetNumComponents(); } //////////////////////////////////////////////////////////////////////////////// void CtrlImpl::GetPlayerIds(std::vector &toFill) { for (const auto &i : *mPlayerSystem) { dtEntity::EntityId eid = i.first; dtEntity::MapComponent *mc = mMapSystem->GetComponent(eid); if (mc) toFill.push_back(mc->GetUniqueId()); } } //////////////////////////////////////////////////////////////////////////////// size_t CtrlImpl::GetNumBuildings() const { return mBuildingManager->getNumBuildings(); } //////////////////////////////////////////////////////////////////////////////// void CtrlImpl::GetBuildingIds(std::vector &toFill) const { std::vector eids; mBuildingManager->getBuildingIds(eids); toFill.reserve(eids.size()); for (const dtEntity::EntityId &eid : eids) { const dtEntity::MapComponent *mc = mMapSystem->GetComponent(eid); toFill.push_back(mc->GetUniqueId()); } } //////////////////////////////////////////////////////////////////////////////// void CtrlImpl::SetShowSelectionBox(const std::string &id, bool show, float duration) { dtEntity::EntityId eid = mMapSystem->GetEntityIdByUniqueId(id); if (eid == 0) { LOG_ERROR("SetShowSelectionBox: Could not find entity with unique id " << id); return; } IconComponent *ic; if (mEntityManager->GetComponent(eid, ic)) { ic->SetShowSelection(show); } else { LOG_ERROR("Cannot set player selection, player entity not found!"); return; } mBuildingManager->setShowPlayerSelection(eid, show); if (show && duration > 0) { // send to the future HideSelectionMessage msg; msg.SetAboutEntityId(eid); mEntityManager->EnqueueMessage(msg, GetSimulationTime() + duration); } } //////////////////////////////////////////////////////////////////////////////// void CtrlImpl::SetPlayerShown(const std::string &id, bool show, double when) { dtEntity::EntityId eid = mMapSystem->GetEntityIdByUniqueId(id); mPlayerSystem->ShowHidePlayer(eid, show, when); if (when <= 0) mBuildingManager->setShowHidePlayer(eid, show); } //////////////////////////////////////////////////////////////////////////////// void CtrlImpl::GetRoomNames(const std::string &id, std::vector &names) { dtEntity::EntityId eid = mMapSystem->GetEntityIdByUniqueId(id); if (eid == 0) { LOG_ERROR("Could not get room names, building does not exist: " << id); return; } RoomDestructionComponent *btc; if (mEntityManager->GetComponent(eid, btc)) { const RoomDestructionComponent::RoomDestructions &rc = btc->GetRoomDestructions(); RoomDestructionComponent::RoomDestructions::const_iterator i; for (i = rc.begin(); i != rc.end(); ++i) names.push_back(i->first); } } //////////////////////////////////////////////////////////////////////////////// bool CtrlImpl::SetRoomDestructionValue(const std::string &id, const std::string &roomname, float value) { dtEntity::EntityId eid = mMapSystem->GetEntityIdByUniqueId(id); // change display in open windows mBuildingManager->setRoomDestructionValue(eid, roomname, value); // change in central model RoomDestructionComponent *btc; if (mEntityManager->GetComponent(eid, btc)) { bool success = btc->SetRoomDestructionValue(roomname, value); btc->Finished(); return success; } return false; } //////////////////////////////////////////////////////////////////////////////// bool CtrlImpl::SetBuildingDestructionValue(const std::string &id, float value) { dtEntity::EntityId eid = mMapSystem->GetEntityIdByUniqueId(id); // change display in open windows mBuildingManager->setBuildingDestructionValue(eid, value); // change in central model RoomDestructionComponent *btc; if (mEntityManager->GetComponent(eid, btc)) { btc->SetBuildingDestructionValue(value); btc->Finished(); return true; } return false; } //////////////////////////////////////////////////////////////////////////////// float CtrlImpl::GetTimeScale() const { return mPaused ? 0 : mTimeScale; } //////////////////////////////////////////////////////////////////////////////// void CtrlImpl::SetTimeScale(float scale) { mTimeScale = scale; } //////////////////////////////////////////////////////////////////////////////// double CtrlImpl::GetSimulationTime() const { return mSimTime; } //////////////////////////////////////////////////////////////////////////////// void CtrlImpl::SetSimulationTime(double t) { mSimTime = t; } //////////////////////////////////////////////////////////////////////////////// static inline void MovePointIntoOBBIfZero(const VCtrl::OBB &obb, osg::Vec3d &v) { if (obb.mBB.valid() && qFuzzyIsNull(v[0]) && qFuzzyIsNull(v[1])) { // v = v * obb.mMatrixInverse; v[0] = obb.mBB._min[0] + ( (obb.mBB._max[0] - obb.mBB._min[0]) /3 ); v[1] = obb.mBB._min[1] + ( (obb.mBB._max[1] - obb.mBB._min[1]) /3 ); v[2] = obb.mBB._min[2] + 0.5; v = v * obb.mMatrix; } } //////////////////////////////////////////////////////////////////////////////// void CtrlImpl::Teleport(const std::string& id, double when, const Vec3d& target, const std::string& bldgname, int roomNumber, const std::string& roomname, ZValue zval) { LOG_DEBUG("CtrlImpl::Teleport: " << "id='"<< id << "'" << ", when="<< when << ", target: x=" << target.x << " y=" << target.y << " z=" << target.z << ", bldgname='"<< bldgname << "'" << ", roomNumber="<< roomNumber << ", roomname='"<< roomname << "'" << ", zval=" << ((zval==ZValue::AboveMsl) ? "ZValue::AboveMsl" : "ZValue::RelativeBuildings")); osg::Vec3d t(target.x, target.y, target.z); VCtrl::OBB obb; const auto fixupZVal = [&] (const std::string &bldgname) { if (zval == ZValue::RelativeBuildings) { auto it = m_loadedBuildings.find(bldgname); if (it != m_loadedBuildings.end()) { const BuildingData &bd = it->second; t[2] += bd.mZ; obb.mBB._min[2] += bd.mZ; obb.mBB._max[2] += bd.mZ; } } }; if (!bldgname.empty() && !roomname.empty()) { fixupZVal(bldgname); bool success = mBuildingManager->getRoomBounds(bldgname, roomname, obb); if (!success) { LOG_ERROR("Room not found: " << bldgname << ", " << roomname); } else { MovePointIntoOBBIfZero(obb, t); } dtEntity::EntityId eid = mMapSystem->GetEntityIdByUniqueId(id); if (mBuildingManager->GetExactPos() == false) { VCtrl::PlayerPosition playPos; osg::Vec2d target2d(t.x(), t.y()); playPos.mPosition = target2d; playPos.mWorldPlayerId = eid; PlayerComponent* pc; success = mEntityManager->GetComponent(eid, pc); assert(success); if (success) { std::string currentRoom = dtEntity::GetStringFromSID(pc->GetCurrentRoom()); if (!currentRoom.empty() && roomname != currentRoom) { mBuildingManager->deleteRoomUsagePlayer(bldgname, currentRoom, eid); } target2d = mBuildingManager->AddRoomUsages(bldgname, roomname, playPos); t[0] = target2d[0]; t[1] = target2d[1]; } } mPlayerSystem->TeleportToTarget(eid, when, t, obb, roomNumber, roomname); } else { dtEntity::EntityId nBEid = 0; float distance; std::string strFeatureId; const Building bld = mBuildingManager->getBuildingFromFeatureId(bldgname); if (bld.mWorldBuildingId > 0) { nBEid = bld.mWorldBuildingId; distance = 1; strFeatureId = bld.mFeatureId; } if (nBEid <= 0) { nBEid = mBuildingManager->getNextBuilding(t, distance, strFeatureId); } if (distance < 125.0f) { fixupZVal(strFeatureId); const dtEntity::MapComponent* mc = mMapSystem->GetComponent(nBEid); std::string bName = mc->GetUniqueId(); RoomDestructionComponent* btc; if (mEntityManager->GetComponent(nBEid, btc)) { const RoomDestructionComponent::RoomDestructions& rc = btc->GetRoomDestructions(); if (rc.empty()) { LOG_ERROR("empty room destruction for building: " << bName); Q_ASSERT(false); } else { const std::string rName = rc.begin()->first; if (mBuildingManager->getRoomBounds(bName, rName, obb)) { dtEntity::EntityId eid = mMapSystem->GetEntityIdByUniqueId(id); mPlayerSystem->TeleportToTarget(eid, when, t, obb, -1, std::string()); } else { // OnShowMsgBox("Room (next Building) not found: " + rName + ", damage: " + std::to_string(rc.begin()->second)); LOG_ERROR("Room (next Building) not found: " << bName << ", " << rName << " Distance: " << distance ); } } } } else { dtEntity::EntityId eid = mMapSystem->GetEntityIdByUniqueId(id); mPlayerSystem->TeleportToTarget(eid, when, t, VCtrl::OBB(), -1, std::string()); } } dtEntity::GetDebugDrawInterface()->AddCross(t, osg::Vec4(1,0,0,1), 1, 20, false); } //////////////////////////////////////////////////////////////////////////////// // shoot to player void CtrlImpl::Shoot(const int32_t id, const std::string &shooter, const std::string &target, const Vec3d &shooterPos, const Vec3d &targetPos, int buildingIdSrc, int roomNumberSrc, int buildingIdDst, int roomNumberDst, int lineType, double when) { if (!m_showHitLines || (m_hitlineDuration_s == 0)) { LOG_DEBUG("Global setting set to not showing hit lines."); return; } if (shooter == target) { //LOG_WARNING("Can't shoot myself!"); return; } dtEntity::EntityId shooterId = mMapSystem->GetEntityIdByUniqueId(shooter); dtEntity::EntityId targetId = mMapSystem->GetEntityIdByUniqueId(target); mPlayerSystem->PlayerShoot(id, shooterId, targetId, shooterPos, targetPos, buildingIdSrc, roomNumberSrc, buildingIdDst, roomNumberDst, lineType, when, m_hitlineDuration_s); } //////////////////////////////////////////////////////////////////////////////// // shoot to room void CtrlImpl::Shoot(const int32_t id, const std::string &shooter, const std::string &targetBuildingId, const std::string &targetRoomId, const Vec3d &shooterPos, int lineType, double when) { if (!m_showHitLines || (m_hitlineDuration_s == 0)) { LOG_DEBUG("Global setting set to not showing hit lines."); return; } const dtEntity::EntityId targetId = mMapSystem->GetEntityIdByUniqueId(targetBuildingId); if (targetId == 0) { LOG_ERROR("Cannot Set Display name, no building found with ID " << targetBuildingId); return; } const dtEntity::EntityId shooterId = mMapSystem->GetEntityIdByUniqueId(shooter); const std::map &rooms = mBuildingManager->GetRoomOBBs(); const std::string key(targetBuildingId + " " + targetRoomId); auto itRoom = rooms.find(key); if (itRoom == rooms.end()) { LOG_WARNING("Could not find room '" << key << "' which should be hit"); return; } const VCtrl::OBB &room(itRoom->second); const auto center = room.mBB.center() * room.mMatrix; const Vec3d targetPos(center.x(), center.y(), center.z()); const int invalidBuildingRoomId = -1; mPlayerSystem->PlayerShoot(id, shooterId, targetId, shooterPos, targetPos, invalidBuildingRoomId, invalidBuildingRoomId, invalidBuildingRoomId, invalidBuildingRoomId, lineType, when, m_hitlineDuration_s); } //////////////////////////////////////////////////////////////////////////////// void CtrlImpl::removeShoot(const int32_t id) { mBuildingManager->removeShoot(id); LOG_WARNING("CtrlImpl::removeShoot '" << id << " "); } //////////////////////////////////////////////////////////////////////////////// void CtrlImpl::ChangeLineMode(unsigned int contextId, bool line) { Q_UNUSED(contextId); // Ignored since 'line' is a global setting m_showHitLines = line; } //////////////////////////////////////////////////////////////////////////////// void CtrlImpl::ChangeNameMode(unsigned int contextId, bool name) { mBuildingManager->changeNameMode(contextId, name); ViewWidget* vw = GetViewWidget(contextId) ; if(vw) vw->setNameModeCheckbox(name); if(mViewerCallback) { mViewerCallback->ViewNameModeChanged(contextId, name); } } //////////////////////////////////////////////////////////////////////////////// bool CtrlImpl::SetDisplayName(const std::string& buildingid, const std::string& roomname, const std::string& strDisplayName) { dtEntity::EntityId bid = mMapSystem->GetEntityIdByUniqueId(buildingid); if (bid == 0) { LOG_ERROR("Cannot Set Display name, no building found with ID " << buildingid); return false; } // change display in open windows mBuildingManager->setRoomDisplayName(buildingid, bid, roomname, strDisplayName); return true; } //////////////////////////////////////////////////////////////////////////////// void CtrlImpl::ShowDisplayNames(const std::string& buildingid, bool show) { dtEntity::EntityId bid = mMapSystem->GetEntityIdByUniqueId(buildingid); if(bid == 0) { LOG_ERROR("Cannot Show Display names, no building found with ID " << buildingid); } mBuildingManager->setShowHideDisplayNames(bid, 9999, show); } //////////////////////////////////////////////////////////////////////////////// void CtrlImpl::SetShowDisplayNamesSelectionBox(const std::string& buildingid, const std::string& id, bool show, float duration) { std::ostringstream en; en << "bd_" << buildingid << "_rm_" << id; dtEntity::EntityId eid = mMapSystem->GetEntityIdByUniqueId(en.str()); if(eid == 0) { LOG_ERROR("SetShowSelectionBox: Could not find entity with unique ids " << buildingid << ", "<< id); return; } mBuildingManager->setShowDisplayNamesSelection(eid, show); if(show && duration > 0) { // send to the future HideSelectionMessage msg; msg.SetAboutEntityId(eid); msg.SetNameFlagId(true); mEntityManager->EnqueueMessage(msg, GetSimulationTime() + duration); } } //////////////////////////////////////////////////////////////////////////////// void CtrlImpl::setHitLineDuration(uint32_t durationSec) { m_hitlineDuration_s = durationSec; mBuildingManager->updateShotDuration(m_hitlineDuration_s); } //////////////////////////////////////////////////////////////////////////////// void CtrlImpl::SetPaused(bool v) { mPaused = v; } //////////////////////////////////////////////////////////////////////////////// bool CtrlImpl::GetPaused() const { return mPaused; } //////////////////////////////////////////////////////////////////////////////// void CtrlImpl::SetPlayerColor(const std::string &id, const Color &color) { dtEntity::EntityId eid = mMapSystem->GetEntityIdByUniqueId(id); osg::Vec4 col(color.x, color.y, color.z, color.w); PlayerComponent *pc; if (mEntityManager->GetComponent(eid, pc)) { pc->SetColor(col); mBuildingManager->setPlayerColor(eid, col); } } //////////////////////////////////////////////////////////////////////////////// void CtrlImpl::SetPlayerIcon(const std::string &id, const std::string &path) { dtEntity::EntityId eid = mMapSystem->GetEntityIdByUniqueId(id); IconComponent *ic; if (mEntityManager->GetComponent(eid, ic)) { ic->SetPath(path); mBuildingManager->setPlayerIcon(eid, path); } } //////////////////////////////////////////////////////////////////////////////// void CtrlImpl::reloadPlayerIcons() { mBuildingManager->reloadPlayerIcons(); } //////////////////////////////////////////////////////////////////////////////// void CtrlImpl::UpdatePlayerPartyColors(const std::map& mapPlayerColors) { for (const auto& player : mapPlayerColors) { dtEntity::EntityId eid = mMapSystem->GetEntityIdByUniqueId(player.first); mBuildingManager->updatePlayerPartyColor(eid, player.second); } } //////////////////////////////////////////////////////////////////////////////// void CtrlImpl::SetProjectDataPaths(const std::vector &paths) { /* * Important: FilePathList must be kept even if a model has already been loaded into RAM * since dynamic data is additionaly loaded when showing a building model! */ osgDB::FilePathList l = osgDB::getDataFilePathList(); osgDB::FilePathList newl = l; for (const std::string &newpath : paths) { bool bFound = false; for (const std::string &path : l) { if (path == newpath) { bFound = true; break; } } if (!bFound) newl.push_front(newpath); } osgDB::setDataFilePathList(newl); } void CtrlImpl::onViewWidget_MaximizeRequest(ViewWidget *pWidget) { m_isInFullscreenMode = true; if (mViewerCallback) mViewerCallback->HandleViewerEvent(BuildingViewer::BgViewerEvents::WidgetMaximized, {pWidget->featureID()}); m_pMainWindow->clearGridLayout(); m_pMainWindow->openView(pWidget); pWidget->setMaximized(true); highlightView(pWidget->featureID()); } void CtrlImpl::onViewWidget_MinimizeRequest(ViewWidget *pWidget) { if (mViewerCallback) mViewerCallback->HandleViewerEvent(BuildingViewer::BgViewerEvents::WidgetMinimized, {pWidget->featureID()}); m_pMainWindow->clearGridLayout(); m_pMainWindow->openViews(m_openedViewWidgets.getViewWidgets()); pWidget->setMaximized(false); m_isInFullscreenMode = false; } void CtrlImpl::onViewWidget_CloseRequest(ViewWidget *pWidget) { if (mViewerCallback) mViewerCallback->HandleViewerEvent(BuildingViewer::BgViewerEvents::WidgetClosed, {pWidget->featureID()}); m_openedViewWidgets.remove(pWidget->featureID()); m_pMainWindow->closeView(pWidget); } void CtrlImpl::onViewWidget_FocusRequest(ViewWidget *pWidget) { if (mViewerCallback) mViewerCallback->HandleViewerEvent(BuildingViewer::BgViewerEvents::WidgetGotFocus, {pWidget->featureID()}); for (const auto &pViewWidget : m_openedViewWidgets.getViewWidgets()) pViewWidget->setSelected(false); pWidget->setSelected(true); } void CtrlImpl::onViewWidget_Zoom(ViewWidget *pWidget, bool zoomIn) { BuildingDisplay *pDisplay = mBuildingManager->getBuildingDisplay(pWidget->contextID()); pDisplay->pMotion->performZoom(zoomIn ? 1 : -1, osg::Vec2(0.5f, 0.5f)); } // Wrapper function bool CtrlImpl::open(const std::string& strFeatureID, bool exactPos) { std::vector vecFeatureID = {strFeatureID}; return open(vecFeatureID, exactPos); } bool CtrlImpl::open(const std::vector &vecFeatureIDs, bool exactPos) { if (vecFeatureIDs.empty()) return true; if (!m_pMainWindow) return false; /* * We have 2 cases: * * 1. View is already open and will therefore by highlighted (blinking border) * 2. New View(s) is added and shown * * We could simply clear m_pMainWindow's layout but loading means that we are simply * adding new widgets to the layout. If the user requested an already opened Widget it will be highlighted (blinking) */ // First case for (const std::string &strFeatureID : vecFeatureIDs) { if (m_openedViewWidgets.exist(strFeatureID)) { // View is already opened, let it blink m_pMainWindow->updateHighlightThreads(m_openedViewWidgets.getViewWidget(strFeatureID)); } } // Second case std::vector m_vecNewViewWidgets; for (const std::string &strFeatureID : vecFeatureIDs) { if (m_openedViewWidgets.size() >= m_maxParallelViewWidgets) { LOG_WARNING("Trying to open more than " << m_maxParallelViewWidgets << " ViewWidgets!"); break; } if (!m_openedViewWidgets.exist(strFeatureID)) { // View has been requested but has not been created so far dtEntity::EntityId bid = mMapSystem->GetEntityIdByUniqueId(strFeatureID); if (bid > 0) { unsigned int contextid; ViewWidget *pSubView = CreateMainWindowView(bid, strFeatureID, contextid, exactPos); if(!pSubView) { LOG_ERROR("Could not open desired window with feature id " << strFeatureID); continue; } pSubView->setSelected(true); QString desc = QString::fromStdString(mMapSystem->GetComponent(bid)->GetDescription()); pSubView->setText(desc); QObject::connect(pSubView, &ViewWidget::signal_MaximizeRequest, this, &CtrlImpl::onViewWidget_MaximizeRequest); QObject::connect(pSubView, &ViewWidget::signal_MinimizeRequest, this, &CtrlImpl::onViewWidget_MinimizeRequest); QObject::connect(pSubView, &ViewWidget::signal_CloseRequest, this, &CtrlImpl::onViewWidget_CloseRequest); QObject::connect(pSubView, &ViewWidget::signal_FocusRequest, this, &CtrlImpl::onViewWidget_FocusRequest); QObject::connect(pSubView, &ViewWidget::signal_Zoom, this, &CtrlImpl::onViewWidget_Zoom); // Context IDs werden im vorangehenden Interface (das, was load(All) aufruft) nicht gespeichert. Daher die warnings, z.b. bei settransparency storeWidget(pSubView, strFeatureID, contextid, m_desiredViewMode, m_desiredPlayerDisplayMode); m_openedViewWidgets.add(pSubView); m_vecNewViewWidgets.push_back(pSubView); } else LOG_WARNING("Requested building was not contained in loaded xml file: " << strFeatureID); } } if (!m_isInFullscreenMode && m_openedViewWidgets.size()) m_pMainWindow->openViews(m_vecNewViewWidgets); return true; } quintptr CtrlImpl::requestMainWindowHandle() { /* QMessageBox msgBox; msgBox.setText("Window handle has been requested"); msgBox.exec(); if(m_pMainWindow) { msgBox.setText("m_pMainWindow IS NULLPTR"); msgBox.exec(); } msgBox.setText("m_pMainWindow: " + QString::number(m_pMainWindow->winId())); msgBox.exec(); */ assert(m_pMainWindow); return m_pMainWindow->winId(); } //////////////////////////////////////////////////////////////////////////////// /// \brief Loads all respectively all so far not loaded widgets /// \return /// bool CtrlImpl::openAll(bool exactPos) { std::vector vecEntityIDs; mMapSystem->GetEntitiesInMap("buildings", vecEntityIDs); std::vector vecFeatureIDs; for (const auto &id : vecEntityIDs) { QString featureId = QString::fromStdString(mMapSystem->GetComponent(id)->GetUniqueId()); vecFeatureIDs.push_back(featureId.toStdString()); } return open(vecFeatureIDs, exactPos); } bool CtrlImpl::getBuildingString(const std::string &strFeatureID, std::string *strBuildingNameID) { // Feature ID is, e.g., "House_16". We are only interested in the real ID "16" std::size_t posSeparator = strFeatureID.find_last_of('_'); if (posSeparator != std::string::npos) { if (strBuildingNameID) *strBuildingNameID = "Building " + strFeatureID.substr(posSeparator + 1); return true; } return false; } void CtrlImpl::storeWidget(ViewWidget *pWidget, const std::string &strBuildingNameID, unsigned int contextid, VCtrl::ViewMode vmode, PlayerDisplayMode displayMode) { if (mViewerCallback) mViewerCallback->ViewCreated(contextid, vmode); pWidget->setWindowTitle(QString::fromStdString(strBuildingNameID)); pWidget->setViewerInterface(this); mViewWidgets[contextid] = pWidget; // Order is important: pWidget must be stored in 'mViewWidgets' before // the following code is called // Set correct ViewWidget button appearance pWidget->setDisplayedViewMode(vmode); pWidget->setDotModeCheckbox((displayMode == PlayerDisplayMode::Dots)); mBuildingManager->changeLineMode(contextid, true); // Always show hit lines for targets outside of a building (2018-05-17: Potential bug? Use m_showHitLines here?) // Set corresponding button settings within BuildingManager ChangeViewMode(contextid, vmode); SetPlayerDisplayMode(contextid, displayMode); SetWallTransparency(contextid, m_desiredTransparencyValue); } void CtrlImpl::OnShowMsgBox(const std::string &strText) { QMessageBox msgBox; msgBox.setText(QString::fromStdString(strText)); msgBox.exec(); } ViewWidget *CtrlImpl::CreateMainWindowView(dtEntity::EntityId bid, const std::string &featureId, unsigned int &contextid, bool exactPos) { osg::Camera* pCamera = createCamera(featureId); ViewWidget *pWidget = addViewWidget(pCamera, contextid, bid, featureId, exactPos); connect(pWidget, &ViewWidget::signal_ViewIsShown, this, &CtrlImpl::restoreViewWidgetStateDeferred); Color col; // ToDo: Find nice color for lightstyle? (112.0/256.0,112.0/256.0,112.0/256.0, 0.0); mBuildingManager->displayBuilding(featureId, contextid, bid, osg::Vec4(col.x, col.y, col.z, col.w), ViewMode::ISOMETRIC, exactPos); return pWidget; } //////////////////////////////////////////////////////////////////////////////// bool CtrlImpl::CreateView(dtEntity::EntityId bid, const std::string &windowtitle, const Color &col, ViewMode viewmode, int screennum, bool fullscreen, int x, int y, int w, int h, unsigned int &contextid, bool exactPos) { const std::string name = "Building " + windowtitle; ViewWidget *pWidget = addViewWidget(createCamera(screennum, fullscreen, x, y, w, h, name, true), contextid, bid, windowtitle); connect(pWidget, &ViewWidget::signal_ViewIsShown, this, &CtrlImpl::restoreViewWidgetStateDeferred); mBuildingManager->displayBuilding(contextid, bid, osg::Vec4(col.x, col.y, col.z, col.w), viewmode, exactPos); storeWidget(pWidget, windowtitle, contextid, viewmode, m_desiredPlayerDisplayMode); return true; } //////////////////////////////////////////////////////////////////////////////// void CtrlImpl::UseExactPositioning(bool exactPos) { LOG_ALWAYS("CtrlImpl: UseExactPositioning=" << ((exactPos) ? "ON" : "OFF")); mBuildingManager->SetExactPos(exactPos); } //////////////////////////////////////////////////////////////////////////////// bool CtrlImpl::OpenView(unsigned int contextId) { auto it = mViewWidgets.find(contextId); if (it == mViewWidgets.end() || it->second == nullptr) return false; //it->second->show(); return true; } //////////////////////////////////////////////////////////////////////////////// bool CtrlImpl::OpenView(const std::string &buildingid, const std::string &windowtitle, const Color &col, ViewMode viewmode, int screennum, bool fullscreen, int x, int y, int w, int h, unsigned int &contextid, bool exactPos) { dtEntity::EntityId bid = mMapSystem->GetEntityIdByUniqueId(buildingid); if (bid == 0) { LOG_ERROR("Cannot open view, no building found with ID " << buildingid); return false; } if (CreateView(bid, windowtitle, col, viewmode, screennum, fullscreen, x, y, w, h, contextid, exactPos)) { //mViewWidgets[contextid]->show(); return true; } return false; } //////////////////////////////////////////////////////////////////////////////// void CtrlImpl::CloseView(ViewWidget *pWidget) { if (pWidget->isMaximized()) m_isInFullscreenMode = false; CloseView(pWidget->contextID()); } //////////////////////////////////////////////////////////////////////////////// void CtrlImpl::CloseView(unsigned int contextid) { mBuildingManager->closeBuildingDisplay(contextid); ViewWidget *vw = GetViewWidget(contextid); if (vw) { vw->closeByAPI(); mViewWidgets.erase(contextid); delete vw; vw = nullptr; } if (mViewerCallback) mViewerCallback->ViewClosed(contextid); } //////////////////////////////////////////////////////////////////////////////// void CtrlImpl::ChangeViewMode(unsigned int contextId, ViewMode viewmode) { ViewWidget *vw = GetViewWidget(contextId); if (vw) { /* * The following order rotates too much and results in a wrong (icon) visualization. * * Start (2D) -> Lock -> Switch to 3D -> Broken visualization * * The following works: * * Start (2D) -> Switch to 3D -> Lock -> Correct visualization * * Workaround: If the user changes the view mode to 3D and "lock" is active, we remove the lock, * switch the viewmode and lock afterwards. */ bool comingFromOrtho = (viewmode == ViewMode::ISOMETRIC); if(comingFromOrtho && vw->lockedRotation()) { mBuildingManager->lockViewRotationToMapRotation(contextId, false); mBuildingManager->changeViewMode(contextId, viewmode, false); mBuildingManager->lockViewRotationToMapRotation(contextId, true); } else { mBuildingManager->changeViewMode(contextId, viewmode, false); } vw->setDisplayedViewMode(viewmode); if (mViewerCallback) mViewerCallback->ViewModeChanged(contextId, viewmode); } } //////////////////////////////////////////////////////////////////////////////// void CtrlImpl::LockViewRotationToMapRotation(unsigned int contextId, bool lock) { mBuildingManager->lockViewRotationToMapRotation(contextId, lock); ViewWidget *vw = GetViewWidget(contextId) ; if (vw) vw->setLockRotationCheckbox(lock); if (mViewerCallback) mViewerCallback->ViewLockRotationChanged(contextId, lock); } //////////////////////////////////////////////////////////////////////////////// void CtrlImpl::SetPlayerDisplayMode(unsigned int contextId, PlayerDisplayMode mode) { mBuildingManager->setPlayerDisplayMode(contextId, mode); ViewWidget *vw = GetViewWidget(contextId) ; if (vw) vw->setDotModeCheckbox(mode == PlayerDisplayMode::Dots); if (mViewerCallback) mViewerCallback->PlayerDisplayModeChanged(contextId, mode); } //////////////////////////////////////////////////////////////////////////////// void CtrlImpl::SetWallTransparency(unsigned int contextId, float transparency) { mBuildingManager->setWallTransparency(contextId, 1.0 - transparency); ViewWidget *vw = GetViewWidget(contextId) ; if (vw) vw->setTransparency(transparency); if (mViewerCallback) mViewerCallback->ViewTransparencyChanged(); } //////////////////////////////////////////////////////////////////////////////// float CtrlImpl::GetMapRotation() const { return mBuildingManager->getMapRotation(); } //////////////////////////////////////////////////////////////////////////////// void CtrlImpl::SetMapRotation(float angle) { mBuildingManager->setMapRotation(angle); } //////////////////////////////////////////////////////////////////////////////// void CtrlImpl::OpenOverview() { unsigned int contextid; ViewWidget *pWidget = addViewWidget(createCamera(0, false, 100, 100, 800, 600, "Overview", true), contextid); connect(pWidget, &ViewWidget::signal_ViewIsShown, this, &CtrlImpl::restoreViewWidgetStateDeferred); pWidget->show(); mBuildingManager->openOverview(contextid); dtEntity::GetDebugDrawInterface()->SetEnabled(true); const std::map &rooms = mBuildingManager->GetRoomOBBs(); for (const auto &it : rooms) { const VCtrl::OBB &room = it.second; dtEntity::GetDebugDrawInterface()->AddOBB(room.mMatrix, room.mBB._min, room.mBB._max, osg::Vec4(1, 0, 1, 1), 1, FLT_MAX, false); const osg::Vec3 pos = room.mBB.center() * room.mMatrix; dtEntity::GetDebugDrawInterface()->AddString(pos, it.first, osg::Vec4(0, 1, 0, 1), FLT_MAX, false); } dtEntity::MoveCameraToPositionMessage msg; msg.SetPosition(dtEntity::Vec3d(72850.0, 13000.0, 0.0)); mEntityManager->EmitMessage(msg); } //////////////////////////////////////////////////////////////////////////////// void CtrlImpl::FastForwardSimulationTo(double t) { FastForwardMessage msg; msg.SetEndTime(t); mEntityManager->EmitMessage(msg); } //////////////////////////////////////////////////////////////////////////////// void CtrlImpl::DeleteQueuedCommands() { mPlayerSystem->DeleteQueuedCommands(); } //////////////////////////////////////////////////////////////////////////////// void CtrlImpl::GetDeviceNames(const std::string &buildingId, std::vector &names) { dtEntity::EntityId bid = mMapSystem->GetEntityIdByUniqueId(buildingId); if (bid == 0) { LOG_ERROR("Cannot get device names, no building found with ID " << buildingId); return; } dtEntityOSG::StaticMeshComponent *smc; if (!mEntityManager->GetComponent(bid, smc)) { LOG_ERROR("Cannot get device names, building has no static mesh?!?"); return; } FindByNameVisitor v(QStringLiteral("window")); smc->GetNode()->accept(v); for (const auto &node : v.mNodes) { const std::string &n = node->getName(); if (!n.empty()) names.push_back(n); } } //////////////////////////////////////////////////////////////////////////////// bool CtrlImpl::ShowDeviceIcon(const std::string &id, const std::string &buildingid, const std::string &devicename, const std::string &iconpath) { dtEntity::EntityId bid = mMapSystem->GetEntityIdByUniqueId(buildingid); if (bid == 0) { LOG_ERROR("Cannot get device names, no building found with ID " << id); return false; } dtEntityOSG::StaticMeshComponent *smc; if (!mEntityManager->GetComponent(bid, smc)) { LOG_ERROR("Cannot get device names, building has no static mesh?!?"); return false; } FindByNameVisitor v(QString::fromStdString(devicename)); smc->GetNode()->accept(v); if (v.mNodes.empty()) { LOG_ERROR("Cannot get device names,not found in static mesh!"); return false; } dtEntityOSG::PositionAttitudeTransformComponent *pat; if (!mEntityManager->GetComponent(bid, pat)) { LOG_ERROR("Cannot position transform component!"); return false; } osg::Matrix mat; mat.setTrans(pat->GetTranslation()); mat.setRotate(pat->GetAttitude()); for (const auto &node : v.mNodes) { osg::ComputeBoundsVisitor cv; node->accept(cv); if (!cv.getBoundingBox().valid()) { LOG_ERROR("Could not place device icon, has no valid geometry!"); continue; } osg::Vec3d pos = cv.getBoundingBox().center() * mat; // create entity for display in world view dtEntity::Entity *e; mEntityManager->CreateEntity(e); // for bookkeeping dtEntity::MapComponent *mc; if (!e->CreateComponent(mc)) return false; mc->SetEntityName(id); mc->SetUniqueId(id); mc->SetMapName("device"); // displays texture or dot IconComponent *bc; if (!e->CreateComponent(bc)) return false; bc->SetPath(iconpath); bc->SetColor(osg::Vec4(1, 1, 1, 1)); bc->SetOriginAtBottom(false); bc->SetTranslation(pos); bc->Finished(); // where to attach icon to scene graph dtEntityOSG::LayerComponent *layer; if (!e->CreateComponent(layer)) return false; layer->SetAttachedComponent(IconComponent::TYPE); layer->Finished(); // add to scene! mMapSystem->AddToScene(e->GetId()); mBuildingManager->addDeviceToBuilding(e->GetId(), bid, pos, iconpath); } return true; } //////////////////////////////////////////////////////////////////////////////// bool CtrlImpl::ShowDeviceIcon(const std::string &id, const std::string &buildingid, const Vec3d &position, const std::string &iconpath) { /* std::stringstream ss; ss<<"CtrlImpl::ShowDeviceIcon: "<GetEntityIdByUniqueId(buildingid); if (bid == 0) { LOG_ERROR("Cannot get device names, no building found with ID " << buildingid); return false; } dtEntityOSG::PositionAttitudeTransformComponent *pat; if (!mEntityManager->GetComponent(bid, pat)) { LOG_ERROR("Cannot get device names, building has no position node?!?"); return false; } osg::Matrix mat; mat.setTrans(pat->GetTranslation()); mat.setRotate(pat->GetAttitude()); osg::Vec3d pos(position.x, position.y, position.z); pos = pos * mat; // create entity for display in world view dtEntity::Entity *e; mEntityManager->CreateEntity(e); // for bookkeeping dtEntity::MapComponent *mc; if (!e->CreateComponent(mc)) return false; mc->SetEntityName(id); mc->SetUniqueId(id); mc->SetMapName("device"); // displays texture or dot IconComponent *bc; if (!e->CreateComponent(bc)) return false; bc->SetPath(iconpath); bc->SetColor(osg::Vec4(1, 1, 1, 1)); bc->SetOriginAtBottom(false); bc->SetTranslation(pos); bc->Finished(); // where to attach icon to scene graph dtEntityOSG::LayerComponent *layer; if (!e->CreateComponent(layer)) return false; layer->SetAttachedComponent(IconComponent::TYPE); layer->Finished(); // add to scene! mMapSystem->AddToScene(e->GetId()); mBuildingManager->addDeviceToBuilding(e->GetId(), bid, pos, iconpath); return true; } //////////////////////////////////////////////////////////////////////////////// bool CtrlImpl::HideDeviceIcon(const std::string &id) { dtEntity::EntityId eid = mMapSystem->GetEntityIdByUniqueId(id); if (eid != 0) { mMapSystem->RemoveFromScene(eid); mBuildingManager->removeDeviceFromBuilding(eid); mEntityManager->KillEntity(eid); return true; } return false; } //////////////////////////////////////////////////////////////////////////////// void CtrlImpl::SetNonRealtimeMode(bool on) { mNonRealtimeMode = on; } //////////////////////////////////////////////////////////////////////////////// bool CtrlImpl::GetNonRealtimeMode() const { return mNonRealtimeMode; } //////////////////////////////////////////////////////////////////////////////// bool CtrlImpl::StartVideoRecording(unsigned int contextid, const std::string &path, int w, int h, int framerate, int quality) { mFrameRate = framerate; #ifdef BUILD_WITH_GSTREAMER VideoSystem *vsys; if (!mEntityManager->GetES(vsys)) return false; return vsys->StartVideoRecording(contextid, path, w, h, framerate, quality); #else LOG_ERROR("Not built with GStreamer, video recording not possible!"); return false; #endif } //////////////////////////////////////////////////////////////////////////////// bool CtrlImpl::StartVideoRecording(const std::string &buildingId, const std::string &path, int w, int h, int framerate, int quality) { if (buildingId == "all") { for (const auto &pWidget : m_openedViewWidgets.getViewWidgets()) StartVideoRecording(pWidget->contextID(), path + pWidget->description() + ".mpg", w, h, framerate, quality); } else { ViewWidget *pWidget = m_openedViewWidgets.getViewWidget(buildingId); if (pWidget) StartVideoRecording(pWidget->contextID(), path + pWidget->description() + ".mpg", w, h, framerate, quality); } return true; } //////////////////////////////////////////////////////////////////////////////// bool CtrlImpl::StopVideoRecording(unsigned int contextid) { #ifdef BUILD_WITH_GSTREAMER VideoSystem *vsys; if (!mEntityManager->GetES(vsys)) return false; return vsys->StopVideoRecording(contextid); #else LOG_ERROR("Not built with GStreamer, video recording not possible!"); return false; #endif } //////////////////////////////////////////////////////////////////////////////// bool CtrlImpl::StopVideoRecording(const std::string &buildingId) { if (buildingId == "all") { for (const auto &pWidget : m_openedViewWidgets.getViewWidgets()) StopVideoRecording(pWidget->contextID()); } else { ViewWidget *pWidget = m_openedViewWidgets.getViewWidget(buildingId); if (pWidget) StopVideoRecording(pWidget->contextID()); } return true; } }