#include "buildingmanager.h" #include "iconcomponent.h" #include "modelsplittercomponent.h" #include "motionmodel.h" #include "orthomotionmodel.h" #include "playercomponent.h" #include "roomdestructioncomponent.h" #include "textcomponent.h" #include "visitors.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "rapidxml.hpp" #include "rapidxml_utils.hpp" // Subtract from room OBB height so that only room navmesh cells are in obb #define ROOM_OBB_Z_MIN_SUBTRACT 0.1f #define ROOM_OBB_Z_MAX_SUBTRACT 0.5f namespace VCtrl { /////////////////////////////////////////////////////////////////////////////////////////////////////// BuildingManager::BuildingManager(dtEntity::EntityManager &em) : mEntityManager(&em) , mShotOverview(false) , mExactPos(true) , mMapRotation(0) , mTickFunctor(this, &BuildingManager::Tick) , m_logger(BuildingViewer::getLogger("BuildingManager")) { em.RegisterForMessages(dtEntity::TickMessage::TYPE, mTickFunctor); } /////////////////////////////////////////////////////////////////////////////////////////////////////// BuildingManager::~BuildingManager() { deleteAllBuildings(); } /////////////////////////////////////////////////////////////////////////////////////////////////////// void BuildingManager::deleteAllBuildings() { for (auto building : mBuildings) { qDeleteAll(building->mShots); qDeleteAll(building->mDevices); qDeleteAll(building->mNames); delete building; } mBuildings.clear(); } //////////////////////////////////////////////////////////////////////////////////////////////////////// osg::Vec2d BuildingManager::AddRoomUsages(const std::string& bldgname, const std::string& roomname, VCtrl::PlayerPosition& playPos) { const std::string key = bldgname + " " + roomname; auto i = mUsedRooms.find(key); if (i == mUsedRooms.end()) { RoomPlayers mroomPlayers; mroomPlayers.push_back(playPos); mUsedRooms[key] = mroomPlayers; } else { // room contains player RoomPlayers &mroomPlayers = i->second; bool knownPlayer = false; // for (const PlayerPosition &pp :mroomPlayers) for (auto it = mroomPlayers.begin(); it != mroomPlayers.end(); ++it) { if (it->mWorldPlayerId == playPos.mWorldPlayerId) { it = mroomPlayers.erase(it); //knownPlayer = true; // playPos.mPosition = pp.mPosition; break; } } if (knownPlayer == false) { VCtrl::OBB roomObb; getRoomBounds(bldgname, roomname, roomObb); double shiftVal = 0.6; for (const PlayerPosition &pp : mroomPlayers) { if (qFuzzyCompare(pp.mPosition[0], playPos.mPosition[0]) && qFuzzyCompare(pp.mPosition[1], playPos.mPosition[1])) { playPos.mPosition = shiftPosition(roomObb, playPos.mPosition, shiftVal); } } mroomPlayers.push_back(playPos); } } return playPos.mPosition; } //////////////////////////////////////////////////////////////////////////////////////////////////////// void BuildingManager::deleteRoomUsagePlayer(const std::string& bldgname, const std::string& roomname, dtEntity::EntityId playerId) { const std::string key = bldgname + " " + roomname; auto i = mUsedRooms.find(key); if (i != mUsedRooms.end()) { RoomPlayers &mroomPlayers = i->second; // VCtrl::OBB roomObb; // getRoomBounds(bldgname, roomname, roomObb); // double shiftVal = -0.6; // size_t deletedPosition{0}; for (auto it = mroomPlayers.begin(); it != mroomPlayers.end(); ++it) { if (mroomPlayers.size() == 0) break; const PlayerPosition &pp = *it; if (pp.mWorldPlayerId == playerId) { if (mroomPlayers.size() > 0 ) { it = mroomPlayers.erase(it); } break; } // deletedPosition++; } // for (auto pos = deletedPosition; pos < mroomPlayers.size(); pos++) { // mroomPlayers[pos].mPosition = shiftPosition(roomObb, mroomPlayers[pos].mPosition, shiftVal); // } } if (mUsedRooms[key].empty()) mUsedRooms.erase(key); } //////////////////////////////////////////////////////////////////////////////////////////////////////// osg::Vec2d BuildingManager::shiftPosition(const VCtrl::OBB& obb, const osg::Vec2d& pos, double& shiftValue) { osg::Vec2d position; const double wall = 0.4; osg::Vec3d v(pos[0], pos[1], 0); v = v * obb.mMatrixInverse; v.x() = v.x() + shiftValue; if ( v.x() > (obb.mBB.xMax() - wall) ) { v.x() = obb.mBB.xMin() + wall; v.y() = v.y() + shiftValue; } if ( v.y() > obb.mBB.yMax() - wall) { v.y() = obb.mBB.yMin() + wall; shiftValue = shiftValue / 2.0; } v = v * obb.mMatrix; position.x() = v.x(); position.y() = v.y(); return position; } /////////////////////////////////////////////////////////////////////////////////////////////////////// void BuildingManager::Tick(const dtEntity::Message &message) { // TRACE_LOG(m_logger) << LOGFUNCTION; QMutexLocker locker(&m_mutexShots); const dtEntity::TickMessage &msg = static_cast(message); double simtime = msg.GetSimulationTime(); // for all buildings for (Building *building : mBuildings) { Building &bldg = *building; //new Tick system? for (BuildingDisplay &dspl : bldg.mDisplays) { // FATAL_LOG(m_logger) << "tick display= " << dspl.mShotDisplays.size(); std::vector::iterator itShotDisplay = dspl.mShotDisplays.begin(); while (itShotDisplay != dspl.mShotDisplays.end()) { ShotDisplay &shotdspl = *itShotDisplay; Shot *shot =shotdspl.mShot; bool lifeTimeOver = (shot->lifeTime() <= simtime); bool displayTimeOver = (shot->displayTime() <= simtime); /* FATAL_LOG(m_logger) << "on shotDisplay Tick: ID=" << shot->getId(); FATAL_LOG(m_logger) << "on shotDisplay Tick: getSimeTimeCreated=" << QString::number(shot->getSimeTimeCreated(),'f',3); FATAL_LOG(m_logger) << "on shotDisplay Tick: simtime=" << (int)simtime; FATAL_LOG(m_logger) << "on shotDisplay Tick: lifeTime=" << QString::number(shot->lifeTime(), 'f', 3); FATAL_LOG(m_logger) << "on shotDisplay Tick: lifeTimeOver=" << lifeTimeOver; FATAL_LOG(m_logger) << "on shotDisplay Tick: displayTimeOver=" << displayTimeOver; */ if (displayTimeOver) { dtEntityOSG::LayerAttachPointComponent *lac; if (!mEntityManager->GetComponent(dspl.mAttachPointId, lac)) { WARNING_LOG(m_logger) << "NO sLayerAttachPointComponent"; return; } if( shotdspl.mGeode.valid()) { DEBUG_LOG(m_logger) << "displayTimeOver OVER KILL IT drawables: !" << shotdspl.mGeode->getNumDrawables(); while (shotdspl.mGeode->getNumDrawables() > 0) shotdspl.mGeode->removeDrawables(0, shotdspl.mGeode->getNumDrawables()); bool ret = lac->GetNode()->asGroup()->removeChild(shotdspl.mGeode); DEBUG_LOG(m_logger) << "displayTimeOver OVER KILL IT! result: " << ret; /* assert(shotdspl.mGeode->getParent(0)); while (shotdspl.mGeode.valid() && shotdspl.mGeode->getNumParents() > 0) { shotdspl.mGeode->getParent(0)->removeChild(shotdspl.mGeode); } assert(shotdspl.mGeode->getNumParents() == 0); */ dtEntity::GetWindowInterface()->RequestRedraw(dspl.mContextId); } } if (lifeTimeOver) { DEBUG_LOG(m_logger) << "lifeTimeOver OVER ERASE IT!"; itShotDisplay = dspl.mShotDisplays.erase(itShotDisplay); } else ++itShotDisplay; } //while } //for /* // for all shots in building std::vector::iterator itShot = bldg.mShots.begin(); while (itShot != bldg.mShots.end()) { Shot *shot = *itShot; // Shot still visible and lifespan is not over? Nothing to do, keep current shot visualization bool lifeTimeOver = (shot->lifeTime() <= simtime); // DEBUG_LOG(m_logger) << LOGFUNCTION << " lifeTimeOver = " << lifeTimeOver << " shot->lifeTime() = " << (int)shot->lifeTime() << " simtime = " << (int)simtime; if ((shot->displayTime() > simtime) && !lifeTimeOver) { ++itShot; // DEBUG_LOG(m_logger) << LOGFUNCTION << " continue...."; continue; } // for all building views for (BuildingDisplay &dspl : bldg.mDisplays) { // DEBUG_LOG(m_logger) << LOGFUNCTION << " BuildingDisplay.mContextId = " << dspl.mContextId << ", dspl.mShotDisplays.size()=" << dspl.mShotDisplays.size(); // find shot display std::vector::iterator itShotDisplay = dspl.mShotDisplays.begin(); while (itShotDisplay != dspl.mShotDisplays.end()) { ShotDisplay &shotdspl = *itShotDisplay; bool remove = false; if (shotdspl.mShot == shot) { if (shotdspl.mGeode.valid()) { DEBUG_LOG(m_logger) <getParent(0); shotdspl.mShot->setDisplayTime(0); assert(parent); while (shotdspl.mGeode.valid() && shotdspl.mGeode->getNumParents() > 0) { shotdspl.mGeode->getParent(0)->removeChild(shotdspl.mGeode); } assert(shotdspl.mGeode->getNumParents() == 0); dtEntity::GetWindowInterface()->RequestRedraw(dspl.mContextId); } if (lifeTimeOver) { itShotDisplay = dspl.mShotDisplays.erase(itShotDisplay); } else ++itShotDisplay; } else ++itShotDisplay; } // while } // for if (lifeTimeOver) { // Remove shot completely itShot = bldg.mShots.erase(itShot); delete shot; } else ++itShot; } */ } } bool BuildingManager::getFloorOfRoom(const std::string &bdlgFeatureId, int roomNumber, int &floorOut) const { floorOut = -999; auto it = mRoomToFloorPerBldg.find(bdlgFeatureId); if (it == mRoomToFloorPerBldg.end()) return false; const std::map &roomToFloor = it->second; { auto itR = roomToFloor.find(roomNumber); if (itR == roomToFloor.end()) return false; floorOut = itR->second; } return true; } void BuildingManager::updateShotDuration(unsigned int newDuration_s) { DEBUG_LOG(m_logger) << LOGFUNCTION << " newDuration_s= " << newDuration_s; QMutexLocker locker(&m_mutexShots); // for all buildings for (Building *building : qAsConst(mBuildings)) { // for all shots in building for (Shot *pShot : building->mShots) pShot->setDisplayTime(newDuration_s); if (!building->mDisplays.empty()) { BuildingDisplay buildingDisplay = building->mDisplays.front(); DEBUG_LOG(m_logger) << LOGFUNCTION << " buildingDisplay.mContextId " << buildingDisplay.mContextId; updateShots(*building, buildingDisplay); dtEntity::GetWindowInterface()->RequestRedraw(buildingDisplay.mContextId); } } } void BuildingManager::setCallbackCamPosChanged(const CallbackCamPosChanged &cb) { mCbCamPosChanged = cb; for (auto building : mBuildings) { for (auto &bd : building->mDisplays) { if (bd.pMotion) bd.pMotion->m_callbackCamPosChanged = mCbCamPosChanged; } } } //////////////////////////////////////////////////////////////////////////////// /// \brief BuildingManager::setCameraPosition /// Setting the camera position for a given Building (respectively its contextid) /// is only possible, if an actual view has been opened by calling BuildingManager::displayBuilding. /// Otherwise, no camera component is available. /// \return bool BuildingManager::setCameraPosition(unsigned int contextid, const BuildingViewer::CameraViewPosition& pos) { BuildingDisplay* pBuildingDisplay = getBuildingDisplay(contextid); if (!pBuildingDisplay) return false; dtEntityOSG::CameraComponent *pCamComp = nullptr; bool success = mEntityManager->GetComponent(pBuildingDisplay->mCameraId, pCamComp); if (!success || !pCamComp) return false; pCamComp->SetOrthoLeft(pos.m_left); pCamComp->SetOrthoRight(pos.m_right); pCamComp->SetOrthoBottom(pos.m_bottom); pCamComp->SetOrthoTop(pos.m_top); pCamComp->UpdateProjectionMatrix(); pCamComp->UpdateViewMatrix(); dtEntity::GetWindowInterface()->RequestRedraw(contextid); return true; } bool BuildingManager::getCameraPosition(unsigned int contextid, BuildingViewer::CameraViewPosition& pos) { BuildingDisplay* pBuildingDisplay = getBuildingDisplay(contextid); if (!pBuildingDisplay) return false; dtEntityOSG::CameraComponent *pCamComp = nullptr; bool success = mEntityManager->GetComponent(pBuildingDisplay->mCameraId, pCamComp); if (!success || !pCamComp) return false; double znear, zfar; // Unused pCamComp->GetCamera()->getProjectionMatrixAsOrtho(pos.m_left, pos.m_right, pos.m_bottom, pos.m_top, znear, zfar); return true; } bool BuildingManager::setDefaultCameraPosition(unsigned int contextid) { BuildingDisplay *pBuildingDisplay = getBuildingDisplay(contextid); if (!pBuildingDisplay) return false; Building *building = nullptr; for (Building *b : mBuildings) { for (const auto &dspl : b->mDisplays) { if (&dspl == pBuildingDisplay) { building = b; break; } } } if (!building) return false; repositionCamera(*building, *pBuildingDisplay); return true; } //////////////////////////////////////////////////////////////////////////////// /// \brief BuildingManager::displayBuilding /// Wrapper method. /// Displays the desired building and checks whether the user tried to set /// the building's room name before. If there are existing room names /// they are additionally set after creating the building with 'displayBuilding' bool BuildingManager::displayBuilding(const std::string& featureId, unsigned int contextid, dtEntity::EntityId buildingid, const osg::Vec4 &bgcolor, ViewMode viewmode, bool exactPos) { if (!displayBuilding(contextid, buildingid, bgcolor, viewmode, exactPos)) return false; Building *building = nullptr; for (Building *b : mBuildings) { if (b->mWorldBuildingId == buildingid) { building = b; break; } } assert(building); auto it = m_mapBuildingRoomNames.find(featureId); if(it == m_mapBuildingRoomNames.end()) return false; const std::map& mapRoomNames = it->second; for(const auto& e : mapRoomNames) setRoomDisplayName(building, e.first, e.second); return true; } //////////////////////////////////////////////////////////////////////////////// bool BuildingManager::displayBuilding(unsigned int contextid, dtEntity::EntityId buildingid, const osg::Vec4& bgcolor, ViewMode viewmode, bool exactPos) { DEBUG_LOG (m_logger ) << "BuildingManager::displayBuilding"; mExactPos = exactPos; dtEntity::EntityManager &em = *mEntityManager; Building *building = nullptr; for (Building *b : mBuildings) { if (b->mWorldBuildingId == buildingid) { building = b; break; } } assert(building); if (!building) return false; // create new BuildingDisplay entry. building->mDisplays.emplace_back(BuildingDisplay(contextid, viewmode)); BuildingDisplay &buildingDisplay = building->mDisplays.back(); // open 3d display window dtEntity::WindowCreatedMessage msg; msg.SetName("Building " + std::to_string(buildingid)); msg.SetContextId(contextid); em.EmitMessage(msg); std::string viewname = "Context_" + std::to_string(buildingDisplay.mContextId); dtEntity::MapSystem *mapsys; if (!em.GetES(mapsys)) return false; // create attach point entity { dtEntity::Entity *e; if (!em.CreateEntity(e)) return false; buildingDisplay.mAttachPointId = e->GetId(); dtEntity::MapComponent *map; if (!e->CreateComponent(map)) return false; map->SetEntityName(viewname); map->SetMapName("attach"); map->SetUniqueId(viewname); dtEntityOSG::LayerAttachPointComponent *attach; if (!e->CreateComponent(attach)) return false; attach->SetName(dtEntity::SID(viewname)); mapsys->AddToScene(e->GetId()); } // split the model { ModelSplitterComponent *msc; bool success = em.GetComponent(buildingid, msc); assert(success); if (!success) return false; std::vector ids; std::vector heights; std::vector floors; msc->Split(ids, heights, floors, dtEntity::SID(viewname)); // add floors / levels to building for (unsigned int i = 0; i < ids.size(); ++i) { dtEntity::EntityId id = ids[i]; int floorId = floors[i]; float height = heights[i]; mapsys->AddToScene(id); dtEntityOSG::StaticMeshComponent *smc; success = em.GetComponent(id, smc); assert(success); if (!success) continue; osg::ComputeBoundsVisitor v; smc->GetNode()->accept(v); buildingDisplay.mWorldBounds.expandBy(v.getBoundingBox()); buildingDisplay.mLevels.emplace_back(BuildingLevel(id, floorId, height, v.getBoundingBox())); // for coloring rooms by destruction value RoomDestructionComponent *btc; if (em.CreateComponent(id, btc)) btc->OnEnteredWorld(); // prepare meshes for transparency ClearVertexColorsVisitor vcv; smc->GetNode()->accept(vcv); } } // create camera and motion model { dtEntity::Entity *cam; if (!em.CreateEntity(cam)) return false; buildingDisplay.mCameraId = cam->GetId(); dtEntityOSG::CameraComponent *camcomp; if (!cam->CreateComponent(camcomp)) return false; camcomp->SetContextId(contextid); camcomp->SetLayerAttachPoint(dtEntity::SID(viewname)); camcomp->SetClearColor(bgcolor); camcomp->SetProjectionMode(dtEntityOSG::CameraComponent::ModeOrthoId); camcomp->Finished(); OrthoMotionModelComponent *motion; if (!cam->CreateComponent(motion)) return false; motion->SetContextId(contextid); motion->Finished(); buildingDisplay.pMotion = motion; buildingDisplay.pMotion->m_callbackCamPosChanged = mCbCamPosChanged; dtEntity::MapComponent *mapcomp; if (!cam->CreateComponent(mapcomp)) return false; std::string name = "cam_" + std::to_string(contextid); mapcomp->SetEntityName(name); mapcomp->SetUniqueId(name); mapcomp->SetMapName("cams"); mapcomp->Finished(); mapsys->AddToScene(cam->GetId()); } // create light source { dtEntity::Entity *light; if (!em.CreateEntity(light)) return false; buildingDisplay.mLightId = light->GetId(); dtEntityOSG::LightComponent *lightcomp; if (!light->CreateComponent(lightcomp)) return false; lightcomp->SetVec4(dtEntityOSG::LightComponent::PositionId, osg::Vec4(0.8f, 0.2f, 1, 0)); lightcomp->SetVec4(dtEntityOSG::LightComponent::DiffuseId, osg::Vec4(1, 1, 1, 1)); lightcomp->SetVec4(dtEntityOSG::LightComponent::AmbientId, osg::Vec4(0.5f, 0.5f, 0.5f, 1.0f)); dtEntity::MapComponent *mapc; if (!light->CreateComponent(mapc)) return false; std::string name = "light_" + std::to_string(contextid); mapc->SetEntityName(name); mapc->SetUniqueId(name); mapc->SetMapName("lights"); dtEntityOSG::LayerComponent *layer; if (!light->CreateComponent(layer)) return false; layer->SetAttachedComponent(dtEntityOSG::LightComponent::TYPE); layer->SetLayer(dtEntity::SID(viewname)); layer->Finished(); mapsys->AddToScene(light->GetId()); } applyViewModeToBuildingParts(*building, buildingDisplay); repositionCamera(*building, buildingDisplay); // add existing players to view { PlayerSystem *ps; if (mEntityManager->GetES(ps)) { for (const auto &i : *ps) { PlayerComponent *pc = i.second; // if they are in this building if (dtEntity::GetStringFromSID(pc->GetCurrentCellGroup()) == building->mNavMeshGrpName) { IconComponent *ic; if (mEntityManager->GetComponent(i.first, ic)) addPlayerToBuilding(*building, buildingDisplay, i.first, pc->GetColor(), ic->GetPath(), ic->GetTranslation(), ic->GetVisible()); } } } } // add existing shot lines to view { QMutexLocker locker(&m_mutexShots); if (!building->mShots.empty()) { for (Shot *s : building->mShots) { ShotDisplay sd; sd.mShot = s; buildingDisplay.mShotDisplays.push_back(std::move(sd)); } updateShots(*building, buildingDisplay); } } // add existing device icons to view { DEBUG_LOG(m_logger) << "mDevices.size = " << building->mDevices.size(); if (!building->mDevices.empty()) { for (Device *d : building->mDevices) { DeviceDisplay sd; sd.mDevice = d; sd.mWorldPlayerId = d->mWorldPlayerId; buildingDisplay.mDeviceDisplays.push_back(std::move(sd)); } updateDevices(*building, buildingDisplay); } } // apply room destruction values { RoomDestructionComponent *btc1; if (em.GetComponent(buildingid, btc1)) { const RoomDestructionComponent::RoomDestructions &rd = btc1->GetRoomDestructions(); for (const BuildingLevel &level : buildingDisplay.mLevels) { RoomDestructionComponent *btc2; if (mEntityManager->GetComponent(level.mEntityId, btc2)) { if (!btc2) continue; for (const auto &destr : rd) btc2->SetRoomDestructionValue(destr.first, destr.second); } } } } // create names if(building->mNames.empty()) { createRoomNames(buildingid, *building); } // add roomnames to view { if(!building->mNames.empty()) { for (Name *n : building->mNames) { NameDisplay nd; nd.mName = n; nd.mWorldPlayerId = n->mWorldPlayerId; nd.visible = false; buildingDisplay.mNameDisplays.push_back(std::move(nd)); } updateNames(*building, buildingDisplay); } } return true; } //////////////////////////////////////////////////////////////////////////////// void BuildingManager::closeBuildingDisplay(unsigned int contextid) { dtEntity::MapSystem* mapsys; if (!mEntityManager->GetES(mapsys)) return; for (Building *building : mBuildings) { for (auto it = building->mDisplays.begin(); it != building->mDisplays.end(); ++it) { const BuildingDisplay &bd = (*it); if (bd.mContextId == contextid) { osgViewer::CompositeViewer* cv = static_cast(dtEntityOSG::GetOSGSystemInterface()->GetViewer()); cv->stopThreading(); // remove view from CompositeViewer osgViewer::ViewerBase::Cameras cams; cv->getCameras(cams); for (osg::Camera *c : cams) { if (c->getGraphicsContext()->getState()->getContextID() == contextid) { cv->removeView(dynamic_cast(c->getView())); } } cv->startThreading(); // remove all level entities for (const BuildingLevel &level : bd.mLevels) { dtEntity::EntityId eid = level.mEntityId; mapsys->RemoveFromScene(eid); mEntityManager->KillEntity(eid); } mapsys->RemoveFromScene(bd.mLightId); mEntityManager->KillEntity(bd.mLightId); mapsys->RemoveFromScene(bd.mCameraId); mEntityManager->KillEntity(bd.mCameraId); mapsys->RemoveFromScene(bd.mAttachPointId); mEntityManager->KillEntity(bd.mAttachPointId); // remove all players on this building for (const PlayerDisplay &pd : bd.mPlayers) { mapsys->RemoveFromScene(pd.mLocalPlayerId); mEntityManager->KillEntity(pd.mLocalPlayerId); } for (const DeviceDisplay &pd : bd.mDeviceDisplays) { mapsys->RemoveFromScene(pd.mLocalPlayerId); mEntityManager->KillEntity(pd.mLocalPlayerId); } // remove all names on this building for (const NameDisplay &nd : bd.mNameDisplays) { mapsys->RemoveFromScene(nd.mLocalPlayerId); mEntityManager->KillEntity(nd.mLocalPlayerId); } building->mDisplays.erase(it); return; } } } } //////////////////////////////////////////////////////////////////////////////// void BuildingManager::addBuilding(dtEntity::EntityId buildingid, const std::string &featureId) { Building *b = new Building; b->mWorldBuildingId = buildingid; b->mFeatureId = featureId; mBuildings.push_back(b); // get transform matrix of building in world space. dtEntityOSG::PositionAttitudeTransformComponent *pat; mEntityManager->GetComponent(buildingid, pat); if (!pat) return; b->mLocalToWorld.setRotate(pat->GetAttitude()); b->mLocalToWorld.setTrans(pat->GetTranslation()); b->mWorldToLocal = osg::Matrix::inverse(b->mLocalToWorld); // get BuildingID (navmesh group name). Used to determine if a player is inside this building VCtrl::ModelSplitterComponent *msc; mEntityManager->GetComponent(buildingid, msc); if (!msc) return; b->mNavMeshGrpName = dtEntity::GetStringFromSID(msc->GetBuildingId()); } //////////////////////////////////////////////////////////////////////////////// BuildingDisplay *BuildingManager::getBuildingDisplay(unsigned int contextid) { for (Building *b : mBuildings) { for (BuildingDisplay &bd : b->mDisplays) { if (bd.mContextId == contextid) return &bd; } } return nullptr; } //////////////////////////////////////////////////////////////////////////////// // get eye direction in isometric mode. // when rotation is locked then this is a static direction, // else it is map angle relative to world space north osg::Vec3 getIsoEyeDir(const Building &bd, const BuildingDisplay &d, float maprotation) { if (d.mLockRotation) { osg::Vec3 eyedir(0, 1, -0.7f); eyedir = bd.mWorldToLocal.getRotate() * eyedir; eyedir.normalize(); eyedir = osg::Quat(-osg::DegreesToRadians(maprotation), osg::Vec3(0, 0, 1)) * eyedir; return eyedir; } else { osg::Vec3 eye = osg::Vec3(-1, 1, -1); eye.normalize(); return eye; } } //////////////////////////////////////////////////////////////////////////////// // place building parts according to projection mode (iso or ortho) void BuildingManager::applyViewModeToBuildingParts(const Building &bldg, BuildingDisplay &dspl) const { dtEntity::EntityManager &em = *mEntityManager; // recalculate bounds dspl.mLocalBounds = osg::BoundingBox(); float maprotation = dspl.mLockRotation ? mMapRotation : 0; switch (dspl.mViewMode) { case ViewMode::ISOMETRIC: { // place building parts stacked over each other in z dir. // Also update local bounds. float h = dspl.mWorldBounds.radius() * osg::PI_2; int count = 0; for (BuildingLevel &lvl : dspl.mLevels) { dtEntityOSG::PositionAttitudeTransformComponent *pat; if (!em.GetComponent(lvl.mEntityId, pat)) { assert(false); continue; } float height = h * count++; lvl.mLocalToLevel.makeTranslate(osg::Vec3d(0, 0, height - lvl.mHeightAboveGround)); pat->SetPosition(osg::Vec3d(0, 0, height - lvl.mHeightAboveGround)); osg::ComputeBoundsVisitor v; pat->GetNode()->accept(v); dspl.mLocalBounds.expandBy(v.getBoundingBox()); } break; } case ViewMode::ORTHOGRAPHIC: { // place building parts beside each other in y dir. // Parts are each rotated individually. Rotated bounding box // defines the distance between building parts. // Also update local bounds. float h = 0; for (BuildingLevel &lvl : dspl.mLevels) { dtEntityOSG::PositionAttitudeTransformComponent *pat; if (!em.GetComponent(lvl.mEntityId, pat)) { assert(false); continue; } pat->SetPosition(osg::Vec3d()); osg::ComputeBoundsVisitor v; pat->GetNode()->accept(v); float w = v.getBoundingBox().yMax() - v.getBoundingBox().yMin(); h += w * 0.6f; osg::Vec3d pos(0, h - v.getBoundingBox().center()[1], -lvl.mHeightAboveGround); pat->SetPosition(pos); lvl.mLocalToLevel.makeTranslate(osg::Vec3d(0, h - v.getBoundingBox().center()[1], 0)); h += w * 0.6f; osg::Quat q(0, 0, 0, 1); if (dspl.mLockRotation) { q = bldg.mLocalToWorld.getRotate(); q *= osg::Quat(osg::DegreesToRadians(maprotation), osg::Vec3(0, 0, 1)); lvl.mLocalToLevel.setRotate(q); } pat->SetAttitude(q); osg::ComputeBoundsVisitor v2; pat->GetNode()->accept(v2); dspl.mLocalBounds.expandBy(v2.getBoundingBox()); } break; } default: LOG_ERROR("Unknown view mode set!"); return; } dtEntity::GetWindowInterface()->RequestRedraw(dspl.mContextId); } //////////////////////////////////////////////////////////////////////////////// // position camera according to projection mode and map rotation value. void BuildingManager::repositionCamera(const Building &bd, const BuildingDisplay &dspl, bool useOriginViewPos) { float maprotation = (dspl.mLockRotation) ? mMapRotation : 0; osg::Vec3d eyedir; osg::Vec3d up; switch (dspl.mViewMode) { case ViewMode::ISOMETRIC: eyedir = getIsoEyeDir(bd, dspl, maprotation); up.set(0, 0, 1); break; case ViewMode::ORTHOGRAPHIC: eyedir.set(0, 0, -1); up.set(0, 1, 0); break; } // fit ortho camera projection to building height (eye direction dependent) osg::Vec3 pxa(eyedir ^ up); osg::Vec3 pya(pxa ^ eyedir); pya.normalize(); osg::Plane py(pya, dspl.mLocalBounds.center()); float ydist = 0; for (unsigned int i = 0; i < 8; ++i) { osg::Vec3 corner = dspl.mLocalBounds.corner(i); float dist = fabs(py.distance(corner)); ydist = std::max(ydist, dist); } ydist *= 1.1f; // add some margin // Get camera entity and component dtEntityOSG::CameraComponent *camcomp; if (!mEntityManager->GetComponent(dspl.mCameraId, camcomp)) return; /* * Save old view state. * If called for the first time for dspl.mContextId the values left, right, ... will be INF. * In this case 'initialCameraPosWasSet' will be false. */ bool initialCameraPosWasSet = (m_setInitialCameraPositionSet.find(dspl.mContextId) != m_setInitialCameraPositionSet.end()); double left, right, bottom, top, znear, zfar; camcomp->GetCamera()->getProjectionMatrixAsOrtho(left, right, bottom, top, znear, zfar); // Request window properties int x, y, w, h; dtEntity::GetWindowInterface()->GetWindowGeometry(dspl.mContextId, x, y, w, h); /* * Jump back to origin and draw building according to the window properties. * This step is always required to keep the building's aspect ratio according to * the window size. */ camcomp->SetOrthoLeft(-ydist * w / h); camcomp->SetOrthoRight(ydist * w / h); camcomp->SetOrthoBottom(-ydist); camcomp->SetOrthoTop(ydist); camcomp->SetOrthoZNear(-1000); camcomp->SetOrthoZFar(1000); m_setInitialCameraPositionSet.insert(dspl.mContextId); camcomp->SetPosition(dspl.mLocalBounds.center()); camcomp->SetEyeDirection(eyedir); camcomp->SetUp(up); camcomp->UpdateProjectionMatrix(); camcomp->UpdateViewMatrix(); /* * If 'useOriginViewPos' is false and an initial camera position has been set * we move the camera back from the origin to the last position the user has set with the mouse/keyboard. */ if(!useOriginViewPos && initialCameraPosWasSet) { camcomp->SetOrthoLeft(left); camcomp->SetOrthoRight(right); camcomp->SetOrthoBottom(bottom); camcomp->SetOrthoTop(top); camcomp->UpdateViewMatrix(); } // move light source dtEntityOSG::LightComponent *light; if (!mEntityManager->GetComponent(dspl.mLightId, light)) return; osg::Vec3 dir(0, 0, -1); if (dspl.mViewMode == ViewMode::ISOMETRIC) dir = pxa; light->SetVec3(dtEntityOSG::LightComponent::DirectionId, dir); } //////////////////////////////////////////////////////////////////////////////// void BuildingManager::updatePlayerPositions(const Building &bd, BuildingDisplay &dspl) { LOG_DEBUG("BuildingManager::updatePlayerPositions: " << " mBuildingDisplayName" << bd.mBuildingDisplayName); // reposition all players currently displayed in building for (PlayerDisplay &pd : dspl.mPlayers) { IconComponent *ic; mEntityManager->GetComponent(pd.mWorldPlayerId, ic); if (!ic) continue; int roomNumber = pd.mIcon->GetRoomNumber(); LOG_DEBUG("BuildingManager::updatePlayerPositions: " << ", pd.mWorldPlayerId" << pd.mWorldPlayerId << ", roomNumber" << roomNumber); pd.mIcon->SetTranslation(remapPosition(bd, dspl, ic->GetTranslation(), roomNumber)); pd.mIcon->SetOriginAtBottom(dspl.mViewMode == ViewMode::ISOMETRIC); } } //////////////////////////////////////////////////////////////////////////////// void BuildingManager::changeViewMode(unsigned int contextId, ViewMode viewmode, bool useOriginViewPos) { for (Building *bldg : mBuildings) { for (BuildingDisplay &dspl : bldg->mDisplays) { if (dspl.mContextId == contextId) { if (dspl.mViewMode != viewmode) { dspl.mViewMode = viewmode; applyViewModeToBuildingParts(*bldg, dspl); updateShots(*bldg, dspl); updatePlayerPositions(*bldg, dspl); updateDevices(*bldg, dspl); updateNames(*bldg, dspl); applyMapRotation(*bldg, dspl); } repositionCamera(*bldg, dspl, useOriginViewPos); dtEntity::GetWindowInterface()->RequestRedraw(contextId); } } } } //////////////////////////////////////////////////////////////////////////////// void BuildingManager::applyMapRotation(const Building &bd, BuildingDisplay &dspl) { dtEntityOSG::CameraComponent *camcomp; if (!mEntityManager->GetComponent(dspl.mCameraId, camcomp)) return; if (dspl.mViewMode == VCtrl::ViewMode::ISOMETRIC) { camcomp->SetEyeDirection(getIsoEyeDir(bd, dspl, mMapRotation)); camcomp->UpdateViewMatrix(); dtEntity::GetWindowInterface()->RequestRedraw(dspl.mContextId); } else { // have to rotate all building parts individually applyViewModeToBuildingParts(bd, dspl); updatePlayerPositions(bd, dspl); updateDevices(bd, dspl); updateNames(bd, dspl); camcomp->UpdateViewMatrix(); } updateShots(bd, dspl); } //////////////////////////////////////////////////////////////////////////////// void BuildingManager::lockViewRotationToMapRotation(unsigned int contextId, bool lock) { for (Building *bldg : mBuildings) { for (BuildingDisplay &dspl : bldg->mDisplays) { if (dspl.mContextId == contextId && dspl.mLockRotation != lock) { dspl.mLockRotation = lock; repositionCamera(*bldg, dspl, false); applyViewModeToBuildingParts(*bldg, dspl); applyMapRotation(*bldg, dspl); repositionCamera(*bldg, dspl, false); break; } } } } //////////////////////////////////////////////////////////////////////////////// void BuildingManager::setPlayerDisplayMode(unsigned int contextId, PlayerDisplayMode mode) { BuildingDisplay *bd = getBuildingDisplay(contextId); if (!bd) { LOG_ERROR("Could not lock view rotation, view with this context id does not exist!"); return; } if (bd->mPlayerDisplayMode == mode) return; bd->mPlayerDisplayMode = mode; for (PlayerDisplay &pd : bd->mPlayers) pd.mIcon->SetShowDot(mode == PlayerDisplayMode::Dots); dtEntity::GetWindowInterface()->RequestRedraw(bd->mContextId); } //////////////////////////////////////////////////////////////////////////////// void BuildingManager::setWallTransparency(unsigned int contextId, float value) { BuildingDisplay *bd = getBuildingDisplay(contextId); if (!bd) { LOG_ERROR("Could not lock view rotation, view with this context id does not exist!"); return; } //LOG_ALWAYS("Transparency: " << value) SetWallsTransparentVisitor v(value); for (const BuildingLevel &lvl : bd->mLevels) { dtEntityOSG::StaticMeshComponent *smc; if (!mEntityManager->GetComponent(lvl.mEntityId, smc)) { assert(false); continue; } smc->GetNode()->accept(v); } dtEntity::GetWindowInterface()->RequestRedraw(bd->mContextId); } //////////////////////////////////////////////////////////////////////////////// float BuildingManager::getMapRotation() const { return mMapRotation; } //////////////////////////////////////////////////////////////////////////////// void BuildingManager::setMapRotation(float angle) { if (qFuzzyCompare(mMapRotation, angle)) return; mMapRotation = angle; for (Building *building : mBuildings) { for (BuildingDisplay &bd : building->mDisplays) { if (bd.mLockRotation) applyMapRotation(*building, bd); } } } //////////////////////////////////////////////////////////////////////////////// void BuildingManager::openOverview(unsigned int contextid) { dtEntity::GetDebugDrawInterface()->SetEnabled(true); dtEntity::WindowCreatedMessage msg; msg.SetName("Overview"); msg.SetContextId(contextid); mEntityManager->EmitMessage(msg); dtEntity::Entity *camera; if (!mEntityManager->CreateEntity(camera)) return; dtEntityOSG::CameraComponent *camcomp; if (!camera->CreateComponent(camcomp)) return; camcomp->SetClearColor(osg::Vec4(0.2f, 0.2f, 0.2f, 1)); camcomp->SetContextId(contextid); camcomp->SetEyeDirection(osg::Vec3(0, -1, 0)); camcomp->SetLayerAttachPoint(dtEntity::SID("root")); camcomp->SetAspectRatio(1.3); camcomp->Finished(); dtEntity::MapComponent *cammap; if (!camera->CreateComponent(cammap)) return; cammap->SetEntityName("cam_overview"); cammap->SetUniqueId("cam_overview"); cammap->SetMapName("cams"); MotionModelComponent *motion; if (!camera->CreateComponent(motion)) return; motion->Finished(); dtEntity::MapSystem *ms; if (mEntityManager->GetES(ms)) ms->AddToScene(camera->GetId()); } //////////////////////////////////////////////////////////////////////////////// void BuildingManager::addPlayerToBuilding(const Building &bd, BuildingDisplay &dspl, dtEntity::EntityId eid, const osg::Vec4 &color, const std::string &symbol, const osg::Vec3d &pos, bool visible) { PlayerDisplay pd; pd.mWorldPlayerId = eid; dtEntity::Entity *e; if (!mEntityManager->CreateEntity(e)) return; pd.mLocalPlayerId = e->GetId(); dtEntity::MapComponent *mc; if (!e->CreateComponent(mc)) return; std::string en = "ctx_" + std::to_string(dspl.mContextId) + "_plr_" + std::to_string(pd.mWorldPlayerId); mc->SetEntityName(en); mc->SetUniqueId(en); mc->SetMapName("proxies"); if (!e->CreateComponent(pd.mIcon)) return; pd.mIcon->SetVisible(visible); pd.mIcon->SetPath(symbol); pd.mIcon->SetColor(color); pd.mIcon->SetVOffset(0.0f); int roomNumber = pd.mIcon->GetRoomNumber(); pd.mIcon->SetTranslation(remapPosition(bd, dspl, pos, roomNumber)); pd.mIcon->SetOriginAtBottom(dspl.mViewMode == ViewMode::ISOMETRIC); pd.mIcon->SetShowDot(dspl.mPlayerDisplayMode == PlayerDisplayMode::Dots); pd.mIcon->Finished(); std::string viewname = "Context_" + std::to_string(dspl.mContextId); dtEntityOSG::LayerComponent *lc; if (!e->CreateComponent(lc)) return; lc->SetAttachedComponent(IconComponent::TYPE); lc->SetLayer(dtEntity::SID(viewname)); dtEntity::MapSystem *ms; if (!mEntityManager->GetES(ms)) return; ms->AddToScene(e->GetId()); dspl.mPlayers.push_back(std::move(pd)); dtEntity::GetWindowInterface()->RequestRedraw(dspl.mContextId); } //////////////////////////////////////////////////////////////////////////////// void BuildingManager::addPlayerToBuildings(const std::string &navmeshgrpname, dtEntity::EntityId eid, const osg::Vec4 &color, const std::string &symbol, const osg::Vec3d &pos, bool visible) { for (Building *building : mBuildings) { if (building->mNavMeshGrpName != navmeshgrpname) continue; for (BuildingDisplay &bd : building->mDisplays) addPlayerToBuilding(*building, bd, eid, color, symbol, pos, visible); } } //////////////////////////////////////////////////////////////////////////////// void BuildingManager::removePlayerFromBuilding(const std::string &navmeshgrpname, dtEntity::EntityId eid, const std::string& roomname) { if (mExactPos == false) deleteRoomUsagePlayer(navmeshgrpname, roomname, eid); for (Building *building : mBuildings) { if (building->mNavMeshGrpName != navmeshgrpname) continue; for (BuildingDisplay &bd : building->mDisplays) { for (auto it = bd.mPlayers.begin(); it != bd.mPlayers.end(); ++it) { const PlayerDisplay &pd = *it; if (pd.mWorldPlayerId == eid) { dtEntity::MapSystem *ms; if (mEntityManager->GetES(ms)) ms->RemoveFromScene(pd.mLocalPlayerId); mEntityManager->KillEntity(pd.mLocalPlayerId); bd.mPlayers.erase(it); dtEntity::GetWindowInterface()->RequestRedraw(bd.mContextId); break; } } } } } //////////////////////////////////////////////////////////////////////////////// void BuildingManager::updatePlayerPosition(const std::string &navmeshgrpname, dtEntity::EntityId eid, const osg::Vec3d &pos, int roomNumber) { LOG_DEBUG("BuildingManager::updatePlayerPosition: " << "navmeshgrpname='"<< navmeshgrpname << "'" << ", eid/WorldPlayerId="<< eid << ", pos: x=" << pos.x() << " y=" << pos.y() << " z=" << pos.z() << ", roomNumber="<< roomNumber); for (Building *building : mBuildings) { if (building->mNavMeshGrpName != navmeshgrpname) continue; for (BuildingDisplay &bd : building->mDisplays) { for (PlayerDisplay &pd : bd.mPlayers) { if (pd.mWorldPlayerId == eid) { pd.mIcon->SetTranslation(remapPosition(*building, bd, pos, roomNumber)); pd.mIcon->SetRoomNumber(roomNumber); dtEntity::GetWindowInterface()->RequestRedraw(bd.mContextId); break; } } } } } //////////////////////////////////////////////////////////////////////////////// osg::Vec3d BuildingManager::remapPosition(const Building &bd, const BuildingDisplay &dspl, const osg::Vec3d &world, int roomNumber) { DEBUG_LOG(m_logger) << LOGFUNCTION << " mFeatureId = " << bd.mFeatureId.c_str() << ", roomNumber= " << roomNumber; if (roomNumber > 0) { // unfortunately the roomNumber/roomName can not be used to retrieve the floor. // Bldg_1106 has floor_-1 with room R_002, 003, 004 ... and floor_-2 has room R_006. // So mRoomToFloorPerBldg is introduced and filled while reading the xml file int floorOut; if (getFloorOfRoom(bd.mFeatureId, roomNumber, floorOut)) { for (const auto &level : dspl.mLevels) { if (level.mFloorId == floorOut) { const osg::Vec3d &l = world * bd.mWorldToLocal; DEBUG_LOG(m_logger) << LOGFUNCTION << " mFeatureId = " << bd.mFeatureId.c_str() << ", roomNumber= " << roomNumber << ", floorOut= " << floorOut << ", world.z()= " << world.z() << ", RET value is =" << (l * level.mLocalToLevel).z(); return l * level.mLocalToLevel; } } // for } } // if (roomNumber > 0) // roomNumber NOT set: use approach of Christian Ehrlicher // (works for excatly horizontal floors) const osg::Vec3d l = world * bd.mWorldToLocal; const size_t numlevels = dspl.mLevels.size(); for (unsigned int i = 0; i < numlevels; ++i) { if (i == (numlevels - 1) || (dspl.mLevels[i + 1].mHeightAboveGround - 0.1f) > l[2]) // l[2] == l.z() { const BuildingLevel &level = dspl.mLevels[i]; DEBUG_LOG(m_logger) << LOGFUNCTION << " remapXPosition" << ", l.z=" << l.z() << ", i=" << i << ", numlevels=" << numlevels << ", (dspl.mLevels[i + 1].mHeightAboveGround - 0.1f)=" << (dspl.mLevels[i + 1].mHeightAboveGround - 0.1f) << ", l[2]=" << l[2] << ", ret=" << (l * level.mLocalToLevel).z(); return l * level.mLocalToLevel; } } LOG_ERROR("Could not remap position!"); return world; } //////////////////////////////////////////////////////////////////////////////// void BuildingManager::setPlayerColor(dtEntity::EntityId eid, const osg::Vec4 &color) { for (Building *building : mBuildings) { for (BuildingDisplay &bd : building->mDisplays) { for (PlayerDisplay &pd : bd.mPlayers) { if (pd.mWorldPlayerId == eid) { pd.mIcon->SetColor(color); dtEntity::GetWindowInterface()->RequestRedraw(bd.mContextId); break; } } } } } //////////////////////////////////////////////////////////////////////////////// void BuildingManager::setPlayerIcon(dtEntity::EntityId eid, const std::string &path) { for (Building *building : mBuildings) { for (BuildingDisplay &bd : building->mDisplays) { for (PlayerDisplay &pd : bd.mPlayers) { if (pd.mWorldPlayerId == eid) { pd.mIcon->SetPath(path); dtEntity::GetWindowInterface()->RequestRedraw(bd.mContextId); break; } } } } } //////////////////////////////////////////////////////////////////////////////// void BuildingManager::reloadPlayerIcons() { for (Building *building : mBuildings) { for (BuildingDisplay &bd : building->mDisplays) { for (PlayerDisplay &pd : bd.mPlayers) pd.mIcon->Reload(); if (bd.mPlayers.size()) dtEntity::GetWindowInterface()->RequestRedraw(bd.mContextId); } } } //////////////////////////////////////////////////////////////////////////////// void BuildingManager::updatePlayerPartyColor(dtEntity::EntityId eid, const VCtrl::Color& col) { for (Building *building : mBuildings) { for (BuildingDisplay &bd : building->mDisplays) { for (PlayerDisplay &pd : bd.mPlayers) { if (pd.mWorldPlayerId == eid) { pd.mIcon->SetColor(osg::Vec4(col.x,col.y,col.z,col.w)); dtEntity::GetWindowInterface()->RequestRedraw(bd.mContextId); break; } } } } } //////////////////////////////////////////////////////////////////////////////// void BuildingManager::setShowPlayerSelection(dtEntity::EntityId eid, bool show) { for (Building *building : mBuildings) { for (BuildingDisplay &bd : building->mDisplays) { for (PlayerDisplay &pd : bd.mPlayers) { if (pd.mWorldPlayerId == eid) { pd.mIcon->SetShowSelection(show); dtEntity::GetWindowInterface()->RequestRedraw(bd.mContextId); break; } } } } } //////////////////////////////////////////////////////////////////////////////// void BuildingManager::setShowHidePlayer(dtEntity::EntityId eid, bool show) { for (Building *building : mBuildings) { for (BuildingDisplay &bd : building->mDisplays) { for (PlayerDisplay &pd : bd.mPlayers) { if (pd.mWorldPlayerId == eid) { if (pd.mIcon->GetVisible() != show) { pd.mIcon->SetVisible(show); dtEntity::GetWindowInterface()->RequestRedraw(bd.mContextId); } break; } } } } } //////////////////////////////////////////////////////////////////////////////// void BuildingManager::setRoomDestructionValue(dtEntity::EntityId eid, const std::string &roomname, float value) { for (const Building *building : mBuildings) { if (building->mWorldBuildingId != eid) continue; for (const BuildingDisplay &bd : building->mDisplays) { bool bRequestRedraw = false; for (const BuildingLevel &level : bd.mLevels) { RoomDestructionComponent *btc; if (mEntityManager->GetComponent(level.mEntityId, btc)) { btc->SetRoomDestructionValue(roomname, value); bRequestRedraw = true; } // if (roomname.compare("2201") == 0) // { // qCritical() << "XXX setRoomDestructionValue: "; // RoomDestructionComponent *btc; // if (mEntityManager->GetComponent(level.mEntityId, btc)) // { // qCritical() << " setRoomDestructionValue: " // << ", roomname=" << roomname.c_str() // << ", value=" << value // << ", level.mEntityId=" << level.mEntityId; // btc->SetRoomDestructionValue("2301", value); // bRequestRedraw = true; // } // } } if (bRequestRedraw) dtEntity::GetWindowInterface()->RequestRedraw(bd.mContextId); } break; } } //////////////////////////////////////////////////////////////////////////////// void BuildingManager::setBuildingDestructionValue(dtEntity::EntityId eid, float value) { for (const Building *building : mBuildings) { if (building->mWorldBuildingId != eid) continue; for (const BuildingDisplay &bd : building->mDisplays) { bool bRequestRedraw = false; for (const BuildingLevel &level : bd.mLevels) { RoomDestructionComponent *btc; if (mEntityManager->GetComponent(level.mEntityId, btc)) { btc->SetBuildingDestructionValue(value); bRequestRedraw = true; } } if (bRequestRedraw) dtEntity::GetWindowInterface()->RequestRedraw(bd.mContextId); } break; } } //////////////////////////////////////////////////////////////////////////////// dtEntity::EntityId BuildingManager::getWorldPlayerIdFromLocalPlayer(dtEntity::EntityId local) const { for (const Building *building : mBuildings) { for (const BuildingDisplay &bd : building->mDisplays) { for (const PlayerDisplay &pd : bd.mPlayers) { if (pd.mLocalPlayerId == local) return pd.mWorldPlayerId; } for (const DeviceDisplay &pd : bd.mDeviceDisplays) { if (pd.mLocalPlayerId == local) return pd.mWorldPlayerId; } } } return 0; } //////////////////////////////////////////////////////////////////////////////// void BuildingManager::removeShoot(int32_t id) { DEBUG_LOG(m_logger) << "removeShoot id=" << id; QMutexLocker locker(&m_mutexShots); for (Building *bldg : mBuildings) { DEBUG_LOG(m_logger) << "Vorher: bldg->mShots.size()=" << bldg->mShots.size(); auto it = std::remove_if(bldg->mShots.begin(), bldg->mShots.end(), [id]( Shot *shot) { if (shot->getId() == id) { //delete shot; const unsigned int duration = 0; shot->setDisplayTime(duration); return true; } return false; }); bool wasRemoved = (it != bldg->mShots.end()); bldg->mShots.erase(it, bldg->mShots.end()); DEBUG_LOG(m_logger) << "Nachher: bldg->mShots.size()=" << bldg->mShots.size(); if (wasRemoved) for (BuildingDisplay &dspl : bldg->mDisplays) { DEBUG_LOG(m_logger) << "wasRemoved id=" << id; updateShots(*bldg, dspl); dtEntity::GetWindowInterface()->RequestRedraw(dspl.mContextId); } } } //////////////////////////////////////////////////////////////////////////////// void BuildingManager::showShoot(int32_t id, dtEntity::EntityId shooter, dtEntity::EntityId target, const dtEntity::Vec3d &shooterPos, const dtEntity::Vec3d &targetPos, int buildingIdSrc, int roomNumberSrc, int buildingIdDst, int roomNumberDst, int lineType, float duration, double simTimeCreated) { PlayerComponent *shooterpc, *targetpc; mEntityManager->GetComponent(shooter, shooterpc); mEntityManager->GetComponent(target, targetpc); if (!shooterpc) // no check for target - may be a building { assert(false); return; } if (mShotOverview == false && targetpc) // only display shots between players in same building { if (shooterpc->GetCurrentCellGroup() != targetpc->GetCurrentCellGroup()) return; } std::string navmeshname = dtEntity::GetStringFromSID(shooterpc->GetCurrentCellGroup()); std::string targetnavmeshname; if (mShotOverview == true && targetpc) targetnavmeshname = dtEntity::GetStringFromSID(targetpc->GetCurrentCellGroup()); QMutexLocker locker(&m_mutexShots); for (Building *bldg : mBuildings) { if ((targetpc == nullptr && target == bldg->mWorldBuildingId) || (bldg->mNavMeshGrpName == navmeshname) || (bldg->mNavMeshGrpName == targetnavmeshname)) { double _simTimeCreated = simTimeCreated != 0 ? simTimeCreated : dtEntity::GetSystemInterface()->GetSimulationTime() ; //float _simTimeCreated = dtEntity::GetSystemInterface()->GetSimulationTime(); Shot* shot = new Shot(id, shooterPos, targetPos, buildingIdSrc, roomNumberSrc, buildingIdDst, roomNumberDst, lineType, shooterpc->GetColor(), static_cast(duration), 120, _simTimeCreated); for(const auto it : bldg->mShots) { if (it->getId() == shot->getId()) { delete shot; return; } } bldg->mShots.push_back(shot); DEBUG_LOG(m_logger) << "XXXX SIM TIME CREATED = " << QString::number(_simTimeCreated, 'f', 5) << " and SIM TIME IS " <GetSimulationTime(), 'f', 5); for (BuildingDisplay &bldgDispl : bldg->mDisplays) { ShotDisplay shotDispl; shotDispl.mShot = shot; DEBUG_LOG(m_logger) << "mShotDisplays.push_back id=" << shot->getId(); bldgDispl.mShotDisplays.push_back(std::move(shotDispl)); updateShots(*bldg, bldgDispl); dtEntity::GetWindowInterface()->RequestRedraw(bldgDispl.mContextId); } if (targetpc == nullptr && target == bldg->mWorldBuildingId) break; } } } //////////////////////////////////////////////////////////////////////////////// void BuildingManager::updateShots(const Building &bd, BuildingDisplay &dspl) { DEBUG_LOG(m_logger) << LOGFUNCTION << " Building= " << bd.mWorldBuildingId; dtEntityOSG::CameraComponent *camcomp; if (!mEntityManager->GetComponent(dspl.mCameraId, camcomp)) return; osg::Vec3 eyedir(camcomp->GetEyeDirection()); dtEntityOSG::LayerAttachPointComponent *lac; if (!mEntityManager->GetComponent(dspl.mAttachPointId, lac)) return; const double simTime = dtEntity::GetSystemInterface()->GetSimulationTime(); DEBUG_LOG(m_logger) << LOGFUNCTION << " dspl.mShotDisplays.size()= " << dspl.mShotDisplays.size() << ", simTime=" << (int)simTime; for (ShotDisplay &shotdspl : dspl.mShotDisplays) { if (!shotdspl.mShot) continue; /* * Only show hitlines when: the current * sim time is >= getSimeTimeCreated * and simdtime <= displayTime * */ const bool bDisplay = (shotdspl.mShot->displayTime() > simTime) && (shotdspl.mShot->simTimeCreated() <= simTime); /* FATAL_LOG(m_logger) << LOGFUNCTION << " AA DURATION= "<< shotdspl.mShot->getDuration(); FATAL_LOG(m_logger) << LOGFUNCTION << " AA SIM TIME= " << (int)simTime << " displayTime= " << (int)shotdspl.mShot->displayTime()<< " (shotdspl.mShot->displayTime() > simTime)= "<< (shotdspl.mShot->displayTime() > simTime); FATAL_LOG(m_logger) << LOGFUNCTION << " AA SIM TIME= " << (int)simTime << " simTimeCreated= " << (int)shotdspl.mShot->getSimeTimeCreated()<< " (shotdspl.mShot->simTimeCreated() <= simTime)= "<< (shotdspl.mShot->simTimeCreated() <= simTime); FATAL_LOG(m_logger) << LOGFUNCTION << " AA shotdspl.mShot->getId()= " << shotdspl.mShot->getId() << ", bDisplay=" << bDisplay << ", shotdspl.mGeode=" << shotdspl.mGeode; */ // NLDEVCTRL-1556 Anzeige der Trefferlinien von aussen auf das Gebäude (Raum) // ändern sich nicht korrekt mit bei der Umschaltung 2D/3D bzw. Nord/Gelände Sicht // Es wird der Geode wieder neu aufgebaut. Dadurch zeigt die Linie an die // richtige Stelle und die Pfeilspitzen ist auch 'schön' while (shotdspl.mGeode.valid() && shotdspl.mGeode->getNumParents() > 0) shotdspl.mGeode->getParent(0)->removeChild(shotdspl.mGeode); shotdspl.mGeode = osg::ref_ptr(); int numParents = -1; if (shotdspl.mGeode) numParents = shotdspl.mGeode->getNumParents(); DEBUG_LOG(m_logger) << LOGFUNCTION << " BB shotdspl.mShot->getId()= " << shotdspl.mShot->getId() << ", shotdspl.mGeode=" << shotdspl.mGeode << ", shotdspl.mGeode->getNumParents=" << numParents << ", bDisplay=" << bDisplay; // does not exist, but should be: if ((!shotdspl.mGeode || shotdspl.mGeode->getNumParents() == 0) && bDisplay) { DEBUG_LOG(m_logger) << "NEW mGeode! id " << shotdspl.mShot->getId(); if (!shotdspl.mGeode) shotdspl.mGeode = new osg::Geode(); osg::StateSet *ss = shotdspl.mGeode->getOrCreateStateSet(); ss->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF); ss->setRenderBinDetails(240001, "RenderBin"); osg::ref_ptr lw = new osg::LineWidth(); DEBUG_LOG(m_logger) << "NEW LINE " << shotdspl.mShot->getLineType(); if (shotdspl.mShot->getLineType() < 2 ) { int stippleSize = 5; if (shotdspl.mShot->getLineType() == 1) //LRF stippleSize = 2; osg::ref_ptr ls = new osg::LineStipple(stippleSize, 0xAAAA); ss->setAttributeAndModes(ls, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE); } //osg::ref_ptr ls = new osg::LineStipple(5, 0xAAAA); //dashed line // ss->setAttributeAndModes(ls, osg::StateAttribute::OFF | osg::StateAttribute::OVERRIDE); // normal LINE lw->setWidth(2); //ss->setAttributeAndModes(lw, osg::StateAttribute::ON); ss->setMode(GL_LIGHTING, osg::StateAttribute::OFF); // lac->GetNode()->asGroup()->addChild(shotdspl.mGeode); // osg::Geometry *gmtr = new osg::Geometry(); shotdspl.mGeode->addDrawable(gmtr); osg::Vec4Array *col = new osg::Vec4Array(1); (*col)[0] = shotdspl.mShot->getColor(); gmtr->setColorArray(col); gmtr->setColorBinding(osg::Geometry::BIND_OVERALL); osg::Vec3d shooterpos = remapPosition(bd, dspl, shotdspl.mShot->getShooterPos(), shotdspl.mShot->getRoomNumberSrc()); osg::Vec3d targetpos = remapPosition(bd, dspl, shotdspl.mShot->getTargetPos(), shotdspl.mShot->getRoomNumberDst()); // make shot lines go to body center height shooterpos[2] += 1; targetpos[2] += 1; DEBUG_LOG(m_logger) << LOGFUNCTION << " shotdspl.mShot->getId()= " << shotdspl.mShot->getId() << ", getShooterPos.z()=" << shotdspl.mShot->getShooterPos().z() << ", BuildingIdSrc=" << shotdspl.mShot->getBuildingIdSrc() << ", RoomNumberSrc=" << shotdspl.mShot->getRoomNumberSrc() << ", shooterpos=" << shooterpos << ", getTargetPos.z()=" << shotdspl.mShot->getTargetPos().z() << ", BuildingIdDst=" << shotdspl.mShot->getBuildingIdDst() << ", RoomNumberDst=" << shotdspl.mShot->getRoomNumberDst() << ", targetpos=" << targetpos; osg::Vec3Array *coords = new osg::Vec3Array(6); (*coords)[0] = shooterpos; (*coords)[1] = targetpos; osg::Vec3d edge = targetpos - shooterpos; edge.normalize(); edge *= 0.8; osg::Vec3d p1 = eyedir ^ edge; p1.normalize(); p1 *= 0.8; (*coords)[2] = p1 - edge * 1.5 + targetpos; (*coords)[3] = targetpos; (*coords)[4] = -p1 - edge * 1.5 + targetpos; (*coords)[5] = targetpos; gmtr->setVertexArray(coords); //gmtr->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::POINTS, 0, 6)); gmtr->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::LINES, 0, 6)); //gmtr->getOrCreateStateSet()->setAttributeAndModes(new osg::Point(10.0f)); //gmtr->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::POINTS, 0, 6)); // lac->GetNode()->asGroup()->addChild(shotdspl.mGeode); } if (bDisplay) lac->GetNode()->asGroup()->addChild(shotdspl.mGeode); } } //////////////////////////////////////////////////////////////////////////////// void BuildingManager::updateDevices(const Building &bd, BuildingDisplay &dspl) { for (DeviceDisplay &ddspl : dspl.mDeviceDisplays) { IconComponent *icon; if (ddspl.mLocalPlayerId == 0) { dtEntity::Entity *e; if (!mEntityManager->CreateEntity(e)) continue; ddspl.mLocalPlayerId = e->GetId(); dtEntity::MapComponent *mc; if (!e->CreateComponent(mc)) continue; std::string en = "ctx_" + std::to_string(dspl.mContextId) + "_plr_" + std::to_string(ddspl.mDevice->mWorldPlayerId); mc->SetEntityName(en); mc->SetUniqueId(en); mc->SetMapName("deviceproxies"); if (!e->CreateComponent(icon)) continue; icon->SetPath(ddspl.mDevice->mPath); icon->SetOriginAtBottom(false); dtEntityOSG::LayerComponent *lc; if (!e->CreateComponent(lc)) continue; std::string viewname = "Context_" + std::to_string(dspl.mContextId); lc->SetAttachedComponent(IconComponent::TYPE); lc->SetLayer(dtEntity::SID(viewname)); dtEntity::MapSystem *ms; if (mEntityManager->GetES(ms)) ms->AddToScene(e->GetId()); } else { if (!mEntityManager->GetComponent(ddspl.mLocalPlayerId, icon)) { assert(false); continue; } } int roomNumber = icon->GetRoomNumber(); icon->SetTranslation(remapPosition(bd, dspl, ddspl.mDevice->mPosition, roomNumber)); icon->Finished(); icon->SetVisible(true); } } //////////////////////////////////////////////////////////////////////////////// void BuildingManager::addDeviceToBuilding(dtEntity::EntityId iconmainid, dtEntity::EntityId buildingid, const osg::Vec3d &pos, const std::string &iconpath) { DEBUG_LOG(m_logger) << "addDeviceToBuilding"; for (Building *building : mBuildings) { DEBUG_LOG(m_logger) << "mWorldBuildingId=" << building->mWorldBuildingId << " buildingid=" << buildingid; if (building->mWorldBuildingId != buildingid) continue; DEBUG_LOG(m_logger) << "addDeviceToBuilding 1"; Device *device = new Device(); device->mWorldPlayerId = iconmainid; device->mPath = iconpath; device->mPosition = pos; building->mDevices.push_back(device); DEBUG_LOG(m_logger) << "building->mDisplays.size=" << building->mDisplays.size(); for (BuildingDisplay &dspl : building->mDisplays) { DEBUG_LOG(m_logger) << "addDeviceToBuilding 2"; DeviceDisplay dd; dd.mDevice = device; dd.mWorldPlayerId = iconmainid; dspl.mDeviceDisplays.push_back(std::move(dd)); updateDevices(*building, dspl); dtEntity::GetWindowInterface()->RequestRedraw(dspl.mContextId); } break; } } //////////////////////////////////////////////////////////////////////////////// void BuildingManager::removeDeviceFromBuilding(dtEntity::EntityId iconmainid) { for (Building *building : mBuildings) { std::vector::iterator j = building->mDevices.begin(); while (j != building->mDevices.end()) { Device *device = *j; if (device->mWorldPlayerId == iconmainid) { for (BuildingDisplay &bd : building->mDisplays) { std::vector::iterator l = bd.mDeviceDisplays.begin(); while (l != bd.mDeviceDisplays.end()) { DeviceDisplay &pd = *l; if (pd.mDevice->mWorldPlayerId == iconmainid) { dtEntity::MapSystem *ms; mEntityManager->GetES(ms); if (ms) ms->RemoveFromScene(pd.mLocalPlayerId); mEntityManager->KillEntity(pd.mLocalPlayerId); dtEntity::GetWindowInterface()->RequestRedraw(bd.mContextId); l = bd.mDeviceDisplays.erase(l); } else ++l; } } j = building->mDevices.erase(j); delete device; } else ++j; } } } //////////////////////////////////////////////////////////////////////////////// // helper function for handling XML template bool fetchAttr(T &t, const rapidxml::xml_node<> *node, const char *name) { using namespace rapidxml; for (xml_attribute<> *attr = node->first_attribute(); attr; attr = attr->next_attribute()) { if (strcmp(attr->name(), name) == 0) { std::istringstream iss(attr->value()); return !(iss >> std::dec >> t).fail(); } } return false; } //////////////////////////////////////////////////////////////////////////////// // helper function for handling XML static bool HasTagName(const rapidxml::xml_node<> *node, const char *name) { return (node->type() == rapidxml::node_element && strcmp(name, node->name()) == 0); } //////////////////////////////////////////////////////////////////////////////// bool BuildingManager::getRoomBounds(const std::string &bldgname, const std::string &roomname, VCtrl::OBB &obb) const { const std::string key = bldgname + " " + roomname; auto i = mRooms.find(key); if (i == mRooms.end() || !i->second.mBB.valid()) { for (const auto &room : mRooms) { const osg::BoundingBox &bb = room.second.mBB; QByteArray ba("id: "); ba += room.first.c_str(); ba += ", min: " + QByteArray::number(bb.xMin()) + ", " + QByteArray::number(bb.yMin()) + ", " + QByteArray::number(bb.zMin()); ba += ", max: " + QByteArray::number(bb.xMax()) + ", " + QByteArray::number(bb.yMax()) + ", " + QByteArray::number(bb.zMax()); #ifdef LTS_DEBUG LOG_ERROR(ba.data()); #endif } LOG_ERROR("Could not find room " << bldgname << "-" << roomname << ", #mRooms: " << mRooms.size()); return false; } obb = i->second; if (!obb.mBB.valid()) { LOG_ERROR("Room" << bldgname << "-" << roomname << " has invalid bounds"); return false; } return true; } //////////////////////////////////////////////////////////////////////////////// static void readBBoxMin(const rapidxml::xml_node<> *roomnode, VCtrl::OBB &room) { if (HasTagName(roomnode, "BBox_Min")) { fetchAttr(room.mBB._min[0], roomnode, "X"); fetchAttr(room.mBB._min[1], roomnode, "Y"); fetchAttr(room.mBB._min[2], roomnode, "Z"); } } static void readBBoxMax(const rapidxml::xml_node<> *roomnode, VCtrl::OBB &room) { if (HasTagName(roomnode, "BBox_Max")) { fetchAttr(room.mBB._max[0], roomnode, "X"); fetchAttr(room.mBB._max[1], roomnode, "Y"); fetchAttr(room.mBB._max[2], roomnode, "Z"); } } static void readPolygon(const rapidxml::xml_node<> *roomnode, VCtrl::OBB &room) { if (HasTagName(roomnode, "Group")) { for (auto grp0node(roomnode->first_node()); grp0node != nullptr; grp0node = grp0node->next_sibling()) { if (HasTagName(grp0node, "Polygon")) { for (auto grp1node(grp0node->first_node()); grp1node != nullptr; grp1node = grp1node->next_sibling()) { if (HasTagName(grp1node, "Vertex")) { for (auto grp2node(grp1node->first_node()); grp2node != nullptr; grp2node = grp2node->next_sibling()) { if (HasTagName(grp2node, "Coord")) { double x,y; fetchAttr(x, grp2node, "X"); fetchAttr(y, grp2node, "Y"); room.mPoly.append(QPointF(x,y)); } } } } } } } } bool BuildingManager::AddRoomOBBs(const BuildingData &b) { LOG_DEBUG("AddRoomOBBs: " + b.mFileName); if (b.mBuildingXMLPath.empty()) { LOG_WARNING("Buildings xml is not set: " + b.mFileName); return false; } const std::string xmlpath = b.mBuildingXMLPath; using namespace rapidxml; const std::string absPath = dtEntity::GetSystemInterface()->FindDataFile(xmlpath); if (absPath.empty()) { LOG_ERROR("Map not found: " + absPath); return false; } file<> file(absPath.c_str()); try { xml_document<> doc; // character type defaults to char doc.parse<0>(file.data()); xml_node<> *rootnode = doc.first_node("STREIF"); if (rootnode == nullptr) { LOG_ERROR("Could not parse building xml!"); return false; } int floorNumber = -999; std::map roomToFloor; for (xml_node<> *hdrNode(rootnode->first_node()); hdrNode != nullptr; hdrNode = hdrNode->next_sibling()) { if (HasTagName(hdrNode, "Header")) { for (xml_node<> *grp1Node(hdrNode->first_node()); grp1Node != nullptr; grp1Node = grp1Node->next_sibling()) { if (HasTagName(grp1Node, "Group")) { for (xml_node<> *grp2Node(grp1Node->first_node()); grp2Node != nullptr; grp2Node = grp2Node->next_sibling()) { if (HasTagName(grp2Node, "Group")) { for (xml_node<> *swtchNode(grp2Node->first_node()); swtchNode != nullptr; swtchNode = swtchNode->next_sibling()) { std::string temp; if (HasTagName(swtchNode, "Name")) { temp = swtchNode->value(); // floor_ if (temp.substr(0,6).compare("floor_") == 0) { bool bOk = false; floorNumber = QString::fromStdString(temp.substr(6)).toInt(&bOk); if (!bOk) floorNumber = -999; // ERROR_LOG(m_logger) << LOGFUNCTION << " floorName=" << floorNumber; } } if (HasTagName(swtchNode, "Switch")) { VCtrl::OBB room; room.mMatrix.setTrans(b.mX, b.mY, b.mZ); osg::Quat q; q.makeRotate(-b.mOrientation / 180.0 * osg::PI, osg::Vec3(0, 0, 1)); room.mMatrix.setRotate(q); room.mMatrixInverse.invert(room.mMatrix); std::string name; for (xml_node<> *roomnode(swtchNode->first_node()); roomnode != nullptr; roomnode = roomnode->next_sibling()) { int roomNumber = -999; if (HasTagName(roomnode, "Name")) { temp = roomnode->value(); // floor_ if (temp.substr(0,8).compare("SW_room_") == 0) { bool bOk = false; roomNumber = QString::fromStdString(temp.substr(8)).toInt(&bOk); if (!bOk) roomNumber = -999; // ERROR_LOG(m_logger) << LOGFUNCTION << " roomName= " << roomNumber; // ERROR_LOG(m_logger) << LOGFUNCTION << " Bldg=" << b.mFeatureId << "Floor=" << floorNumber << " Room=" << roomNumber; if (roomNumber>=0) roomToFloor[roomNumber+2000]=floorNumber; } } if (HasTagName(roomnode, "SubId")) { for (xml_attribute<> *attr = roomnode->first_attribute(); attr; attr = attr->next_attribute()) { if (strcmp(attr->name(), "ID") == 0) name = attr->value(); } } if (name.empty()) continue; readBBoxMin(roomnode, room); readBBoxMax(roomnode, room); } // LOG_ERROR("BBox111 room" << name << " in xml " << xmlpath << " - " // << " minX " << room.mBB._min.x() << " minY " << room.mBB._min.y() // << " maxX " << room.mBB._max.x() << " minY " << room.mBB._max.y() // ); // no bbox found? check for new xml structure if (!room.mBB.valid()) { for (auto roomnode(swtchNode->first_node()); roomnode != nullptr; roomnode = roomnode->next_sibling()) { if (HasTagName(roomnode, "Group")) { for (auto grp3node(roomnode->first_node()); grp3node != nullptr; grp3node = grp3node->next_sibling()) { readBBoxMin(grp3node, room); readBBoxMax(grp3node, room); readPolygon(grp3node, room); } if (room.mBB.valid() && !room.mPoly.empty()) { // LOG_ERROR("BBox222 room" << name << " in xml " << xmlpath << " - " // << " minX " << room.mBB._min.x() << " minY " << room.mBB._min.y() // << " maxX " << room.mBB._max.x() << " minY " << room.mBB._max.y() // ); break; // short circuit } } } } if (!room.mBB.valid()) LOG_ERROR("Could not parse room bounding boxes for room" << name << " in xml " << xmlpath); room.mBB._min[2] -= ROOM_OBB_Z_MIN_SUBTRACT; // 0.1f room.mBB._max[2] -= ROOM_OBB_Z_MAX_SUBTRACT; // 0.5f // GUEZE-605 / GUEZE-668 // U_Bahn-Tunnel: Roomnames and player were not shown // Due to ROOM_OBB_Z_.._SUBTRACT the bb became invalid. Skip in that cases if (!room.mBB.valid()) { // Subtract from room OBB height so that only room navmesh cells are in obb LOG_ERROR("Subtract from room OBB height fails, revert... room=" << name); room.mBB._min[2] += ROOM_OBB_Z_MIN_SUBTRACT; // 0.1f room.mBB._max[2] += ROOM_OBB_Z_MAX_SUBTRACT; // 0.5f } if (!room.mBB.valid()) LOG_ERROR("Could not parse room bounding boxes for room " << name << " in xml " << xmlpath); const std::string key = b.mFeatureId + " " + name; room.mBuilding = dtEntity::SID(b.mFeatureId); mRooms[key] = room; dtEntity::GetDebugDrawInterface()->AddOBB(room.mMatrix, room.mBB._min, room.mBB._max, osg::Vec4(1, 0, 1, 1), 1, FLT_MAX, false); osg::Vec3 pos = room.mBB.center() * room.mMatrix; dtEntity::GetDebugDrawInterface()->AddString(pos, key, osg::Vec4(0, 1, 0, 1), FLT_MAX, false); } } } } } } } } mRoomToFloorPerBldg[b.mFeatureId] = roomToFloor; } catch (const std::exception &ex) { LOG_ERROR("XML Parsing error: " + std::string(ex.what())); return false; } return true; } bool BuildingManager::LoadBuildings(const std::string &path, std::vector &buildings) { using namespace rapidxml; const std::string absPath = dtEntity::GetSystemInterface()->FindDataFile(path); if (absPath.empty()) { LOG_ERROR("Map not found: " + absPath); return false; } file<> file(absPath.c_str()); try { xml_document<> doc; // character type defaults to char doc.parse<0>(file.data()); xml_node<> *rootnode = doc.first_node("MetaData"); if (rootnode == nullptr) { LOG_ERROR("Could not parse map xml!"); return false; } for (xml_node<> *tagNode(rootnode->first_node()); tagNode != nullptr; tagNode = tagNode->next_sibling()) { if (HasTagName(tagNode, "Tag")) { int topmesh = -1; for (xml_node<> *groupNode(tagNode->first_node()); groupNode != nullptr; groupNode = groupNode->next_sibling()) { if (HasTagName(groupNode, "Group")) { ++topmesh; for (xml_node<> *modelNode(groupNode->first_node()); modelNode != nullptr; modelNode = modelNode->next_sibling()) { if (HasTagName(modelNode, "Model")) { BuildingData b; for (xml_node<> *dataNode(modelNode->first_node()); dataNode != nullptr; dataNode = dataNode->next_sibling()) { if (HasTagName(dataNode, "Orientation")) fetchAttr(b.mOrientation, dataNode, "Angle"); else if (HasTagName(dataNode, "Position")) { fetchAttr(b.mX, dataNode, "X"); fetchAttr(b.mY, dataNode, "Y"); fetchAttr(b.mZ, dataNode, "Z"); } else if (HasTagName(dataNode, "Description")) fetchAttr(b.mDescription, dataNode, "Name"); else if (HasTagName(dataNode, "Feature")) fetchAttr(b.mFeatureId, dataNode, "Id"); else if (HasTagName(dataNode, "File")) fetchAttr(b.mFileName, dataNode, "Name"); else if (HasTagName(dataNode, "STREIF")) fetchAttr(b.mBuildingXMLPath, dataNode, "Name"); } b.mBuildingGroup = topmesh; LOG_INFO("add building " + b.mFeatureId + " - " + b.mFileName); buildings.push_back(std::move(b)); } } } } } } } catch (const std::exception &ex) { LOG_ERROR("XML Parsing error: " + std::string(ex.what())); return false; } return true; } size_t BuildingManager::getNumBuildings() const { return mBuildings.size(); } void BuildingManager::getBuildingIds(std::vector &toFill) const { toFill.reserve(mBuildings.size()); for (const Building *building : mBuildings) toFill.push_back(building->mWorldBuildingId); } dtEntity::EntityId BuildingManager::getNextBuilding(const osg::Vec3d &pos, float &distance, std::string &featureId) const { const osg::Vec2d pos2d(pos[0], pos[1]); float minDist = 1000000.0f; dtEntity::EntityId eid = -1; for (const Building *building : mBuildings) { const osg::Vec3d currentPos = osg::Vec3d(0, 0, 0) * building->mLocalToWorld; const osg::Vec2d currentPos2d(currentPos[0], currentPos[1]); float dist = (currentPos2d - pos2d).length(); if (dist < minDist) { minDist = dist; eid = building->mWorldBuildingId; featureId = building->mFeatureId; } } distance = minDist; return eid; } Building BuildingManager::getBuildingFromFeatureId(const std::string &bldname) const { Building bld; bld.mWorldBuildingId = 0; for (const Building *building : mBuildings) { if (building->mFeatureId == bldname) { bld.mBuildingDisplayName = building->mBuildingDisplayName; bld.mFeatureId = building->mFeatureId; bld.mWorldBuildingId = building->mWorldBuildingId; } } return bld; } void BuildingManager::updateNames(const Building& bd, BuildingDisplay& dspl) { for (NameDisplay& ddspl : dspl.mNameDisplays) { TextComponent *name; if (ddspl.mLocalPlayerId == 0) { dtEntity::Entity* e; if (!mEntityManager->CreateEntity(e)) continue; ddspl.mLocalPlayerId = e->GetId(); dtEntity::MapComponent* mc; if (!e->CreateComponent(mc)) continue; std::string en = "ctx_" + std::to_string(dspl.mContextId) + "_nam_" + std::to_string(ddspl.mName->mWorldPlayerId); mc->SetEntityName(en); mc->SetUniqueId(en); mc->SetMapName("nameproxies"); if (!e->CreateComponent(name)) continue; name->SetText(ddspl.mName->mtext); name->SetDisText(ddspl.mName->mdistext); dtEntityOSG::LayerComponent* lc; if (!e->CreateComponent(lc)) continue; std::string viewname = "Context_" + std::to_string(dspl.mContextId); lc->SetAttachedComponent(TextComponent::TYPE); lc->SetLayer(dtEntity::SID(viewname)); dtEntity::MapSystem* ms; if (mEntityManager->GetES(ms)) ms->AddToScene(e->GetId()); } else { if (!mEntityManager->GetComponent(ddspl.mLocalPlayerId, name)) { assert(false); continue; } } int roomNumber = -1; { bool bOk = false; roomNumber = QString::fromStdString(ddspl.mName->mtext).toInt(&bOk);; if (!bOk) roomNumber = -1; } name->SetTranslation(remapPosition(bd, dspl, ddspl.mName->mPosition, roomNumber)); name->SetVisible(dspl.mNameMode); name->SetDisText(ddspl.mName->mdistext); name->Finished(); } } dtEntity::EntityId BuildingManager::getWorldNameIdFromLocalName(dtEntity::EntityId local, std::string &buildingName, std::string &room) const { for (const Building *building : mBuildings) { for (const BuildingDisplay &bd : building->mDisplays) { for (const NameDisplay &nd : bd.mNameDisplays) { if (nd.mLocalPlayerId == local) { buildingName = nd.mName->mbuilding; room = nd.mName->mtext; return nd.mWorldPlayerId; } } } } return 0; } void BuildingManager::changeLineMode(unsigned int contextId, bool line) { mShotOverview = line; bool bRequestRedraw = false; for (Building *building : mBuildings) { for (BuildingDisplay &bd : building->mDisplays) { if (bd.mContextId == contextId) { if (bd.mLineMode != line) { bd.mLineMode = line; bRequestRedraw = true; } } } } if (bRequestRedraw) dtEntity::GetWindowInterface()->RequestRedraw(contextId); } void BuildingManager::changeNameMode(unsigned int contextId, bool name) { bool bRequestRedraw = false; for (Building *building : mBuildings) { for (BuildingDisplay &bd : building->mDisplays) { if (bd.mContextId == contextId) { if (bd.mNameMode != name) { bd.mNameMode = name; setShowHideDisplayNames(building->mWorldBuildingId, contextId, name); bRequestRedraw = true; } } } } if (bRequestRedraw) dtEntity::GetWindowInterface()->RequestRedraw(contextId); } void BuildingManager::setBuildingDisplayName(const std::string& featureId, dtEntity::EntityId eid, const std::string& strDisplayName) { m_mapBuildingDisplayNames[featureId] = strDisplayName; // Building already opened? if(eid > 0) { setBuildingDisplayName(eid, strDisplayName); } } void BuildingManager::setBuildingDisplayName(dtEntity::EntityId eid, const std::string& strDisplayName) { // TODO: Set Building Name for (Building *building : mBuildings) { if (building->mWorldBuildingId != eid) continue; building->mBuildingDisplayName = strDisplayName; break; } } void BuildingManager::setRoomDisplayName(const std::string& featureId, dtEntity::EntityId eid, const std::string& roomname, const std::string& strDisplayName) { m_mapBuildingRoomNames[featureId][roomname] = strDisplayName; // Building already opened? if (eid > 0) { for (Building *building : mBuildings) { if (building->mWorldBuildingId != eid) continue; setRoomDisplayName(building, roomname, strDisplayName); return; } } } void BuildingManager::setRoomDisplayName(Building *building, const std::string& roomname, const std::string& strDisplayName) { for (BuildingDisplay &bd : building->mDisplays) { for (NameDisplay &nd : bd.mNameDisplays) { if (nd.mName->mtext == roomname) { nd.mName->mdistext = strDisplayName; TextComponent* tc; if (mEntityManager->GetComponent(nd.mLocalPlayerId, tc)) { tc->SetDisText(strDisplayName); tc->Finished(); } // roomname must be unique, so we can stop here dtEntity::GetWindowInterface()->RequestRedraw(bd.mContextId); return; } } } } void BuildingManager::setShowHideDisplayNames(dtEntity::EntityId eid, unsigned int contextId, bool show) { for (Building *building : mBuildings) { if (building->mWorldBuildingId != eid) continue; for (BuildingDisplay &bd : building->mDisplays) { bool bRequestRedraw = false; if ( (bd.mContextId == contextId) || (contextId == 9999) ) { for (NameDisplay &nd : bd.mNameDisplays) { nd.visible = show; TextComponent* tc; if (mEntityManager->GetComponent(nd.mLocalPlayerId, tc)) { tc->SetVisible(show); bRequestRedraw = true; } } } if (bRequestRedraw) dtEntity::GetWindowInterface()->RequestRedraw(bd.mContextId); } break; } } void BuildingManager::setShowDisplayNamesSelection(dtEntity::EntityId eid, bool show) { for (Building *building : mBuildings) { for (const BuildingDisplay &bd : building->mDisplays) { bool bRequestRedraw = false; for (const NameDisplay &nd : bd.mNameDisplays) { if (nd.mWorldPlayerId == eid) { TextComponent* tc; if (mEntityManager->GetComponent(nd.mLocalPlayerId, tc)) { tc->SetShowSelection(show); bRequestRedraw = true; } } } if (bRequestRedraw) dtEntity::GetWindowInterface()->RequestRedraw(bd.mContextId); } } } //////////////////////////////////////////////////////////////////////////////// void BuildingManager::createRoomNames(dtEntity::EntityId buildingid, Building& building) { RoomDestructionComponent* btc; if (mEntityManager->GetComponent(buildingid, btc)) { const RoomDestructionComponent::RoomDestructions& rc = btc->GetRoomDestructions(); for (const auto &i : rc) { const std::string &roomName = i.first; VCtrl::OBB roomObb; getRoomBounds(building.mNavMeshGrpName, roomName, roomObb); osg::Vec3 posX = roomObb.mBB.center(); // wg. [GUEZE-726] wird der neue Anpassung zunächst deaktiviert, bis Klarheit in // die flt.XML-Datei gekommen ist (undest_room vs. FOOTPRINT vs. polygon1 vs. polygon2 ... // Es handelt sich um einen Flüchtigkeitsfehler bei bldg_1111. (fix: SPD-/Renoir r 4747) // Daher wird "[GUEZE-609] Positionierung der Raumnamen" wieder aktiviert: if (!roomObb.mPoly.isEmpty() && !roomObb.mPoly.containsPoint(QPointF(posX.x(), posX.y()), Qt::OddEvenFill)) { // [GUEZE-609] Positionierung der Raumnamen const QPointF qpfCenter(posX.x(), posX.y()); QPointF nearestVertex; qreal distance = 999999999; for (const QPointF &vertex : qAsConst(roomObb.mPoly)) { // distance: qreal dis = (vertex-qpfCenter).manhattanLength(); if (disCreateEntity(e)) continue; // for bookkeeping dtEntity::MapComponent* mc; if (!e->CreateComponent(mc)) { mEntityManager->KillEntity(e->GetId()); continue; } const std::string en = "bd_" + building.mNavMeshGrpName + "_rm_" + roomName; mc->SetEntityName(en); mc->SetUniqueId(en); mc->SetMapName("roomlabel"); dtEntity::EntityId nameMainId = e->GetId(); Name* name = new Name(); name->mWorldPlayerId = nameMainId; name->mtext = roomName; name->mdistext = ""; name->mPosition = pos; //name->SetColor(osg::Vec4(1,1,1,1)); name->mbuilding = building.mNavMeshGrpName; building.mNames.push_back(name); } } } }